Rails 4.2 ActionMailer: no implicit conversion of Message into Hash - ruby-on-rails-4

I am delivering the mail message from
class DelayMessageJob < ActiveJob::Base
queue_as :default
def perform(*args)
message = self.arguments.first
MessageMailer.default(message).deliver_now!
message[:sent] = true
message.save
end
end
it's running fine until there... scheduled then enqueued
my MessageMailer class is quite simple
class MessageMailer < ApplicationMailer
def default(message)
#content = message.text
mail(
to: "#{message[:recipient_email]}",
subject: "Hi and welcome"
)
end
end
and it's parent ApplicationMailer
class ApplicationMailer < ActionMailer::Base
default from: "from#example.com"
layout 'mailer'
end
debugging the MessageMailer class, I can get the message arg ..
#<Message id: 19, recipient_email: "yves#icloud.com", text: "retesting",
delay_until_time: "2015-07-29 15:14:00", timezone_offset: 2,
sent: false, created_at: "2015-07-29 15:13:48",
updated_at: "2015-07-29 15:13:48"
mail is raising: TypeError: no implicit conversion of Message into Hash
UPDATE 1
I changed message[:sent] to message.sent, same error
8:06:17 sidekiq.1 | 2015-07-30T06:06:17.365Z 4871 TID-oxt3zobs8
WARN: {
"class"=>"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
"wrapped"=>"DelayMessageJob",
"queue"=>"default", "args"=> [{
"job_class"=>"DelayMessageJob",
"job_id"=>"9f1b52c0-bb4a-479c-bb5c-5ca11ae257b0",
"queue_name"=>"default",
"arguments"=>[{"_aj_globalid"=>"gid://delay-message-on-rails/Message/23"}]
}],
"retry"=>true,
"jid"=>"8cbffb19c9d88c48e9b9aa00",
"created_at"=>1438236297.952947,
"enqueued_at"=>1438236377.2572238,
"error_message"=>"no implicit conversion of Message into Hash",
"error_class"=>"TypeError",
"failed_at"=>1438236377.3641548, "retry_count"=>0
}
08:06:17 sidekiq.1 | 2015-07-30T06:06:17.365Z 4871 TID-oxt3zobs8
WARN: TypeError: no implicit conversion of Message into Hash
the error seems to be raised upon message being enqueued ...

