I have a class method mixed in to all my models. the method gets called when the model class is evaluated. unfortunately (for me), this seems to be on-demand, whenever the model is needed in development env. how can have rails load all the models at start up? is this even advisable?
class Foo < ActiveRecord::Base
include Acl
register_acl # i need this to be called for all models at start up
end
Basically, the register_acl takes a few arguments of "actions" that the model would like to be access controlled. Suppose one of the action of Foo is "manage" and the system needs to be aware of this action at start up. I think in the model is the most natural place to have this logic.
thank you!
The correct way to do this application-wide is to turn on cache_classes in your configuration. By default it's off in development but on in production.
If you want to do it sporadically:
Rails.application.eager_load!
I dont know if this is ideal, but it works for me. Somewhere in the config/initialize/, i do this:
Dir.glob("#{Rails.root}/app/models/*.rb").sort.each { |file| require_dependency file }
and that preloads my models
In MVC concept models are not intended to act by themselves, i.e. they should only act when controller sends them a message (for example, #foo.register_acl). Model instances even should not exist until they are created by controller.
What are you trying to achieve with your register_acl method?
If you really need something to be executed on object creation you can use initialize() method which is called whenever a Ruby object is created.
However if you need model to execute some code by itself you are most likely facing some code smell and you need to change something within your app.
Related
I'm writing a Rails app that will focus heavily on searching. For this purpose, I feel like I should split the actual search action on the controller off from another method to prepare the search parameters, just to make sure I can test accurately. However, I'm not sure where to put such a method. Others have told me to put it into a new class, but I'm not sure where you put an extra class file in a Rails project. Is there a customary directory location people usually store extra classes? If not, how would such a method work/be tested as a private method in the same controller by which it is called?
You would usually put extra classes in the lib folder. As for testing, nothing particular really. If you are using RSpec, simply do:
describe ExtraClass do
it 'saves the day'
end
I know the dogma says to not access current_user in a model but I don't fully agree with it. For example, I want to write a set of logging functions when an action happens via a rails callback. Or simply writing who wrote a change when an object can have multiple people write to it (not like a message which has a single owner). In many ways, I see current_user more as config for an application - in other words make this app respond to this user. I would rather have my logging via the model DSL rather than in the action where it seems REALLY out of place. What am I missing?
This idea seems rather inelegant Access current_user in model
as does this: http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
thx
edit #1
So my question isn't if there are gems that can do auditing / logging. I currently use paper_trail (although moving away from it because I can do same functionality in approx 10 lines of ruby code); it is more about whether current_user should never be accessed in the model - I essentially want to REDUCE my controller code and push down logic to models where it should be. Part of this might be due to the history of ActiveRecord which is essentially a wrapper around database tables for which RoR has added a lot of functionality over the years.
You've given several examples that you'd like to accomplish, I'll go through the solution to each one separately:
I want to write a set of logging functions when an action happens via
a rails callback
Depending on how you want to log (DB vs writing to the logger). If you want to log to the DB, you should have a separate logging model which is given the appropriate information from the controller, or simply with a belongs_to :user type setup. If you want to write to the logger, you should create a method in your application controller which you can call from your create and update methods (or whatever other actions you wanted to have a callback on.)
Or simply writing who wrote a change when an object can have multiple people write to it
class Foo < ActiveRecord::Base
belongs_to :user, as: :edited_by
end
class FooController < ApplicationController
def update
#foo = Foo.find(params[:id])
#foo.attributes = params[:foo]
#foo.edited_by = current_user
end
end
I think you're misunderstanding what the model in Rails does. Its scope is the database. The reason it can't access current_user, is because the current user is not stored in the database, it is a session variable. This has absolutely nothing to do with the model, as this is something that can not exist without a browser.
ActiveRecord::Base is not a class that is designed to work with the browser, it is something that works with the database and only the database. You are using the browser as an interface to that model, but that layer is what needs to access browser specific things such as session variables, as your model is extending a class that is literally incapable of doing so.
This is not a dogma or style choice. This is a fact of the limitations of the class your model is extending from. That means your options basically boil down to extending from something else, handling it in your controller layer, or passing it to the model from your controller layer. ActiveRecord will not do what you want in this case.
The two links you show (each showing imho the same approach) is very similar to a approach I still use. I store the current_user somewhere (indeed thread-context is the safest), and in an observer I can then create a kind of audit-log of all changes to the watched models, and still log the user.
This is imho a really clean approach.
An alternative method, which is more explicit, less clean but more MVC, is that you let the controller create the audit-log, effectively logging the actions of the users, and less the effects on different models. This might also be useful, and in one website we did both. In a controller you know the current-user, and you know the action, but it is more verbose.
I believe your concerns are that somehow this proposed solution is not good enough, or not MVC enough, or ... what?
Another related question: How to create a full Audit log in Rails for every table?
Also check out the audited gem, which solves this problem as well very cleanly.
Hope this helps.
We have created a FakeModel object class which inherits from Object,
to allow working with models who don't have a DB table.
It has the basic functionality of a regular ActiveRecord model.
We also added in the class the following line:
include ActiveRecord::Validations
The problem is this:
A new request is sent to the controller, and creates a new object inheriting from the FakeModel class.
When the validations of that object run, they run more then once.
Too be more specific - with each request sent to the server,
the validations run one time more than the last request.
I'm guessing something here "sticks" on the server-level
(of course, when I restart the server, it resets back to running the validations just once)
What can be the cause of that?
UPDATE :
The ActiveModel solution isn't possible for me because I'm using Rails 2.3.8. I still need to figure out where is the problem.
I would suggest you to use ActiveModel instead of writing your own Model engine from scratch, see this blog post for a tutorial you can also watch this screencast
I'm stabbing the dark here, but it sounds like the validations keep being included every time the model is loaded/saved.
Can you show us where you include it?
In Hyperactive Resource, instead of include we used:
# make validations work just like ActiveRecord by pulling them in directly
require "active_record/validations.rb"
extend ActiveRecord::Validations::ClassMethods
I want to add a method to a Rails model, to be used in testing. If I do this
class Model
def something_new
do_something
end
end
in the Rails console or in a file loaded at run time, Model is overwritten rather than modified. If I put something like v = Model.classbefore the lines above, the new method is successfully added to the existing class. Apparently the reference is needed to signal that an existing class is being re-opened.
On the other hand, one can add a method to, say, Fixnum without having to refer to it first. What is going on here, and what is the usual way to ensure that an existing class is re-opened and modified rather than being overwritten?
Thanks.
It sounds like you're not requiring the class before using it. When you write Model.class and there is no Model class, Rails automagically brings in Model for you. If you just write class Model, it just sees that as a class definition. Just doing require 'model' should work.
Use class_eval, that way you will be reopening the class the right way.
Here's a very good article on reopening classes.
Just as an addition to Chuck's answer here is the quote from Rails docs:
6.1.1 Constants after the class and module Keywords
Ruby performs a lookup for the constant that follows a class or module keyword because it needs to know if the class or module is going to be created or reopened.
If the constant is not defined at that point it is not considered to be a missing constant, autoloading is not triggered.
I'm trying to access a model for global system settings. It should be loaded in almost every controller. The problem is I can't find any help on getting rails to load the dam thing.
If this helps, the model is called Config and has two columns; 'key' and 'value'
I come from a background in PHP. I remember codeigniter could load models via.
$this->load->model('a_model_name');
The controllers I'm trying to load the model in do not have the same name as the model.
The problem here is actually the name of the model. Config is used by other Rails classes - see the list of reserved names on the Rails wiki
Rails autoloads classes when it needs them. So long as the class is in a file with the same name, and it's in one of the directories Rails looks for classes in, you just use the class as if it was loaded and Rails will load it automatically
You can add before_filter in ApplicationController:
before_filter :load_config
...
private
def load_config
something
end
By default all controllers in Rails inherits from ApplicationController, so all controllers will execute load_config method at the begining.
By the way I really like rails-settings gem/plugin for configuration. It is probably something that you need. And it loads data when it is needed, so you don't have to load config in each controller, but just when you need some value, then you just do:
Settings.key
and you get what you want.
Presumably, if you want to load a piece of information with a particular key you could use Config.find_by_key('the_key_you_want') to get the record in question.
I'm not sure that this would be the best way to handle your configuration, but it really depends on how your system needs to work.
EDIT:
Didn't see your update to the question before I posted my answer, seems like I misunderstood what you meant. Leaving this answer here in case it's useful, Gareth's answer seems like a fair answer.