I'm trying to use a specific DB on a whole model. Basically, we have a master/slave DB configuration, and I want to do using(:master) on whole order-related methods (so on the whole order model).
The thing is that I can apparently do using(:master) at the top of my model, but I'm unsure about what it does and I cant find it in the doc:
Will all the calls in the model use master (like, if a method does User.find in this model, will it use master?)
or
Will all the calls all across the app for the Order methods use master (like, if a method in the User model does Order.find, will it use master?)
Thanks for the explanation.
Here is how we did it.
We made the master connection the same as the default database defined in the database.yml.
Then in each of the models that live in master we would put the following line right below the class line.
octopus_establish_connection Rails.env.to_sym
Related
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.
I am new to ruby and rails and I am having difficulty conceptualizing the MVC techniques in conjunction with database views. I am dealing with a legacy database that has several viiews that are used to generate reports.
Where I get lost is how do I actually use a database view. Should it be put in a model? If so what exactly would that look like?
As an example the legacy db has a view called qryTranscriptByGroup. It is used in the legacy application in an SQL statement such as "SELECT * FROM qryTranscriptByGroup WHERE group='test_group'". This returns a small number of records usually less than 100.
If i create a model, Transcript, how would I define a method like Transcript.find_by_group(group)? As well, it would seem that I might need to prevent any other "find" methods as they would be invalid in this context.
There is also the the fact that the view is read-only and I would need to prevent any attempts to create, update or destroy it.
Perhaps I am going about this entirely the wrong way. The bottom line is that I need to get information from several tables (models?) that represent the information about a user (a transcript). Actually one or more users (transcripts plural).
-Thanks!
You can use a database view like a normal model.
In your case:
class Transcript < ActiveRecord::Base
set_table_name "qryTranscriptByGroup"
set_primary_key "if_not_id"
end
The query will be then:
Trascript.find_by_group('test_group')
without you need to declare anything.
Rails uses the method_missing method to magically generate find_by_column_name methods.
For the create/update/delete action you can simply delete them or not create them in the controller.
Let's say I'm within a rails project in some model named Apple.
I'm going to switch databases to another server so I'll call: ActiveRecord::Base.establish_connection()
I want to access the table 'Banana' in this particular database. I can see that the table exists by calling table_exists?
However, I'm not sure how to access the table... I want to just be able to do something like Banana.find(:all)
How should I work this out?
Have you tried connection_ninja gem? Pretty handy for multi-db connections:
https://github.com/cherring/connection_ninja
I have a standard rails application, that uses a mysql database through Active Record, with data loaded through a separate parsing process from a rather large XML file.
This was all well and good, but now I need to load data from an Oracle database, rather than the XML file.
I have no control how the database looks, and only really need a fraction of the data it contains (maybe one or two columns out of a few tables). As such, what I really want to do is make a call to the database, get data back, and put the data in the appropriate locations in my existing, Rails friendly mysql database.
How would I go about doing this? I've heard* you can (on a model by model basis) specifiy different databases for Rails Models to use, but that sounds like they use them in their entirety, (that is, the database is Rails friendly). Can I make direct Oracle calls? Is there a process that makes this easier? Can Active Record itself handle this?
A toy example:
If I need to know color, price, and location for an Object, then normally I would parse a huge XML file to get this information. Now, with oracle, color, price, and location are all in different tables, indexed by some ID (there isn't actually an "Object" table). I want to pull all this information together into my Rails model.
Edit: Sounds like what I'd heard about was ActiveRecord's "establish_connection" method...and it does indeed seem to assume one model is mapped to one table in the target database, which isn't true in my case.
Edit Edit: Ah, looks like I might be wrong there. "establish_connection" might handle my situation just fine (just gotta get ORACLE working in the first place, and I'll know for sure... If anyone can help, the question is here)
You can create a connection to Oracle directly and then have ActiveRecord execute a raw SQL statement to query your tables (plural). Off the top of my head, something like this:
class OracleModel < ActiveRecord::Base
establish_connection(:oracle_development)
def self.get_objects
self.find_by_sql("SELECT...")
end
end
With this model you can do OracleModel.get_objects which will return a set of records whereby the columns specified in the SELECT SQL statement are attributes of each OracleModel. Obviously you can probably come up with a more meaningful model name than I have!
Create an entry named :oracle_development in your config/database.yml file with your Oracle database connection details.
This may not be exactly what you are looking for, but it seems to cover you situation pretty well: http://pullmonkey.com/2008/4/21/ruby-on-rails-multiple-database-connections/
It looks like you can make an arbitrarily-named database configuration in the the database.yml file, and then have certain models connect to it like so:
class SomeModel < ActiveRecord::Base
establish_connection :arbitrary_database
#other stuff for your model
end
So, the solution would be to make ActiveRecord models for just the tables you want data out of from this other database. Then, if you really want to get into some sql, use ActiveRecord::Base.connection.execute(sql). If you need it as a the actual active_record object, do SomeModel.find_by_sql(sql).
Hope this helps!
I don't have points enough to edit your question, but it sounds like what you really need is to have another "connection pool" available to the second DB -- I don't think Oracle itself will be a problem.
Then, you need to use these alternate connections to "simply" execute a custom query within the appropriate controller method.
If you only need to pull data from your Oracle database, and if you have any ability to add objects to a schema that can see the data you require . . . .
I would simplify things by creating a view on the Oracle table that projects the data you require in a nice friendly shape for ActiveRecord.
This would mean maintaining code to two layers of the application, but I think the gain in clarity on the client-side would outweigh the cost.
You could also use the CREATE OR REPLACE VIEW Object AS SELECT tab1., tab2. FROM tab1,tab2 syntax so the view returned every column in each table.
If you need to Insert or Update changes to your Rails model, then you need to read up on the restrictions for doing Updates through a view.
(Also, you may need to search on getting Oracle to work with Rails as you will potentially need to install the Oracle client software and additional Ruby modules).
Are you talking about an one-time data conversion or some permanent data exchange between your application and the Oracle database? I think you shouldn't involve Rails in. You could just make a SQL query to the Oracle database, extract the data, and then just insert it into the MySQL database.
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.