belongs_to a specific version - activerecord

I need to store the specific version of a model with an order. I'm planning to use a versioning gem like paper_trail or vestal_versions. I'd like the correct version automatically loaded with the order.
Ideally, I'd simply store the object with order.update_attributes(:stuff => bought_stuff) and the order would remember the version of the stuff so that subsequent loads would make order.reload.stuff still be the object as it was when the order was saved.
Is there a gem that would provide such a functionality? I couldn't find one.
Otherwise, how can I achieve that with ActiveRecord and a versioning gem?

Actually, I could achieve almost what I want with PaperTrail and this :
class Stuff < ActiveRecord::Base
has_paper_trail
end
class Order < ActiveRecord::Base
belongs_to :stuff
def stuff_with_version
stuff_without_version.version_at(created_at) if stuff_without_version
end
alias_method_chain :stuff, :version
end

Not sure this is necessarily the best design for you, but you could use paper_trail for this. Simply add the macro method 'has_paper_trail' at the top of your model class and any time an instance changes, a serialised copy of it is created in a table called "versions" along with a polymorphic relationship back to the actual model.
Supposing you want to relate a particular version of a 'product' to an order, start by adding a relationship to the versions table - i.e. a migration that adds a 'version_id' to your order, and then set up the relationship as follows:
class Order
belongs_to :version
def product
version
end
def product=(p)
version=p.versions.last
end
end
class Product
has_paper_trail
end
Using this, when you add a product to an order, it will relate the order to the latest version of the product instead. When you retrieve the product, it will pull out the version; i.e. the product as it was when you created the order. Getting the relationship to work the other way around (i.e. relating products back to orders) might be more complicated, but this is a start.

Related

Adding ActiveRecord validations to PaperTrail's Version model?

I'm trying to add a validation to PaperTrail::Version which will prevent sensitive data from being stored in the versions table. The idea being you'll get lots of obvious errors if you forget to sanitize your has_paper_trail call within your model.
If I add a custom validator in config/initializers/paper_trail it works ... for a while. Then PaperTrail starts acting with its default behavior and my methods are undefined.
Example Code:
PaperTrail::Rails::Engine.eager_load!
module PaperTrail
class Version
# Ensure no sensitive values end up in the versions table
validate :prohibited_attributes
...
Try a custom version class. See documentation section 6.a. Custom Version Classes.
6.a. Custom Version Classes
You can specify custom version subclasses with the :class_name
option:
class PostVersion < PaperTrail::Version
# custom behaviour, e.g:
self.table_name = :post_versions
end
class Post < ActiveRecord::Base
has_paper_trail :class_name => 'PostVersion'
end
Using PaperTrail::Rails::Engine.eager_load! was a good idea. Not sure why that didn't work for you. Hopefully this is a workaround.

Multiple controllers, two "depths" of relationships

I'm writing my first app in Ruby on Rails (I've only went through railstutorial.org before ) which is simple Electronic Prescription Service and I run on one thing I can't cope with.
I want to create form for adding new prescription as well as adding some medicines which belong to this newly created prescription.
First problem is - how can I actually add medicines to the prescription when during filling the form prescription doesn't exist yet? What I did before was I first create prescription with #user.prescription.create(presc_attributes) and later #prescription.relations.create(medicine_id).
Now I need to do this probably on one form sending the whole thing using one button ( one HTML request, am I right? ) unless you guys have got better idea. I'm also struggling with an idea where to actually put method creating this prescription. Should it be in PrescriptionController#new or maybe connected to RelationsController#new as well?
I've read couple of articles about nested fields_for but they don't seem to match exactly what I need.
Im really new to RoR so sorry if I missed something important.
Here is my EER as well
http://i.stack.imgur.com/sa9CB.png
UPDATE---
ahhh i see, I think what you want is a relationship with an inverse_of.
If you are using a belongs_to on the join model, it is a good idea to set the :inverse_of option on the belongs_to, which will mean that the following example works correctly (where tags is a has_many :through association):
#post = Post.first
#tag = #post.tags.build name: "ruby"
#tag.save
The last line ought to save the through record (a Taggable). This will only work if the :inverse_of is set:
class Taggable < ActiveRecord::Base
belongs_to :post
belongs_to :tag, inverse_of: :taggings
end
If you do not set the :inverse_of record, the association will do its best to match itself up with the correct inverse. Automatic inverse detection only works on has_many, has_one, and belongs_to associations.
FROM http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Understanding how ActiveRecord exists? works when building an association

