Is there a way to get a collection of all the Models in your Rails app?
EDIT: Look at the comments and other answers. There are smarter answers than this one! Or try to improve this one as community wiki.
Models do not register themselves to a master object, so no, Rails does not have the list of models.
But you could still look in the content of the models directory of your application...
Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path|
# ...
end
EDIT: Another (wild) idea would be to use Ruby reflection to search for every classes that extends ActiveRecord::Base. Don't know how you can list all the classes though...
EDIT: Just for fun, I found a way to list all classes
Module.constants.select { |c| (eval c).is_a? Class }
EDIT: Finally succeeded in listing all models without looking at directories
Module.constants.select do |constant_name|
constant = eval constant_name
if not constant.nil? and constant.is_a? Class and constant.superclass == ActiveRecord::Base
constant
end
end
If you want to handle derived class too, then you will need to test the whole superclass chain. I did it by adding a method to the Class class:
class Class
def extend?(klass)
not superclass.nil? and ( superclass == klass or superclass.extend? klass )
end
end
def models
Module.constants.select do |constant_name|
constant = eval constant_name
if not constant.nil? and constant.is_a? Class and constant.extend? ActiveRecord::Base
constant
end
end
end
Rails: Overriding ActiveRecord association method
You can use block with has_many
to extend your association with methods. See comment "Use a block to extend your associations" here.
Overriding existing methods also works, don't know whether it is a good idea however.
has_many :tags, :through => :taggings, :order => :name do
def << (value)
"overriden" #your code here
super value
end
end
Clean way to return ActiveRecord object with associations
Solution
A DRY approach is inside your models, you can define a attributes
method and have it return the shape of the object that you want the render function to use.
# thing.rb
def attributes
# Return the shape of the object
# You can use symbols if you like instead of string keys
{
'id' => id, # id of the current thing
'other_fields' => other_fields, # add other fields you want in the serialized result
'object_a' => object_a, # explicitly add the associations
'object_b' => object_b
}
end
The associations object_a
and object_b
should get serialized as normal. You can repeat the same approach for them by adding an attributes
method in their respective classes if you want to limit/customize their serialized result.
So when render json:
is called on a single, or a collection of, thing model(s), the shape of the json objects returned will be as defined in the method above.
Note:
One caveat is your key names in the hash being returned in attributes
has to match the name of the method (or association name). I'm not too sure why. But the workaround that I've used when needing to add a key with a different name than its corresponding column is to make a method in the model of the key name I want to use.
For example, say your Thing
model has a column name
, but in your json result you want the key name that corresponds to that column to be called name_of_thing
. You'd do the following:
def name_of_thing
name
end
def attributes
{
'name_of_thing' => name_of_thing,
# other fields follow
# ...
}
end
Conditional Logic
Conditions dependent on fields/associations in the model
The attributes
method can support conditional based on fields in the model.
# thing.rb
def attributes
result = {}
result['id'] = id
# add other fields
# For example, if association to object_a exists
if object_a
result.merge!({
'object_a' => object_a
})
end
# return result
result
end
Conditions dependent on input from outside the model
If you want to make your method render different fields in different places, one thing you can do is override the as_json
method, which can work better for these cases because this method accepts options in a parameter.
# thing.rb
def as_json(options = {})
result = {}
result['id'] = id
# add other fields to result
if(options[:show_only_ids])
result.merge!({
'object_a_id' => object_a_id,
'object_b_id' => object_b_id
})
else
result.merge!({
'object_a' => object_a,
'object_b' => object_b
})
end
# return result
result
end
Then you need to modify your controller (or wherever you're calling the serialization of the Thing
model) to pass in the appropriate option when necessary.
# thing_controller.rb
render json: Thing.all.as_json({ show_only_ids: true })
When rendering, you don't always have to explicitly specify the as_json
. The render function will call it anyway by default. You only need to explicitly make that call when you want to pass in options.
Declarative_Athorization: with_permissions_to for all models on single place
Try adding:
default_scope { with_permissions_to(:read) }
to your models
What is the right way to override a setter method in Ruby on Rails?
===========================================================================
Update: July 19, 2017
Now the Rails documentation is also suggesting to use super
like this:
class Model < ActiveRecord::Base
def attribute_name=(value)
# custom actions
###
super(value)
end
end
===========================================================================
Original Answer
If you want to override the setter methods for columns of a table while accessing through models, this is the way to do it.
class Model < ActiveRecord::Base
attr_accessible :attribute_name
def attribute_name=(value)
# custom actions
###
write_attribute(:attribute_name, value)
# this is same as self[:attribute_name] = value
end
end
See Overriding default accessors in the Rails documentation.
So, your first method is the correct way to override column setters in Models of Ruby on Rails. These accessors are already provided by Rails to access the columns of the table as attributes of the model. This is what we call ActiveRecord ORM mapping.
Also keep in mind that the attr_accessible
at the top of the model has nothing to do with accessors. It has a completely different functionlity (see this question)
But in pure Ruby, if you have defined accessors for a class and want to override the setter, you have to make use of instance variable like this:
class Person
attr_accessor :name
end
class NewPerson < Person
def name=(value)
# do something
@name = value
end
end
This will be easier to understand once you know what attr_accessor
does. The code attr_accessor :name
is equivalent to these two methods (getter and setter)
def name # getter
@name
end
def name=(value) # setter
@name = value
end
Also your second method fails because it will cause an infinite loop as you are calling the same method attribute_name=
inside that method.
Does adding a :before_save to an AR model override one already added through a custom extension to AR?
To clarify, both the default added above and any customized before_save callback added to a specific model will execute. . . I believe same rules as described under "Inheritable callback queues" in the ActiveRecord::Callbacks docs will apply. Callback macros like before_save "add behavior into a callback queue that is kept intact down through an inheritance hierarchy." – Jordan
Related Topics
How to Get Words Frequency in Efficient Way with Ruby
Rails Redirect_To :Back Not Working
Can't Launch Simple Sinatra App Using Rackup and Jruby (No Response from Web Server)
Why Are Metaclasses Created in Ruby
How to Make a Non-Blocking Request for an Exclusive Lock Using File#Flock
How to Enable Ssl for a Standalone Sinatra App
Differencebetween 'File.Read' and 'Io.Read'
How to Make a Specific Gem Version as Default
Native Extensions Fallback to Pure Ruby If Not Supported on Gem Install
How to Pass <Arguments> to Irb If I Don't Specify <Programfile>
Passing Binding or Arguments to Erb from the Command Line
How I Know My Document's Size Inside Mongodb with the Ruby Driver
In a Sinatra App on Heroku, Session Is Not Shared Across Dynos
Postgres Range Fields + Rails Forms
Add a Callback Function to a Ruby Array to Do Something When an Element Is Added