Rails as_json include parent object? - ruby-on-rails

Hello I'm trying to use as_json to output the parent object as an include.
Here is my code :
photo.as_json(:include => [:comments, :likes])
This code works, this one doesn't :
photo.as_json(:include => [:comments, :likes, :user])
I get the error :
NoMethodError: undefined method `macro' for nil:NilClass
Any one ?
Thanks :)

Try
user = User.find(1)
user.as_json(:include => {:photos => {:include => [:comments, :likes]}})

I ended up using acts_as_api which allows for methods, templates and a lot of cool features that got the work done much easier.

you call the "methods" option instead:
photo.as_json(:methods => [:user], :include => [:comments, :likes, :user])
I've used this in Rails 4.0, ruby 2.0 to bring back what i need.

Related

Downgrading rails project to Ruby 1.9.3 | amusing .to_json trouble

Dear stackoverflow people,
I want to downgrade a rails project so it can run on rails 3 without any problems. It worked before on the newest version of rails, but the office does not want to use this one. I have trouble with rewriting this particular line:
#results = #sessions.to_json(:include => [:orientations, :subtopics, :data_files, :participants, :formats, :comments => {:include => [:user => {:only => [:id, :name]}]}])
#sessions are of course a list of results. Is there anyone who knows how I can write an alternative for this that will also run on older versions of rails?
Thanks in advance
Kind regards
Here you go:
Anytime to_json is called on an object, as_json is invoked to create the data structure, and then that hash is encoded as a JSON string using ActiveSupport::json.encode. This happens for all types: Object, Numeric, Date, String, etc (see active_support/json).
ActiveRecord objects behave the same way. There is a default as_json implementation that creates a Hash that includes all the model’s attributes. You should override as_json in your Model to create the JSON structure you want. as_json, just like the old to_json, takes an option hash where you can specify attributes and methods to include decoratively.
def as_json(options={})
super(:only => [:email, :avatar], :include =>[:addresses])
end
Your controller code to display one model should always look like this:
render :json => #user
And if you have to do anything out of the ordinary, call as_json passing your options.
render :json => { :success => true,
:user => #user.as_json(:only => [:email]) }
The moral of the story is: In controllers, do not call to_json directly, allow render to do that for you. If you need to tweak the JSON output, override as_json in your model, or call as_json directly.
Fix your code now to use as_json - it will be one less thing to worry about when you migrate to Rails 3 or Ruby 1.9.3.

Rails 3: using composed_of with validation causes ActiveRecord error

I am using the following code snippet from the Rails docs to convert IPs into integers before inserting them into the database:
composed_of :user_ip,
:class_name => 'IPAddr',
:mapping => %w(user_ip to_i),
:constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
:converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
The composed_of block is then followed by this simple validation:
validates_uniqueness_of :user_ip
But the validation in turn throws an error when trying to create a new object:
TypeError: Cannot visit IPAddr
Remove the validation and the error is gone.
If I understand correctly, this is happening because :user_ip becomes an IPAddr object, and that does not sit well with ActiveRecord. Is this correct, and if so, is there a way around it?
Not sure if you ever found a solution to this, but I was able to patch it in my gem by adding a visit method to Arel.
You should be able to do something like the following to get it to work. This is based on how Arel converts values for other types of objects such as dates. Not sure if you need to convert the value to a string, but it might just work as an integer.
module Arel
module Visitors
class ToSql
def visit_IPAddr
quote(value.to_i)
end
end
end
end
I needed to be able to dynamically generate these methods so I used the following in my gem:
Arel::Visitors::ToSql.class_eval do
define_method "visit_#{klass.name}", lambda {|value| quote(value.to_s) }
end

Upgrading Rails 2.3.11 to 3.1.3 - trying to replace proxy_owner with proxy_association.owner

Working on upgrading an internal gem from Rails 2.3.11 to 3.1.3 - have the following piece of code which models a channel of actions:
has_many :actions, :class_name => 'Streamer::Model::Action', :through => :action_channel_entries, :order => 'actions.id desc' do
def publish(action)
proxy_association.owner.publish(action)
end
def subscriptions
proxy_association.owner.subscriptions
end
end
Instead of a CollectionProxy the self in this has_many is an ActiveRecord::Relation as the error from my unit test demonstrates:
undefined local variable or method `proxy_association' for #<ActiveRecord::Relation:0x106d60f68>
from /Users/brett/.rvm/gems/ree-1.8.7-2011.03#ncsl_r313/gems/activerecord-3.1.3/lib/active_record/relation.rb:459:in `method_missing'
from /Users/brett/Projects/ncsl/app/lib/streamer/model/action_channel.rb:11:in `subscriptions'
from (irb):10
Right now I'm just looking for suggestions on how to investigate this further and perhaps some understanding of the different uses of CollectionProxy and ActiveRecord::Relation.
You can try something like this:
#association.owner
The problem was fixed via this issue: https://github.com/rails/rails/issues/3890

Ruby on Rails: Defining a method with options

I'm looking to define a method that lets me pass options; something like:
#user.tasks(:completed => true)
I thought something like this would work in my user model (but it's not):
User.rb model
def tasks(options)
tasks.find(:all, options)
end
How would I define the method correctly to let me use #user.tasks(:completed => true)?
This is basically how I'd do it:
def tasks(options={})
unless options[:something].blank?
# do stuff
end
end
There are some different ways to pass options, but you definitively want to pass a hash with a default value (so that you can call the method without options).
In your case the following should address what you want to do:
def tasks(options={})
Task.find(:all, options[:conditions])
end
Edit: and then call it #thing.tasks( {:conditions => "blah"} )
I haven't tested but it should be ok
Edit 2: But like EmFi said it's not optimal to do this. Consider using an association instead. You'll be able to go #thing.tasks.find(:all, :conditions => {blah})
Does User have a has_many :tasks association? That seems to be what you're after here. In that case Rails provides finders for you, which you can access like this:
#user.tasks.find :all, :conditions => { :completed => true }
Or even shorter:
#user.tasks.all :conditions => { :completed => true }
If that's not terse enough and you always want to use a particular condition, try a named scope:
# In your Task model:
named_scope :completed, :conditions => { :completed => true }
# Then you can just call...
#some_user.tasks.completed # => Only completed Tasks for #some_user
Why would you associate a find all on another model with an instance method? I could understand if it was a relation and the find required find options based on the calling record. But there's ActiveRecord Associations for that.
Then there's ActiveRecord::Base#all(options) which is an alias for Task.find(:all, options)
Together make things simpler:
class User < ActiveRecord::Base
has_many :tasks
end
#user.tasks.all(:conditions => {:completed => true})
what you need is:
options[:conditions] in your method
Activerecord provides a method called with_scope, so to pass any additional conditions
#user.tasks(:completed => true)
you can define the task method as
def tasks(options={})
with_scope :find => options
User.all :order => 'id desc'
end
end
and this will merge any hash passed as options parameter with the actual find
The only caveat is you need to modify your method call slightly
#user.tasks(:conditions => {:completed => true})
or to something like
#user.tasks(:select => 'username')
But if there is an association between user and tasks model then I would do what Jordan has in his post

Custom getters in Ruby on Rails

I have a MailingList model that has_may :people
For most of my application, I only want to get people that are active
So #mailing_list.people should only return people that are active
In my model, I can't do
def people
self.people.find_all{ |p| !p.activated_at.nil? }
end
because that keeps calling itself. What is the ruby/rails way to automatically filter the people. Another possible issue is that I think self.people returns an array of active record objects where self.people.find_all... will return an array. This will cause some of my code to break. It's easy fixes but is there a way to return active record objects? It would be nice to have the option.
Thanks!
This is a perfect example for a named scope:
class Person < ActiveRecord::Base
named_scope :active, :conditions => 'activated_at is not null'
end
Then just call it:
# equivalent to Person.find(:all, :conditions => 'activated_at is not null')
#active_people = Person.active
You can also filter at the association level.
has_many :people, :conditions => {:activated => true}
You can used the standard find method or a dynamic finder. Your find might read as follows:
people.find(:all, :conditions => "activated_at = nil")
OR
people.find_all(:conditions => "activated_at = nil")
A dynamic version of this might read as:
people.find_by_activated_at(nil)

Resources