ActionController::Parameters permitted assigned with field is nil in Model : Rails 5 - ruby-on-rails

I am migrating code from Rails 3 to Rails 5.1.6 and Ruby 2.5. I am facing strange issue. I have written after_initialize callback in model and assigning default value to field. The field is serialize and when there is nil in that field it will assign default value inside the callback.
Here is my code for clear picture
class MyModel < ActiveRecord::Base
serialize :settings
after_initialize :set_default_setting
def set_default_setting
self.settings = self.settings || {my_fields: ["a", "b", "c"]}
end
end
Here is my Rails console log :
[1] 2.5.0 :135 > md = MyModel.find(1)
=> #<MyModel:0x00007fe433608348
id: 1,
settings: <ActionController::Parameters permitted: >,
created_at: Tue, 16 Jan 2018 08:45:26 GMT +00:00,
updated_at: Fri, 04 May 2018 10:50:20 BST +01:00>
[2] 2.5.0 :136 > md.settings
=> <ActionController::Parameters permitted: >
[3] 2.5.0 :137 > md.settings.nil?
=> false
My question is why <ActionController::Parameters permitted: > is being added in to nil field ? when I execute nil? method it should return me nil. Above code was working fine with Rails 3.2 and Ruby 2.0 from which I am upgrading to Rails 5 and Ruby 2.5
Any help will be appreciated.
Thank you in Advance.

Related

Upgrading Rails 3.2 app to Rails 4, Strong Params behaving strange

I'm in the middle of the process of upgrading a Rails 3.2 app to Rails 4.
I'm working on replacing the attr_accessible from the models with Strong Params in controller.
I noticed that the #permit method behaves a bit different on the 3.2 I'm upgrading than it should behave in a Rails 4 app. In order to validate my suspicion I created a new Rails 4 app from scratch and tested the same code while getting a different outcome:
Rails 3.2 app that is currently being upgraded
[9] pry(main)> x = { attr1: { attr2: "val1", attr3: "val2"}, attr4: "val3", attr5: "val4", attr6: { attr7: "val5"}}
=> {:attr1=>{:attr2=>"val1", :attr3=>"val2"}, :attr4=>"val3", :attr5=>"val4", :attr6=>{:attr7=>"val5"}}
[10] pry(main)> params = ActionController::Parameters.new x
=> {"attr1"=>{"attr2"=>"val1", "attr3"=>"val2"}, "attr4"=>"val3", "attr5"=>"val4", "attr6"=>{"attr7"=>"val5"}}
[11] pry(main)> params.require(:attr1)
=> {"attr2"=>"val1", "attr3"=>"val2"}
[12] pry(main)> params.permit(:attr1)
ActionController::UnpermittedParameters: found unpermitted parameters: attr1, attr4, attr5, attr6
[19] pry(main)> params.permit(:attr4)
ActionController::UnpermittedParameters: found unpermitted parameters: attr1, attr5, attr6
Rails 4 app
2.2.3 :009 > x = { attr1: { attr2: "val1", attr3: "val2"}, attr4: "val3", attr5: "val4", attr6: { attr7: "val5"}}
=> {:attr1=>{:attr2=>"val1", :attr3=>"val2"}, :attr4=>"val3", :attr5=>"val4", :attr6=>{:attr7=>"val5"}}
2.2.3 :012 > params = ActionController::Parameters.new x
=> {"attr1"=>{"attr2"=>"val1", "attr3"=>"val2"}, "attr4"=>"val3", "attr5"=>"val4", "attr6"=>{"attr7"=>"val5"}}
2.2.3 :013 > params.require(:attr1)
=> {"attr2"=>"val1", "attr3"=>"val2"}
2.2.3 :014 > params.permit(:attr1)
=> {}
2.2.3 :032 > params.permit(:attr4)
=> {"attr4"=>"val3"}
I use the protected_attributes gem during the upgrade and I also placed this code in config/initializers/strong_parameters.rb:
ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
# config.action_controller.always_permitted_parameters = %w( controller action format )
module ActionController
class Parameters
remove_const(:NEVER_UNPERMITTED_PARAMS) if (defined?(NEVER_UNPERMITTED_PARAMS))
NEVER_UNPERMITTED_PARAMS = %w( controller action format)
end
end
Do you have any idea what the problem might be?
You'll need to permit all nested parameters, see here: http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-permit.
So in your case, for the first attribute you should do
params.permit(attr1: [:attr2, :attr2])
and likewise for the other parts of your hash.
Apparently the reason I was getting this error was because I had this setting in my config/application.rb
config.action_controller.action_on_unpermitted_parameters = :raise
Deleting it gave me the ability to permit only the params I wanted.

Time difference in rspec

