In rails, if I want to override an attribute method, eg. a setter, or getter etc, I might need the instance method to be defined.
However, activerecord does not define attribute methods until an instance is first synchronized.
This can be seen in:
class MyModel < ActiveRecord::Base
end
MyModel.attribute_methods_generated? # => false
MyModel.instance_method(:a_db_column)
# => NameError Exception: undefined method `a_db_column' for class `MyModel'
MyModel.new # implicitly calls define_attribute_methods
# MyModel.define_attribute_methods # can also use this instead of MyModel.new
MyModel.attribute_methods_generated? # => true
MyModel.instance_method(:a_db_column)
#<UnboundMethod: MyModel(#<Module:0x000000030a20a0>)#__temp__>
Is there any problem that could occur in calling define_attribute_methods early? Even doing something like:
class MyModel < ActiveRecord::Base
define_attribute_methods
# is there any code here which might cause problems?
end
Why do you need the instance method to be defined ? From what I understand, you're trying to add/override an instance method, so when you call this method, define_attribute_methods would have been called since an instance was created.
Tell me if I get you wrong.
From the information you provided it seems like you want to patch the methods generated by ActiveRecord on a model. Instead of triggering ActiveRecord's method generation, why not patch the define_attribute_methods to call your patching method after it has completed?
Related
Let's say I have this setup in Ruby on Rails:
class A < ActiveRecord::Base
after_create :perform_some_action
#...
private
def perform_some_action
if some_condition_met?
#take some action
end
end
Should I add something to the method name, perform_some_action, to indicate that it depends on a condition?
Definitely, yes, because your condition check is inside the method.
This way, when calling your method from outside, the code will look self-explanatory. If not, when you read your call it will give you the impression that take_some_action is performed whithout any condition.
However, if 37 conditions are required inside your method, perhaps it's the signal that this method should be split up in several smaller ones.
I saw in the documentation that in ROR 3.2 it's generally better to use scope methods in the model for retrieving records with arguments instead of lamda scopes
Using a class method is the preferred way to accept arguments for
scopes. These methods will still be accessible on the association
objects
But when do you use dynamic finders (such as find_by_* instead of scope and scope methods? dynamic finders can also receive arguments and they seem to be simpler to use directly from the view.
If I want to retrieve all the records that have
attribute1 == [boolean] AND attribute2 IN [array]
which method is advisable (scope, scope method, or dynamic finder?)
You shouldn't be using any finders in your views.
I would probably write this using a class method.
something like this:
class Dog < ActiveRecord::Base
attr_accessible :chewed, :color
def self.toys chewed = false, colors = []
where chewed: chewed, color: colors
end
end
I'm using a decorator class to add some functionality to a model of mine. I only want this information to be used to process some values but not to save anything to the database.
I have my decorator class, something like:
class Deco
def initialize o
#target = o
end
def method_missing method, *args, &block
#target.send(method, *args, &block)
end
#my extra methods
end
And I use it like so:
deco_model = Deco.new(model)
There problem here is when I want to associate this deco_mode to another one, I get a type mismatch error, which makes sense, but if I add the following method to my decorator class:
def class
#target.class
end
I still get the same error, but it says: Model(#aaaaaa) expected, got Model(#aaaaa)
Yes, the "Model" would be the class of my model, and the object id is the same in both cases... so if the object id is the same, why am I still getting the exception?
Thanks
Turns out I also had to overwrite the is_a? method used inside ActiveRecord::Associaions::Association
After doing:
def is_a? klass
#target.class.object_id == klass.object_id
end
The exception is not being thrown anymore.
I have my mongoid query in the model
def self.get_result collection_name, hash_params, page, per_page
self.collection_name = collection_name
#result_pg = self.where(hash_params).page(page).per(per_page)
end
I have it here because my collection names are passed as parameters and I don't have models for all my collection. Instead I have one model and I set its name dynamically based on the request parameter.
My controller code
Collection.get_result params[:state], hash_param, params[:page], params[:per]
My View code
<%= paginate #result_pg %>
When I use the above code I get
undefined method `current_page' for nil:NilClass ERROR
I think the variable is being lost when you call it in the model. Instead, have it return the object back to you like so:
return self.where(hash_params).page(page).per(per_page) // put this in your model
Then, I'm the controller, connect the pieces:
#result_pg = Collection.get_result params[:state], hash_param, params[:page], params[:per]
// in your controller like so
Right now, the controller isn't setting #result_pg, that is why it is nil and you are getting the nil error. Try this and let me know if that worked out for you.
Updated
Ok, as for the reason this happens. When Rails loads a controller, the variables that you declare with a # symbol, like #bologna will be passed to the view and something will be done with them.
On the other hand, you are not technically declaring and instantiating that variable in the controller, you are doing it in the model, as per your source code that you posted above. Declaring the variable there can be done but it isn't useful because the controller has no idea that it even happened. When you tell the Class Collection to perform a method, usually you have that method return something back to you, if you don't, then the variable is lost.
It is similar to me asking someone to go to the store and buy me some groceries, you did the right things, giving the method all the information it needed, passing it the right variables to do its job correctly, but not telling it to actually come back with them, is where the error occurs. In your method, the groceries get purchased at the store and left there. The revised function I wrote for you tells it return with the groceries and put them in the variable #result_pg. The variable is declared in the Controller like it is supposed to be.
So in short, anything you want to have accessible in the view, needs to be declared in the controller. And anytime you want something back from a method, always have it return the information to you.
Also, it isn't entirely necessary to even have the method call to the Class Collection. In a project of my own where I use Kaminari, I just simply do the whole call from in the controller like so:
#notes = current_user.notes.page params[:page]
// My Application has :users that have_many :notes
So you could simplify it that way if you want, but the method that I suggested that fixes it the way you are doing it will work too, however you prefer.
I hope my explanation helped and wan't too long winded.
class InsertRemoveJTree( JTree ):
def setModel( self, treeModel ):
# for reasons I don't understand, the first call below results in infinite recursion...
# in other words the "super" approach returns this object, not its underlying base-class
# object. The second approach works as expected
# super( InsertRemoveJTree, self ).setModel( treeModel )
JTree.setModel( self, treeModel )
anyone got any idea what this is about? "setModel" is an attribute of the InsertRemoveJTree object as listed by dir().
Also many other methods from JTree work fine using the "super" approach.
I also tried going:
super(InsertRemoveJTree, self ).model = treeModel
... but it claimed there was no attr "model"
Later
I have come to the conclusion that this is a restriction due to the fact of calling a method on a pre-existing Java class, and that my statement "Also many other methods from JTree work fine using the "super" approach." is wrong. There are some protected methods which are called by means of super_XXX (e.g. DefaultTreeModel: super_fireTreeNodesInserted) and which cause quite a few problems to a novice Jython user, until you start examining instances' attributes using dir( instance )). But as a rule this appears to be a general restriction: i.e. you can't call the base class of a Java class subclassed in Jython using "super( PresentClass, self )..."
Your first call means "Call the setModel defined in InsertRemoveJTree", ie a recursive call.
You want super( JTree, self ).setModel( treeModel ) to call the one defined in JTree.
Here's the Python doc for super.