Say I have a system table 'categories' with 2 fixed records. The user will not be allowed to delete these 2 but may wish to add their own to extend their list. I need to be able to pull out the 'garden' category at certain times, e.g when creating a garden project.
A class attribute reader that returns the garden instance would do the job, but I would like to know how this can be improved with caching?
I believe memoization would only work per process which is almost pointless here. I would like it to be set once (perhaps the first time it's accessed or on app start-up) and just remain in cache for future use.
Example setup:
class Project < ActiveRecord::Base
belongs_to :category
end
class Category < SystemTable
cattr_reader :garden
def self.garden
##garden ||= self.find_by_name('garden')
end
end
How about the approach shown below, which will retrieve the garden instance once when the Category class is loaded:
class Category < SystemTable
GARDEN = self.find_by_name('garden')
end
Now whenever you need the garden category you can use Category::GARDEN.
Interlock, a plugin which works with memcached, will automatically cache any instances that are made by a straight 'find' using id. Similarly, you can bypass interlock and just cache the object in memcached manually. Class-level variables is a dirty and potentially bug-causing solution (and i'm not even sure if it works). There's plenty of info on installing and using memcached/memcache-client on the web.
Related
The default behaviour for Ruby on Rails is to save changes made to collection associations.
Is there any way to change this behaviour, so that I can modify the collections in memory without the changes being written to the database.
So if I have two classes:
class Project < ActiveRecord::Base
has_many :tasks
class Task < ActiveRecord::Base
belongs_to :project
and write some code like:
Project.tasks.clear
Project.tasks << task1
Project.tasks << task2
then it automatically deletes all tasks associated with the Project and automatically writes the changes to the db.
This is a contrived example of what I'm trying to achieve. I know I could use Project.tasks.build() to add a new task to the collection without it being saved automatically, but the the tasks that I'm adding are not new tasks.They are links to a limited set of tasks defined in db. You could think of them as entries in an enumeration of tasks. In addition Project.tasks.clear immediately hits the db.
In java world, using Hibernate, I would disconnect the entity from the session and be able to modify the entity in memory until reconnecting and saving.
Thanks
Have you tried using the task_ids attribute instead?
Change your code to:
Project.tasks_ids = []
Project.tasks_ids << task1.id
Project.tasks_ids << task2.id
I know this question is a bit old, but since I tried to search a similar problem on Google I thought this might be useful to other people.
I wondering is there a way to represent model differently (or probably control access on fields level) depending on it's (model instance) state and controller using it.
Example:
Imagine we have an Order model with product_id, count, price and status fields.
status could be one of: :new, :confirmed, :accepted, :cancelled, :delivered and :closed.
Application can access Order from, say, two controllers CustomerOrdersController and SellerOrdersController. So, CustomerOrdersController could create and edit orders. But able to change only count field. On the other hand SellerOrdersController could edit orders. But able to change only price field. I.e. it would be great if instance of Order class that CustomerOrdersController working with have no price= method. Same for count=(product=) and SellerOrderController.
Further more set of columns permitted to edit depends on status field (probably work for some state machine).
So, the question is: how would you do this in your app?
PS
I think about some ActiveModel proxy objects for ActiveRecord instances, but do not know actually will it work or not. Consider:
class CustomerOrderProxy < ActiveModel::Base end
class SellerOrderProxy < ActiveModel::Base end
class Order < ActiveRecord::Base
def wrap_proxy(controller_user)
controller_user == CustomerOrdersController ? CustomerOrderProxy(self) : SellerOrderProxy(self)
end
end
Another approach would be to do tons of checks and params validations inside controller actions, but I do not want to. I believe in "Fat model - skinny controller" :)
PPS
I know that ruby have plenty state machine plugins, but AFAI understand they define only transitions, not method set (i.e. representation) of the object.
This sounds like simple access control. Access is granted based on the authorized user, not which controller is being used. Take a look at the cancan gem for implementing clean, declarative access control for your AR objects.
Looks like I've found appropriate solution: in Ryan Bates's screencast Dynamic attr_accessible
Update:
In Rails 3.1 update_attributes(params[:order], role) could be used. Check out rails api. Though it cannot be used to change access control according to object's state.
my sql DB contains tables "jobs" and "job_categories."
"job_categories" associates job category strings (i.e. "Software Development") with an integer number (i.e. 7).
I need these associations saved into variables in my job controller for various query functions. How can I use rails to dynamically link changes to the job_categories table to variables in my jobs controller? I've worked with RoR for a few weeks now but am still a little fuzzy on how everything interacts. Thank you!
There's one big gotcha with what you're trying to do, but first I'll answer your question as asked.
Create class-level accessors in your JobsController, then write an Observer on the JobCategory class that makes the appropriate changes to the JobsController after save and destroy events.
class JobsController < ActionController::Base
##categories = JobCategory.find(:all)
cattr_accessor :categories
# ...
end
class JobCategoryObserver < ActiveRecord::Observer
def after_save(category)
JobsController.categories[category.name] = category.id
end
def after_destroy(category)
JobsController.categories.delete(category.name)
end
end
You'll need additional logic that removes the old name if you allow for name changes. The methods in ActiveRecord::Dirty will help with that.
So, the gotcha. The problem with an approach like this is that typically you have more than one process serving requests. You can make a change to the job_categories table, but that change only is updated in one process. The others are now stale.
Your job_categories table is likely to be small. If it's accessed with any frequency, it'll be cached in memory, either by the OS or the database server. If you query it enough, the results of that query may even be cached by the database. If you aren't querying it very often, then you shouldn't be bothering with trying to cache inside JobsController anyway.
If you absolutely must cache in memory, you're better off going with memcached. Then you get a single cache that all your Rails processes work against and no stale data.
I'm just getting rolling with RoR so I'm sure this is pretty basic. Let's say I have two models: Account and Transaction
class Account < ActiveRecord::Base
has_many :transactions
end
class Transaction < ActiveRecord::Base
belongs_to :account
end
What methods (for each model) become available/are auto generated after I make this association?
Thanks
It depends. Some methods (e.g. Account#transactions, Transaction#account) will be there from the get-go. Others will be created as needed (via a method_missing hook) such as dynamic finders. The exact list can depend on other factors, including things like acts_as, etc. used elsewhere.
Are you concerned about which ones are created or about what the full possibilities are?
-- MarkusQ
P.S. See here for more:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
run script/console from the root of your rails app and then you can explore all of the magic model methods like this:
> account = Account.new
> account.methods
> ...[long list of methods]...
> transaction = Transaction.new
> transaction.methods
> ...[long list of methods]...
You'll get a long list of all of the methods for the object including the generated ones.
All of the methods will be listed without line breaks - and with 100+ methods it can be hard to read.
You can use an .irbrc file in your home directory with some custom methods to format output in irb so it is easier to read.
going on inkdeep's answer : you can output something like this in your view so it's formatted:
#transaction.methods.each do |method|
method + ""
end
I'm using HAML so syntax is a bit different, but just look up "do"
So I'm working on a Rails app to get the feeling for the whole thing. I've got a Product model that's a standard ActiveRecord model. However, I also want to get some additional product info from Amazon ECS. So my complete model gets some of its info from the database and some from the web service. My question is, should I:
Make two models a Product and a ProductAWS, and then tie them together at the controller level.
Have the Product ActiveRecord model contain a ProductAWS object that does all the AWS stuff?
Just add all the AWS functionality to my Product model.
???
As with most things: it depends. Each of your ideas have merit. If it were me, I'd start out this way:
class Product < ActiveRecord::Base
has_one :aws_item
end
class AWSItem
belongs_to :product
end
The key questions you want to ask yourself are:
Are you only going to be offering AWS ECS items, or will you have other products? If you'll have products that have nothing to do with Amazon, don't care about ASIN, etc, then a has_one could be the way to go. Or, even better, a polymorphic relationship to a :vendable interface so you can later plug in different extension types.
Is it just behavior that is different, or is the data going to be largely different too? Because you might want to consider:
class Product < ActiveRecord::Base
end
class AWSItem < Product
def do_amazon_stuff
...
end
end
How do you want the system to perform when Amazon ECS isn't available? Should it throw exceptions? Or should you rely on a local cached version of the catalog?
class Product < ActiveRecord::Base
end
class ItemFetcher < BackgrounDRb::Rails
def do_work
# .... Make a cached copy of your ECS catalog here.
# Copy the Amazon stuff into your local model
end
end
Walk through these questions slowly and the answer will become clearer. If it doesn't, start prototyping it out. Good luck!
You can use the composed_of relationship in ActiveRecord. You make a regular class with all the attributes that you manage through AWS and specify that your Product-class is composed_of this class. ActiveRecord will handle the delegation of the mapped attributes to and from this class.
See the documentation of composed_of
#Menno
What about using ActiveResource for the AWS-attributes class?
If you are retrieving data from two completely different sources (ActiveRecord on one hand and the Internet on the other), there are many benefits to keeping these as separate models. As the above poster wrote, Product has_one (or has_many) :aws_item.