I'm writing a simple class to parse strings into relative dates.
module RelativeDate
class InvalidString < StandardError; end
class Parser
REGEX = /([0-9]+)_(day|week|month|year)_(ago|from_now)/
def self.to_time(value)
if captures = REGEX.match(value)
captures[1].to_i.send(captures[2]).send(captures[3])
else
raise InvalidString, "#{value} could not be parsed"
end
end
end
end
The code seems to work fine.
Now when I try my specs I get a time difference only in year and month
require 'spec_helper'
describe RelativeDate::Parser do
describe "#to_time" do
before do
Timecop.freeze
end
['day','week','month','year'].each do |type|
it "should parse #{type} correctly" do
RelativeDate::Parser.to_time("2_#{type}_ago").should == 2.send(type).ago
RelativeDate::Parser.to_time("2_#{type}_from_now").should == 2.send(type).from_now
end
end
after do
Timecop.return
end
end
end
Output
..FF
Failures:
1) RelativeDate::Parser#to_time should parse year correctly
Failure/Error: RelativeDate::Parser.to_time("2_#{type}_ago").should == 2.send(type).ago
expected: Wed, 29 Aug 2012 22:40:14 UTC +00:00
got: Wed, 29 Aug 2012 10:40:14 UTC +00:00 (using ==)
Diff:
## -1,2 +1,2 ##
-Wed, 29 Aug 2012 22:40:14 UTC +00:00
+Wed, 29 Aug 2012 10:40:14 UTC +00:00
# ./spec/lib/relative_date/parser_spec.rb:11:in `(root)'
2) RelativeDate::Parser#to_time should parse month correctly
Failure/Error: RelativeDate::Parser.to_time("2_#{type}_ago").should == 2.send(type).ago
expected: Sun, 29 Jun 2014 22:40:14 UTC +00:00
got: Mon, 30 Jun 2014 22:40:14 UTC +00:00 (using ==)
Diff:
## -1,2 +1,2 ##
-Sun, 29 Jun 2014 22:40:14 UTC +00:00
+Mon, 30 Jun 2014 22:40:14 UTC +00:00
# ./spec/lib/relative_date/parser_spec.rb:11:in `(root)'
Finished in 0.146 seconds
4 examples, 2 failures
Failed examples:
rspec ./spec/lib/relative_date/parser_spec.rb:10 # RelativeDate::Parser#to_time should parse year correctly
rspec ./spec/lib/relative_date/parser_spec.rb:10 # RelativeDate::Parser#to_time should parse month correctly
The first one seems like a time zone issue but the other one is even a day apart? I'm really clueless on this one.
This is a fascinating problem!
First, this has nothing to do with Timecop or RSpec. The problem can be reproduced in the Rails console, as follows:
2.0.0-p247 :001 > 2.months.ago
=> Mon, 30 Jun 2014 20:46:19 UTC +00:00
2.0.0-p247 :002 > 2.months.send(:ago)
DEPRECATION WARNING: Calling #ago or #until on a number (e.g. 5.ago) is deprecated and will be removed in the future, use 5.seconds.ago instead. (called from irb_binding at (irb):2)
=> Wed, 02 Jul 2014 20:46:27 UTC +00:00
[Note: This answer uses the example of months, but the same is true for the alias month as well as year and years.]
Rails adds the month method to the Integer class, returning an ActiveSupport::Duration object, which is a "proxy object" containing a method_missing method which redirects any calls to the method_missing method of the "value" it is serving as a proxy for.
When you call ago directly, it's handled by the ago method in the Duration class itself. When you try to invoke ago via send, however, send is not defined in Duration and is not defined in the BasicObject that all proxy objects inherit from, so the method_missing method of Rails' Duration is invoked which in turn calls send on the integer "value" of the proxy, resulting in the invocation of ago in Numeric. In your case, this results in a change of date equal to 2*30 days.
The only methods you have to work with are those defined by Duration itself and those defined by BasicObject. The latter are as follows:
2.0.0-p247 :023 > BasicObject.instance_methods
=> [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
In addition to the instance_eval you discovered, you can use __send__.
Here's the definition of method_missing from duration.rb
def method_missing(method, *args, &block) #:nodoc:
value.send(method, *args, &block)
end
value in this case refers to the number of seconds in the Duration object. If you redefine method_missing to special case ago, you can get your test to pass. Or you can alias send to __send__ as follows:
class ActiveSupport::Duration
alias send __send__
end
Here's another example of how this method_missing method from Duration works:
macbookair1:so1 palfvin$ rails c
Loading development environment (Rails 4.1.1)
irb: warn: can't alias context from irb_context.
2.0.0-p247 :001 > class ActiveSupport::Duration
2.0.0-p247 :002?> def foo
2.0.0-p247 :003?> 'foobar'
2.0.0-p247 :004?> end
2.0.0-p247 :005?> end
=> nil
2.0.0-p247 :006 > 2.months.foo
=> "foobar"
2.0.0-p247 :007 > 2.months.respond_to?(:foo)
=> false
2.0.0-p247 :008 >
You can call the newly defined foo directly, but because BasicObject doesn't implement respond_to?, you can't "test" that the method is defined there. For the same reason, method(:ago) on a Duration object returns #<Method: Fixnum(Numeric)#ago> because that's the ago method defined on value.

Rails save method if no changes

I seem to remember reading somewhere that rails won't commit to the database if no attributes have been changed (presumably as part of active record dirty) is this the case? I can't seem to find it and am none the wiser having had a quick look through the source code.
If this is false I need to use a before_save callback because I want to run some code based on whether or not a change has occured.
I assume after_save with dirty data won't work??
Rails won't do an UPDATE but it will run the after_save callbacks even if nothing has changed:
pry $ u = User.first
=> #<User id: 1, email: "dave#davebryand.com", username: "dbryand"...
pry $ u.updated_at
=> Tue, 24 Jun 2014 18:36:04 UTC +00:00
pry $ u.changed?
=> false
pry $ u.save
From: /Users/dave/Projects/sample/app/models/user.rb # line 3 :
1: class User < ActiveRecord::Base
2:
=> 3: after_save -> { binding.pry }
pry $ exit
=> true
pry $ u.updated_at
=> Tue, 24 Jun 2014 18:36:04 UTC +00:00
If you want to do something dependent on changes to a particular attribute in a before_save, just do:
pry $ u = User.first
=> #<User id: 1, email: "dave#davebryand.com", username: "dbryand"...
pry $ u.save
=> true
pry $ u.name = "John Doe"
=> "John Doe"
pry $ u.save
From: /Users/dave/Projects/sample/app/models/user.rb # line 3 :
1: class User < ActiveRecord::Base
2:
=> 3: before_save -> { binding.pry if name_changed? }
Dirty docs here: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html

Why does Date.yesterday counts as Date.today also?

I have the following model and methods:
class UserPrice < ActiveRecord::Base
attr_accessible :price, :purchase_date,
def self.today
where(:purchase_date => Date.today)
end
def self.yesterday
where(:purchase_date => Date.yesterday)
end
Why on my form if I give my date_select field :purchase_date 1/4/2012(yesterday method) it also counts as today(today method) and if I give it 1/5/2012 (today) it is nil?
P.S. I am using Rails 3.0.10 with PostgreSQL.
Update
This is my console returns:
$ rails console --s
Loading development environment in sandbox (Rails 3.0.10)
Any modifications you make will be rolled back on exit
irb(main):001:0> Date.today
=> Wed, 04 Jan 2012
irb(main):002:0> Time.now
=> 2012-01-04 21:28:07 -0500
irb(main):003:0> Time.zone.now
=> Thu, 05 Jan 2012 02:28:18 UTC +00:00
irb(main):004:0> Date.yesterday
=> Wed, 04 Jan 2012
irb(main):005:0>
Now yesterday is screwed up, makes no sense....
This is happening because calculations.rb is calling the "current" method of the Date class for the configured timezone (Defaults to UTC).
If you open the rails console you can see the date at which "Date.yesterday" is calculating on by doing:
Time.zone.today
This will probably show you tomorrow's date. So Date.yesterday for what rails sees as today, is today. ;)
You can work with the Date library more directly by doing:
Date.today.advance(:days => -1)
This will give you yesterday's date like you expect whereas active_support is returning:
Date.current.advance(:days => -1)
If you use the Time class, you'll have access to UTC (or "zulu") time zone rather than using the environment's time zone in Date class.
class UserPrice < ActiveRecord::Base
attr_accessible :price, :purchase_date,
def self.today
where(purchase_date: today_utc_date)
end
def self.yesterday
where(purchase_date: today_utc_date.yesterday)
end
private
def today_utc_date
#today ||= Time.zone.today
end
end
Also, if you need to process an outside date, for example params[:purchase_date]:
Time.parse(params[:purchase_date]).utc.to_date

Rails to_json Why are timestamps breaking, and being set to 2000-01-01T01:31:35Z

I'm building a json object in rails as follows:
#list = Array.new
#list << {
:created_at => item.created_at
}
end
#list.to_json
Problem is this gets received by the browser like so:
"created_at\":\"2000-01-01T01:31:35Z\"
Which is clearly not right, in the DB it has:
2011-06-17 01:31:35.057551
Why is this happening? Any way to make sure this gets to the browser correctly?
Thanks
You need to do some testing / debugging to see how that date is coming through.
For me, in Rails console (Rails 3.0.9, Ruby 1.9.2)
ruby-1.9.2-p180 :014 > d = Date.parse("2011-06-17 01:31:35.057551")
=> Fri, 17 Jun 2011
ruby-1.9.2-p180 :015 > #list = {:created_at => d}
=> {:created_at=>Fri, 17 Jun 2011}
ruby-1.9.2-p180 :016 > #list.to_json
=> "{\"created_at\":\"2011-06-17\"}"
i.e. it's just fine. Can you see whether the date is really ok?
The trouble lies with the way to_json escapes characters. There is a very good post on the subject here:
Rails to_json or as_json
You may need to look into overriding as_json.

Resources