Rails: #inspect not displaying attribute - ruby-on-rails

I am defining #foo as a class instance attribute, and using the after_initialize callback to set the value of this when a record is created/loaded:
class Blog < ActiveRecord::Base
#foo = nil
after_initialize :assign_value
def assign_value
#foo = 'bar'
end
end
However, when I inspect a Blog object, I am not seeing the #foo attribute:
> Blog.first.inspect
=> "#<Blog id: 1, title: 'Test', created_at: nil, updated_at: nil>"
What do I need to do to get inspect to include this? Or conversely, how does inspect determine what to output?
Thanks.

Active record determines which attributes to show in inspect based on the columns in the database table:
def inspect
attributes_as_nice_string = self.class.column_names.collect { |name|
if has_attribute?(name)
"#{name}: #{attribute_for_inspect(name)}"
end
}.compact.join(", ")
"#<#{self.class} #{attributes_as_nice_string}>"
end
Lifted from base.rb on github
To change the output of inspect you'll have to overwrite it with your own method e.g.
def inspect
"#{super}, #foo = #{#foo}"
end
Which should output:
> Blog.first.inspect
=> "#<Blog id: 1, title: 'Test', created_at: nil, updated_at: nil>, #foo = 'bar'"

Related

ActiveModelSerializers :camel_lower configuration not working

I want to leverage AMS to create json data to pass as GraphQL variables in my test suite. Apparently, it support :camel_lower which would convert hash keys like :some_field to :someField but I can't seem to get it to work. Here's the relevant code:
/config/initializers/active_model_serializers.rb:
ActiveModelSerializers.config.key_transform = :camel_lower
/app/serializers/service_serializer.rb:
class ServiceSerializer < ApplicationSerializer
attributes :id, :name, :time_increment
end
rails console:
ActiveModelSerializers.config.key_transform
=> :camel_lower
s = Service.new(name: 'Test', time_increment: 10)
=> #<Service id: nil, name: "Test", time_increment: 10, created_at: nil, updated_at: nil>
ss = ServiceSerializer.new(s)
=> #<ServiceSerializer:0x00007f3771dd9dc0 #object=#<Service id: nil, name: "Test", time_increment: 10, created_at: nil, updated_at: nil>, #instance_options={}, #root=nil, #scope=nil>
ss.as_json
=> {:id=>nil, :name=>"Test", :time_increment=>10}
The result I was expecting was:
=> {:id=>nil, :name=>"Test", :timeIncrement=>10}
ActiveModelSerializers has been in some sort of maintainance state for a long time and doesn't seem to be receiving any updates.
My personal choice has been either the blueprinter gem or jsonapi-serializers. The blueprinter one is closer to AMS.
It is very easy to work with
# Gemfile
gem 'blueprinter'
and the usual
bundle install
Creating a serializer is very straightforward
# e.g. app/blueprinter/service_blueprint.rb
class ServiceBlueprint < Blueprinter::Base
# identifier :id
fields :id, :name, :time_increment
end
Add a class LowerCamelTransformer
# e.g. app/blueprinter/lower_camel_transformer.rb
class LowerCamelTransformer < Blueprinter::Transformer
def transform(hash, _object, _options)
hash.transform_keys! { |key| key.to_s.camelize(:lower).to_sym }
end
end
And in config/initializers/blueprinter.rb
Blueprinter.configure do |config|
config.default_transformers = [LowerCamelTransformer]
end
Test it
s = Service.find(1)
puts ServiceBlueprint.render(s)
# Will give you a nice output with lower camel case
Use
ActiveModelSerializers.config.adapter = :json
it worked for me

Unable to set Rails model attribute from console or controller

I'm new to Rails and am working on getting an application set up in Rails 4.2.4. I have a model called List that looks like the following in the database (PostgresQL):
List(id: integer, user_id: integer, name: string, created_at: datetime, updated_at: datetime, friendly_name: string)
and in List.rb:
class List < ActiveRecord::Base
attr_accessor :name, :friendly_name
belongs_to :user
has_many :items
end
I am trying to modify the name attribute from a controller action:
def save_name
...
list_to_edit = List.find(params[:id].to_i)
list_to_edit.name = params[:name]
list_to_edit.save!
...
end
But the changes are not being persisted. I have confirmed that params[:name] and list_to_edit are not nil. When I try to change the attribute in the Rails console like this:
> l = List.last
> l.name = 'TestName'
> l.save!
I don't see any errors. After executing the above commands and executing l.name I do see TestName. When I type l or List.last, however I still see
#<List id: 29, user_id: 17, name: nil, created_at: "2015-11-07 18:55:04", updated_at: "2015-11-07 18:55:04", friendly_name: nil>
What do I need to do to set the name attribute of a List? I can post any additional file content if it is helpful.
After trying a few more things it looks like all I needed to do was remove name from the array being passed to attr_accessor in List.rb. I believe when I was trying to change the list name with my_list.name = 'something' I was modifying the instance variable, not the attribute stored in the database.

Rails console is not showing attribute when called

>> Reply.first
=> #< Reply id: 1, body: "line1\r\n\r\nline2\r\n" >
But when I do
>> Reply.first.body
=> "line1"
Its breaking a few of my tests where they are looking for :
assert_difference 'Reply.where(:body => "line1\r\n\r\nline2").count' do
How can my tests be reassured there are line breaks?
Seems like you have a custom getter, something like:
class Reply < ActiveRecord::Base
def body
"foo"
end
end
reply = Reply.new(body: "bar")
#=> #<Reply id:nil, body: "bar" created_at: nil, updated_at: nil>
reply.body
#=> "foo"
In that case, you can fetch the raw attribute using Model[:attribute_name]:
reply[:body]
#=> "bar"
Change the snytax a little bit when you have backslash's
assert_difference 'Reply.where("body = 'line1\r\n\r\nline2\r\n'").count' do