I have two models that are associated via a has_many relationship. E.g.
class Newspaper < ActiveRecord::Base
has_many :articles
end
class Article < ActiveRecord::Base
belongs_to :newspaper
validates :uid, presence: true,
uniqueness: { case_sensitive: true }
end
A newspaper is updated several times a day but we only want to construct and add articles to the association that do not already exist. The following code was my first cut of achieving this.
new_articles.each do |article|
unless newspaper.articles.exists? uid: article.uid
newspaper.articles.build(uid: article.uid)
end
end
The newspaper object is either new and unsaved, or retrieved with existing relationships at this point.
My tests indicate that I am able to add two articles to the newspaper that have the same UID using the code above and this is obviously not want I want.
I appears to me that my current code will result in a validation failure upon being saved as the validation looks at uniqueness across the entire articles table and not the association.
What I'm struggling to understand is how the exists? method behaves in this scenario (and why it's not saving my bacon as I planned). I'm using FactoryGirl to build a newspaper, add an article and then simulate an update containing an article with the same uid as the article I've already added. If the code works I should get only one associated article but instead I get two. Using either build or create makes no difference, thus whether the article record is already present in the database does not appear to change the outcome.
Can anyone shed some light on how I can achieve the desired result or why the exists? method is not doing what I expect?
Thanks
The association exists? actually creates a scoped query, as per the association. This is why your existing articles filter doesn't work.
unless newspaper.articles.exists? uid: article.uid
# `articles.exists?` here will produce this if the newspaper is new
# ... WHERE "articles"."newspaper_id" IS NULL AND "articles.uid" = '<your uid>'
# and this, if the newspaper is persisted (with an id of 1)
# ... WHERE "articles"."newspaper_id" = 1 AND "articles.uid" = '<your uid>'
The case of the new newspaper is clearly wrong, as it would only return articles with a nil newspaper ID. But the persisted case is probably undesirable as well, as it still unnecessarily filters against newspaper ID, when you real concern here is that the UID is unique.
Rather, you probably want simply against Article, rather than scoping the exists? through the association, like:
unless Article.exists? uid: article.uid
Concerning your other problem:
this appears to be a FactoryGirl problem where the create method isn't creating db entries in the same way I can in the irb.
FactoryGirl.create should still abide by validations. It might help to see your test.

Avoid creating duplicate objects from subclasses of a given model in Rails

I have a model group.rb with subclasses organization.rb, company.rb, etc. I'm wondering if there is a way to create an organization with the name "Rails Beginners Society" without also creating a company with the name "Rails Beginners Society"? As it stands right now it looks like if I do something like Organization.find_or_create_by_name(:name => #profile.organization) I end up not only creating an organization, but also a company and all the other subclasses of group.rb with the name I supply in my Controller.
Any ideas/guidance would be much appreciated!
My Models look like this:
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
end
class Organization < Group
end
Etc...
It sounds like what you're trying to do is STI (Single Table Inheritance). Rails does support this.
class Group < ActiveRecord::Base
end
class Organization < Group
end
etc...
If you have a column type in your groups table, Rails should handle everything for you.

overwrite ActiveRecord

as all of us, I have some application. In database I store sensitive data and I want to overwrite ActiveRecord (or somewhere else?) to always add AND user_id = current_user statement to all SQL queries sent to database (I will add user_id column to all tables). I basically want to ensure, that all operations done by user, are performed ONLY on his data.
Regards,
Mateusz
Sounds like what you're after is a strategy for implementing multitenancy in your application.
You might want to checkout the multitenant gem which can help you to isolate your queries to data belonging to the current tenant.
Alternatively, you can enforce this at the database level using PostgreSQL schemas. Gay Naor has an excellent talk on implementing this strategy among others.
If you search on "multi-tenant rails apps" you'll find several other discussions on this problem.
There is no way to enforce it on application level, the only way to enforce it is to forbid to team to use Class.find/where/etc methods and allow to call them only on collections. So instead of Task.find you should always use current_user.tasks.find etc.
You can add AbstractModel with default scope and then inherit all other models from it:
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
default_scope where(:user_id => Thread.current[:current_user])
end
class SomeModel < AbstractModel
...
end
I've used Thread.current[:current_user] because of current_user is inaccessible inside of models. You should assign current_user to Thread.current in some before_filter method in ApplicationController etc.

Resources