As I want to send emails in background, using sidekiq, I should not forget to start sidekiq 'mailers' queue in my Procfile
sidekiq: sidekiq -q devise,1 -q default -q mailers
I can send delayed notifications using directly ActionMailer or using ActiveJob
1- using ActionMailer
controllers/messages_controler.rb
MessageMailer.keep_inform(#message).deliver_later(wait_until: #message[:delay_until_time])
mailers/message_mailer.rb
class MessageMailer < ApplicationMailer
def keep_inform(notification)
#content = notification.text
mail(
to: "#{notification.recipient_email}",
subject: "Hi and welcome"
)
end
end
2- using ActiveJobs
controllers/messages_controler.rb
DelayMessageJob.set(wait_until: #message[:delay_until_time]).perform_later(#message)
jobs/delay_message_jobs.rb
class DelayMessageJob < ActiveJob::Base
queue_as :default
def perform(notification)
MessageMailer.keep_inform(notification).deliver_now
notification.sent = true
notification.save
end
end
In both case :
I defined my mailers/messages_mailer.rb
class MessageMailer < ApplicationMailer
def keep_inform(notification)
#content = notification.text
mail(
to: "#{notification.recipient_email}",
subject: "Hi and welcome"
)
end
end
NOTE:
I don't know if it's important, but I changed the term 'message' to 'notification', I guess 'message' is used internally ...

Related

find_or_create_by fails when used with custom ActiveModel::Validator

I have 2 models named Drug and Frequency.
#app/models/frequency.rb
class Frequency
validates :code, presence: true
end
#app/models/drug.rb
class DrugValidator < ActiveModel::Validator
def validate(record)
drug_attributes = {name: record.name}
if Drug.where(name: record.name).any?
record.errors[:base] << "The drug #{record.name} already exists in system."
end
end
end
class Drug
validates_with DrugValidator, if: (lambda {|drug| drug.name.present?})
end
> pf = Frequency.find_or_create_by(code: 'Q24')
Frequency Load (0.4ms) SELECT "frequencies".* FROM "frequencies" WHERE "frequencies"."code" = $1 LIMIT 1 [["code", "Q24"]]
=> #<Frequency id: 7, code: "Q24", created_at: "2015-11-02 03:41:34", updated_at: "2015-11-02 03:41:34">
> pf.persisted?
=> true
> pf.valid?
=> true
> pf.new_record?
=> false
> pd = Drug.find_or_create_by(name: 'SomeDrug', unit: 'tablet')
=> #<Drug id: 10, name: "SomeDrug", created_at: "2015-11-05 07:42:46", updated_at: "2015-11-05 07:42:46", unit: "tablet">
> pd.persisted?
=> true
> pd.new_record?
=> false
> pd.valid?
=> false
> pd.errors.messages
=> {:base=>["The drug SomeDrug already exists in system."]}
the custom DrugValidator is executed even when fetching the record from the system. How do i avoid this for find_or_create_by?
Specify when this validation is active.
class Drug
validates_with DrugValidator, if: (lambda {|drug| drug.name.present?}), on: :create
end

Chartkick Timeline undefined method `each_pair' for#< Array:... > in Rails 4

I'm having an issue with a Chartkick Timeline chart in Rails 4.
When the page loads this error is thrown:
"undefined method `each_pair' for#< Array:... >"
in the Chartkick code as shown below;
def chartkick_deep_merge(hash_a, hash_b)
hash_a = hash_a.dup
hash_b.each_pair do |k, v|
tv = hash_a[k]
hash_a[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
end
hash_a
end
I have attempted various iterations of the code using to_json, as_json, #events.each do ..., #events.map... attempted
timeline("chart-1", "phase_masters_phase_time_path")
that points to
render json: Events.select(:id, :event_types, :starttime, :endtime).where(project_masters_id: params[:proj_id], phase_masters_id: params[:id])
The variables appear to be being set correctly as can be seen in hash_b
hash_a
{:width=>"400px", :height=>"400px", :colors=>["#B9D3EE", "#9FB6CD", "#A2CD5A", "#6E8B3D", "#FFB90F", "#E3A869"]}
hash_b
[[#<Events id: 1, event_types: "Estimating", starttime: "2015-10-05 11:15:00", endtime: "2015-10-13 21:15:00">], [#<Events id: 2, event_types: "Roll Forming", starttime: "2015-09-09 11:00:01", endtime: "2015-09-16 18:29:59">], [#<Events id: 7, event_types: "Magest On Site", starttime: "2015-09-25 11:00:01", endtime: "2015-09-25 18:29:59">]]
view:
<div id = "chart-1"> </div>
<%=
timeline("chart-1", #events.map { |evnt| [evnt] } )
%>
controller:
#events = Events.select(:id, :event_types, :starttime, :endtime).where(project_masters_id: params[:proj_id], phase_masters_id: params[:id])
respond_to do |format|
format.html
format.json {render json: #events}
end
Any help with getting this running would be appreciated.
It seems like your passing an array of Events to the method timeline, that method expects an array of triplets (of type array).
I'm not sure I'm reading your code right but it seems like the call to timeline could instead be something like
timeline("chart-1", #events.map { |evnt| [evnt.types, evnt.starttime, evnt.endtime] })

How to create an Array inside ActiveModel::EachValidator

I am new to Ruby-on-Rails 4. I have created a custom validator but cannot assign value to an array. It shows error
undefined method <<' for nil:NilClass.
It highlights #msg << 1
For instance, my model is like
class User < ActiveRecord::Base
has_secure_password
validates :email,:email_format => true, :on => :create
validates :password, password_format:{with: "upercase"}
end
My custom validator
class PasswordFormatValidator < ActiveModel::EachValidator
#def initilize(options)-Fixed
def initialize(options)
#msg=[]
#password1 = options[:attributes=>[:password]]
#val=options.inspect
super
end
def validate_each(record, attribute, value)
record.errors[attribute] << #val
unless (value.nil? || value.empty?)
#msg << 1
#record.errors[attribute] << "testing"
end
end
end
#val output
{:attributes=>[:password], :complexity=>3, :length=>6, :class=>User(id: integer, email: string, password_digest: string, created_at: datetime, updated_at: datetime)}
You have a typo in your constructor name, it should be initialize and not initilize. This is why your #msg variable is undefined - your constructor has never been called!
Have a nice day.

Rails 4: composed_of mapping to JSON store attribute

I have the following models set up
# task.rb
class Task << AR
# everything all task objects have in common
end
# login_request.rb
class Tasks::LoginRequest < Task
store :data, accessors: [:email, :first_name, :last_name, :expires_at]
composed_of :valid_until, class_name: 'DateTime', mapping: %w(expires_at to_s), constructor: Proc.new { |date| (date && date.to_datetime) || DateTime.now }, converter: Proc.new { |value| value.to_s.to_datetime }
end
I'm using the datetime_select helper in my form:
# _form.html.haml
= f.datetime_select :valid_until
This works quite well, but when I call update in my controller with the submitted form data I get the following error message:
1 error(s) on assignment of multiparameter attributes [error on assignment [2014, 4, 2, 9, 48] to valid_until (can't write unknown attribute 'expires_at')]
So, I'm guessing the updated method tries to manipulate the attributes hash directly, but obviously it can't find the attribute expires_at, since it's a simple accessor method of the JSON column data.
I know I could simply add this field to the DB and it would probably work - although there's no need then to have a composed_of statement. But I'd rather not go this route, because not every task has a expires_at column.
How can I overcome this error? Or did I miss something?
Currently compose_of is not supporting this scenario since it writes directly to attributes that are assumed to be in the database. I wrote a tweaked compose_of version that does (based of Rails 4.0.2 version)
Putting this in initialize folder:
#/initialize/support_store_in_composed_of.rb
module ActiveRecord
module Aggregations
extend ActiveSupport::Concern
def clear_aggregation_cache #:nodoc:
#aggregation_cache.clear if persisted?
end
module ClassMethods
def composed_of_with_store_support(part_id, options = {})
options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter, :store)
name = part_id.id2name
class_name = options[:class_name] || name.camelize
mapping = options[:mapping] || [ name, name ]
mapping = [ mapping ] unless mapping.first.is_a?(Array)
allow_nil = options[:allow_nil] || false
constructor = options[:constructor] || :new
converter = options[:converter]
reader_method(name, class_name, mapping, allow_nil, constructor, options[:store])
writer_method(name, class_name, mapping, allow_nil, converter, options[:store])
create_reflection(:composed_of, part_id, nil, options, self)
end
private
def reader_method(name, class_name, mapping, allow_nil, constructor, store=nil)
define_method(name) do
if #aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
if store.present?
attrs = mapping.collect {|pair| send(pair.first)}
else
attrs = mapping.collect {|pair| read_attribute(pair.first)}
end
object = constructor.respond_to?(:call) ?
constructor.call(*attrs) :
class_name.constantize.send(constructor, *attrs)
#aggregation_cache[name] = object
end
#aggregation_cache[name]
end
end
def writer_method(name, class_name, mapping, allow_nil, converter, store=nil)
define_method("#{name}=") do |part|
klass = class_name.constantize
unless part.is_a?(klass) || converter.nil? || part.nil?
part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
end
if part.nil? && allow_nil
mapping.each { |pair| self[pair.first] = nil }
#aggregation_cache[name] = nil
else
if store.present?
mapping.each { |pair| send("#{pair.first}=", part.send(pair.last)) }
else
mapping.each { |pair| self[pair.first] = part.send(pair.last) }
end
#aggregation_cache[name] = part.freeze
end
end
end
end
end
end
And using it like this would solve your problem.
class Task < ActiveRecord::Base
store :data, accessors: [:email, :first_name, :last_name, :expires_at]
composed_of_with_store_support :valid_until, class_name: 'DateTime', mapping: %w(expires_at to_s),
constructor: Proc.new { |date| (date && date.to_datetime) || DateTime.now },
converter: Proc.new { |value| value.to_s.to_datetime },
store: true
end

Promotion/bubble-wrap TableScreen data

I'm stuck on this:
I need to populate data into my app.
I'm using Promotion for the very first time....
Without ProMotion I use to fetch the data in the init method
Now my code looks like below:
class Parties < ProMotion::TableScreen
attr_accessor :_cells
#news = []
include MyUiModules
title 'Yazarlar'
refreshable callback: :on_refresh,
pull_message: "Pull to refresh",
refreshing: "Refreshing data…",
updated_format: "Last updated at %s",
updated_time_format: "%l:%M %p"
def on_refresh
#MyItems.pull_from_server do |items|
##my_items = items
end_refreshing
#update_table_data
# end
end
def table_data
_cells = []
[{
title: nil,
cells: create_cells(_cells)
}]
end
def will_appear
Barbutton.create_bar(self)
set_attributes self.view, {
backgroundColor: hex_color("DBDBDB")
}
end
def go_to_next
App.delegate.slide_menu.show_menu
end
def create_cells(_cells)
BW::HTTP.get(URL) do |response|
json = BW::JSON.parse response.body.to_str
for line in json
_cells << { title: line["val_for_title"]}
end
end
_cells
end
end
Unfotunately this does return an empty array, and I can't figure out how to solve it.
Thx for your help
You can't do that because BW::HTTP.get is asynchronous !
Instead try something like this:
def on_init
#data = []
end
def table_data
[
{
title: nil,
cells: #data
}
]
end
def on_refresh
BW::HTTP.get(URL) do |response|
#data = []
json = BW::JSON.parse(response.body.to_str)
json.each do |hash|
#data << { title: hash["val_for_title"]}
end
update_table_data
end_refreshing
end
end
Hope it helps :-)