ActiveRecord derived attribute persistence which depends on id value

How do you persist a derived attribute which depends on the value of id in rails? The snippet below seems to work-- Is there a better rails way?
class Model < ActiveRecord::Base
....
def save
super
#derived_attr column exists in DB
self.derived_attr = compute_attr(self.id)
super
end
end
Callbacks are provided so you should never have to override save. The before_save call in the following code is functionally equivalent to all the code in the question.
I've made set_virtual_attr public so that it can be calculated as needed.
class Model < ActiveRecord::Base
...
# this one line is functionally equivalent to the code in the OP.
before_save :set_virtual_attr
attr_reader :virtual_attr
def set_virtual_attr
self.virtual_attr = compute_attr(self.id)
end
private
def compute_attr
...
end
end
I think the more accepted way to do this would be to provide a custom setter for the virtual attribute and then provide an after_create hook to set the value after the record is created.
The following code should do what you want.
class Virt < ActiveRecord::Base
def after_create()
self.virtual_attr = nil # Set it to anything just to invoke the setter
save # Saving will not invoke this callback again as the record exists
# Do NOT try this in after_save or you will get a Stack Overflow
end
def virtual_attr=(value)
write_attribute(:virtual_attr, "ID: #{self.id} #{value}")
end
end
Running this in the console shows the following
v=Virt.new
=> #<Virt id: nil, virtual_attr: nil, created_at: nil, updated_at: nil>
>> v.save
=> true
>> v
=> #<Virt id: 8, virtual_attr: "ID: 8 ", created_at: "2009-12-23 09:25:17",
updated_at: "2009-12-23 09:25:17">
>> Virt.last
=> #<Virt id: 8, virtual_attr: "ID: 8 ", created_at: "2009-12-23 09:25:17",
updated_at: "2009-12-23 09:25:17">

Rails Single table inheritance problem

I'm trying to setup single table inheritance in my Rails app for a User model and its subclasses Member, Subscriber, and Staff.
I have a model file for each: user.rb, member.rb, etc
The user model is defined: class User < ActiveRecord::Base; end;
I subclassed the other models as such: class Member < User; end; and so on.
In my users table I have all the fields every class needs plus the type field. Now when I go to the console and try to create a new instance of say member or subscriber i get the following error:
TypeError: can't dup NilClass
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2184:in 'dup'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2184:in 'scoped_methods'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2188:in 'current_scoped_methods'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2171:in 'scoped?'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2439:in 'send'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2439:in 'initialize'
from (irb):6:in 'new'
from (irb):6
Rails know the subclasses models are there because in the console when I simply call Member or Subscriber, i get the class definition returned.
I've read the simple documentation, but I must be missing something?
I tried on my side starting from a scratch application and it works
Here is my User model (User.rb)
class User < ActiveRecord::Base
end
My member model (Member.rb)
class Member < User
end
I have one migration file to create my users table which contains:
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :users
end
end
Now launching the console:
➜ ./script/console
Loading development environment (Rails 2.3.4)
>> u = User.new
=> #<User id: nil, name: nil, created_at: nil, updated_at: nil>
>> m = Member.new
=> #<Member id: nil, name: nil, created_at: nil, updated_at: nil>
>> m.name="hop"
=> "hop"
>> m.save
=> true
However I did not manage to reproduce your error :(
Do you have a type column of type varchar (string in ruby)? Try the following commands (in a new rails project)
class Member < User
end
C:\projects\test\sti>ruby script\generate model user name:string type:string membertype:string
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/user.rb
create test/unit/user_test.rb
create test/fixtures/users.yml
create db/migrate
create db/migrate/20091019051506_create_users.rb
C:\projects\test\sti>rake db:migrate
(in C:/projects/test/sti)
== CreateUsers: migrating ====================================================
-- create_table(:users)
-> 0.0000s
== CreateUsers: migrated (0.0000s) ===========================================
C:\projects\test\sti>ruby script\console
Loading development environment (Rails 2.3.4)
>> u = User.new
=> #<User id: nil, name: nil, type: nil, membertype: nil, created_at: nil, updated_at: nil>
>> m = Member.new
=> #<Member id: nil, name: nil, type: "Member", membertype: nil, created_at: nil, updated_at: nil>
>> m.name = 'fred'
=> "fred"
>> m.save
=> true
>> u.name = 'rader'
=> "rader"
>> u.save
=> true
>> User.find :all
=> [#<Member id: 1, name: "fred", type: "Member", membertype: nil, created_at: "2009-10-19 05:17:11", updated_at: "2009-10-19 05:17:11">, #<User id: 2, name: "rader", type: nil, membertype: nil, created_at: "2009-10-19 05:17:24", updated_at: "2009-10-19 05:17:24">]
>>
Check this page, there are more than few solutions to this problem (even in comments).
http://strd6.com/2009/04/cant-dup-nilclass-maybe-try-unloadable/
I'm thinking that the problem is in one of your model definitions because of the stack trace you show. If you still are having a problem, pastie your code, and i'm sure you'll get a good answer.
I hade exactly this problem, after I extracted some functionality to a plugin.
But i my case it worked from the console, so i made sure id reloaded, with this line in init.rb
ActiveSupport::Dependencies.load_once_paths.delete(
File.expand_path(File.dirname(__FILE__))+'/app/models')
I ran into something similar a while back and this website helped:
http://www.dansketcher.com/2009/05/11/cant-dup-nilclass/
class User < ActiveRecord::Base
unloadable
...
end
Not sure why this occurs as I could not track down anything abnormal. I do believe it was a STI situation though.

Resources