Mongoid Versioning Scopes - ruby-on-rails

Mongoid has built-in versioning when you mix-in the Mongoid::Versioning module. It works really well for me but I'm inelegantly working with versions on a model. Let me give you an example. Assume I'm building a blog app (I'm not).
My model is Post. Let's say I wanted to find all previous published versions of a single post. The following works:
post = Post.first # just grab something
published_posts = post.versions.find_all{ |v| v.published == true }
Etc. Then I could do something with published_posts or whatever. I'd love to make a named scope for this so I'm not putting the find_all block in my view but I can't figure out how to override the built-in functionality of the Version class.
I've tried various scopes in my Post class. For example:
# I can't get this and other variations on this to work
scope :versions_published,
:where => 'self.versions.find_all{ |v| v.published == true }'
I also tried monkey patching the Version class but it doesn't like it very much. I have a workaround, I was just hoping to learn more about the built-in Versioning Mongoid provides, especially how to extend the Version class.

The scope you've defined is very, very wrong for two fundamental reasons:
You can't pass a string to the :where argument. You need to pass in a hash
scopes are defined on the class (Post in this case), so you cannot reference self.versions since self refers to the Post class and not to any specific post
The solution is to use the matches helper in Mongoid (which is a shortcut to Mongo's $elemMatch):
class Post
#...
scope :versions_published, where(:versions.matches => {:published => true})
#...
end
I would not recommend monkeypatching the Version class unless you really know what you are doing.

Related

How does one alias a method that does not yet exist? (Ruby Decorator)

This is going to sound pretty crazy, but I'm trying to build a generic decoration-based system which will allow a decorated class to do all kinds of crazy stuff with attributes. The goal is to be able to define attributes at a high level, decorate an ORM class (ActiveRecord, for example, though our primary case is actually quite a bit different), and use those decorations in various places in the app to automate some dynamic "magic' our app needs. For instance, we'll use the attributes to automatically generate forms and views, translate complex form hashes into flatter structures, etc.
To accommodate both use cases we've identified so far, I have a mixable module and a decorator (using Draper so Rails form magic still works, though I'm not married to Draper necessarily) which look more or less like this (obviously lots of details are omitted):
class DecoratorThing < Draper::Decorator
include CoreMixinStuff
delegate_all
end
module CoreMixinStuff
extend ActiveSupport::Concern
module ClassMethods
def attribute(stuff, blah)
attribute = AttributeDefinition.new(...)
add_translation_methods(attribute)
...
end
def add_translation_methods(attribute)
name = attribute.name
reader = name
writer = "#{name}="
# In the case of field wrappers, we have to alias the original reader and writer so we
# don't overwrite them completely
if attribute.translation_type == :wrapper
alias_method :"_orig_#{reader}", reader
alias_method :"_orig_#{writer}", writer
# Otherwise, we need to error if the reader or writer would collide
elsif instance_methods.include?(reader) || instance_methods.include?(writer)
raise RuntimeError.new("Cannot define an attribute which overrides existing methods (#{name.inspect})")
end
end
end
end
Then the actual decorator for a specific instance does things like this:
class FooDecorator < DecoratorThing
decorates Foo
attribute :field, multiple: true, serialize: true
attribute :field2, field: :delegation_field
attribute :field3 do |field|
field.subtype ...
field.subtype ...
end
end
The intent there would be to allow Foo#field to take an array and serialize it internally into a string before sending it off to wherever the decorated object takes it. Foo#field2 would just pass data as-is to delegation_field. Foo#field3 would take a complex hash of data and delegate it to the subtype fields.
The latter two cases are painful, but I have them working in a prototype. The first is the problem because of the alias_method stuff above - since the attribute method is run on the decorator, the method I'm trying to alias doesn't actually exist yet. It's not until FooDecorator.new(some_foo_instance) is called that those other instance methods are available.
I think my options are limited to the following, but I'm hoping there's some better choice:
Give up on decoration and just accept that this whole thing has to be a mixin instead
Give up on the mixin, requiring decoration instead, and go through the decorated object rather than aliasing the methods
Give up on field wrappers and require attributes to always have a unique name and delegate to a field (in the example above, attribute :field... would become attribute :field_wrapper, multiple: true, serialize: true, field: "field")
Give up on serializing data, making it the user's responsibility to properly define / override methods to handle the data they define
The fourth option is probably the sanest, but I've made a lot of assumptions around being able to serialize, so if somebody else knows a nice way to make this happen, that'd be super swell.
I think I have an answer that fits this question.
Basically, my solution adds a proxy column over the top of an existing column associated with an active-record model.
This proxy column overrides the original column's accessor methods. The basic proxy functionality doesn't do much beside set and get the value of original columns.
The actual code is quite long so I've created a gist for it. Check it out here.
The proxy column object can be subclassed/overridden to provide any sort of functionality required. For example, in your code AttributeDefinition would subclass ProxyColumn. The AttributeDefinition would act as a proxy between the different attributes and the values. It could wrap/unwrap the attributes value in a DecoratorThing based on whatever conditions that you deem appropriate.
One thing it doesn't do, at this stage, is provide a way of overriding how/what methods are added. However, all that could easily be changed based on your needs. I actually based that part off your add_translation_methods method anyways.
This is the basic usage to define the columns:
class MyModel
# Add the concern to get the functionality
include DataCollectionElements # concern
# create proxy column and set various options
proxy_column :my_col_1
proxy_column :my_col_2, :alias => [:col1, col2, col3]
proxy_column :my_col_3, :column => :actual_col_name
# define the proxy object using class/string/symbol
proxy_column :my_col_4, :proxy => MyProxy # or 'MyProxy' or :my_proxy
# define the proxy object using a proc/lambda
proxy_column :my_col_5, :proxy => ->(model, code, options) { MyProxy.new(model, code, options) }
proxy_column :my_col_6, :proxy => proc {|model, code, options| MyProxy.new(model, code, options) }
# define the proxy object using a block
proxy_column :my_col_7 do |model, code, options|
MyProxy.new(model, code, options)
end
end

What is the real benefit of scopes

I've looked at over 10 pages trying to find the benefit of a scope over any other ActiveRecord class method that returns an ActiveRecord::Relation.
In the following for example why are scopes better than the alternative below it which do the same thing:
#scope :pat1, lambda {{:conditions => ["name like ?", 'J%']}}
#scope :pat2, lambda {{:conditions => ["id > 5"]}}
def self.pat1
where("name like ?", 'J%')
end
def self.pat2
where("id > 5")
end
def patx
self.class.pat1.pat2.first
end
The documentation over and over again says that scopes are beneficial because they can be chained...
"All scope methods will return an ActiveRecord::Relation object which will allow for further methods (such as other scopes) to be called on it."
-guides.rubyonrails.org
"The main reason scopes are better than plain class methods is that they can be chained with other methods"
http://ruby.railstutorial.org
...but the alternative above can also be chained producing the same results.
Just trying to figure out if there's an emperor's new clothes thing going on here. Even from a syntactic standpoint there appears to be no benefit. Are they faster- some sources vaguely suggest that.
ActiveRecord scopes are really just syntax sugar wrapped in a best practice, as noted already.
In the 2.x days of Rails, when they were called "named_scope", they mattered a bit more. They allowed easy chaining of conditions for generating a query. With the improvements in Rails 3.x with Arel, it is simple to create functions for query relations, as you noted. Scopes just provide a simple and elegant solutions for chainable, predefined queries. Having all the scopes at the top of a model improves the readability and helps shows how the model is used.
When you write a scope, it is essentially doing the same thing. Here is what the Rails source looks like:
def scope(name, scope_options = {})
name = name.to_sym
valid_scope_name?(name)
extension = Module.new(&Proc.new) if block_given?
scope_proc = lambda do |*args|
options = scope_options.respond_to?(:call) ? unscoped { scope_options.call(*args) } : scope_options
options = scoped.apply_finder_options(options) if options.is_a?(Hash)
relation = scoped.merge(options)
extension ? relation.extending(extension) : relation
end
singleton_class.send(:redefine_method, name, &scope_proc)
end
The benefits to scopes in this case are that they are the idiomatic way of defining queries, in some cases fewer lines of code, and you can do extensions.
The example in the source looks like this:
scope :red, where(:color => 'red') do
def dom_id
'red_shirts'
end
end
Which allows you to call Model.red.dom_id.
Yes, they are syntactic short-cuts that basically represent the methods have you have found.
Why better?
The most immediate effect is that 2 lines code is way easier to read and maintain than 9 lines of code.
Rails always seeks a DRY approach and here the repeated def self.method end's are obscuring the actual code.
There are a few very interesting differences between scopes and class methods that return relations.
It is easier to deal with nil parameters for scopes with a simple param.present? check, for class methods you must explicitly return a non-nil relation if a param would cause a nil relation.
Scopes are more easily extensible than class methods. Simply pass a block (for instance to deal with pagination) to add the methods. Class methods can be extended but not as elegantly.
For the full rundown see this post from Plataformatec.

Rails 3 translations within models in production

I'm trying to translate an app into Japanese and everything was going smoothly until I put it into production.
As cache_classes is now true any translation within a model reverts to the default locale.
I know I'm probably supposed to target the translations directly in the yml file but I'm not sure how I would do that for the following simplified code:
class TimeseriesForecast < ActiveRecord::Base
##field_names = {
:location_name => I18n.t('forecast_timeseries.location_name'),
:local_date_time => I18n.t('forecast_timeseries.local_date_time'),
:zulu_date_time => I18n.t('forecast_timeseries.zulu_date_time'),
:temp_mean => I18n.t('forecast_timeseries.temp_mean')
}
end
Many thanks
Your I18n.t() call is evaluated at compile time since you are defining class variables, not instance variables. You need to put your call to I18n.t where they will be evaluated at runtime.
But if you want to translate ActiveRecord field names, use human_attribute_name and provide your translations via YML. You do not need to manually provide translations, Rails handles it all for you automatically.
The respective documentation is at http://guides.rubyonrails.org/i18n.html Chapter 5.1.
Don't use I18n.t or translate method in your models. You can do this instead:
In your model
Use something like this to add internationalized errors to the name attribute of your model (Check documentation: ActiveModel/Errors/method-i-add):
self.errors.add(:name, :your_error_key)
# The error key could be something like :wrong_name
NOTE: Sometimes you won't even need to add errors with errors.add method. For example if you add validations in your model with somethind like this:
validates :name, presence: true
Rails will add an error with the key :blank (the key depens on the validation type). In other words rails internally will issue self.errors.add(:name, :blank)
In your locale
Then in your locale.jp.yml can use any of this (just one):
activerecord.errors.models.[model_name].attributes.[attribute_name]
activerecord.errors.models.[model_name]
activerecord.errors.messages
errors.attributes.[attribute_name]
errors.messages
In your case replace [model_name] with timeseries_forecast and [attribute_name] with your_error_key
For example:
en:
errors:
messages:
your_error_key: "Your error message in english"
Don't think you're improving performance by caching the names in the class. Make it a method instead.
class TimeseriesForecast < ActiveRecord::Base
def self.field_names
{ :location_name => I18n.t('forecast_timeseries.location_name'),
:local_date_time => I18n.t('forecast_timeseries.local_date_time'),
:zulu_date_time => I18n.t('forecast_timeseries.zulu_date_time'),
:temp_mean => I18n.t('forecast_timeseries.temp_mean') }
end
end
# usage
TimeseriesForecast.field_names
Better yet, return just the actual fields and do the translation in the view, if you're gonna be strict MVC about it (some Rails methods - like collection_select - make it harder to do that though, hence the suggestion above).

Rails gem rails3-jquery-autocomplete how to scope by user

I'm using the Rails gem rails3-jquery-autocomplete to add categories to posts.
I would like to restrict the search to include only categories that belong to the current user or post's author in the results.
The documentation says that I can specify a scope:
:scopes
Added option to use scopes. Pass scopes in an array. e.g :scopes =>
[:scope1, :scope2]
But I'm not sure how I would pass the user id here?
It seems like a comon scenario, am I missing something obvious?
I found an answer that suggests modifying the get_item method, but that seems to break the auto-complete
Scoping the results for rails3 jquery autocomplete plugin
In posts_controller:
def get_autocomplete_items(parameters)
items = super(parameters)
items = items.where(:user_id => current_user.id)
end
I'm first calling the original get_autocomplete_items method, and then filtering out the results by current_user.id.
This question helped:
Rails 3: alias_method_chain still used?
I had a similar problem I solved thanks to the answers above.
My autocomplete also worked against a User model, but I needed to restrict the results to the user's institution (Institution has many :users). My controller creates an #institution instance variable that is accessed in the view.
Although the get_autocomplete_items method cannot directly access the instance variable, I found that the data CAN be passed to autocomplete as a parameter (note: I use the simple_forms gem, so the input call looks a little different than the standard rails syntax).
In my view:
<%= f.input :email, :url => autocomplete_user_email_institutions_path(:institution_id=>#institution.id.to_s), :as => :autocomplete %>
In my controller:
autocomplete :user, :email, :extra_data => [:first_name, :last_name]
def get_autocomplete_items(parameters)
super(parameters).where(:institution_id => params[:institution_id])
end
My autocomplete list is now scoped to just the users who work for a particular institution.
deb's answer works for me.
The code can be cleaned up a bit:
def get_autocomplete_items(parameters)
super(parameters).where(:user_id => current_user.id)
end
There is small update to code for those who have having trouble with super method.because of dynamic dispatch it above code need to replaced as below:
def get_autocomplete_items(parameters)
items = super(parameters)
items = items.where(searchable: true)
end
to this:
def get_autocomplete_items(parameters)
items = active_record_get_autocomplete_items(parameters)
items = items.where(searchable: true)
end
Reference: https://github.com/crowdint/rails3-jquery-autocomplete/issues/278
To answer the question posed by #ctilley79, multiple autocompletes is not a problem because, in addition to the possibility of passing more values in the params hash, you also have access to the autocomplete parameters. On my form (as an example), I have both a City and a Zip autocomplete. I need to restrict the City to those in a certain state. So my controller action looks like this:
def get_autocomplete_items(parameters)
if (parameters[:model] == City)
super(parameters).where("state_id" => params[:state_id])
else
super(parameters)
end
end
You also have access to the method in case you need it. Do logger.debug on the parameters to see all that is available.
I know the gem and the question are old but I found myself using this gem and needing this answer recently... None of the old answers will work anymore because in the source code, the method get_autocomplete_items is generated dynamically and has the ORM prepended on the method name. This is what got it working for me. I assume most folks are using ActiveRecord too but check the autocomplete.rb method 'get_prefix' to figure out what you should prepend to the method name to get it working.
Hope this saves someone a bunch of time. Be the change you want to see and all that ;)
def active_record_get_autocomplete_items(parameters)
super(parameters).where(id: current_user.id)
end
I faced a similar problem. Our site is multi-tenant, so everything needs to be scoped to the tenant.
To make this easier, I modified rails3-jquery-autocomplete to accept another option called :base_scope. It takes a string, that gets eval'd instead of using the model. All the other functionality works, so you can append additional scopes and where clauses if you need to.
My fork is here: https://github.com/GiveCorps/rails3-jquery-autocomplete
I am not sure that the tests i wrote prove it will always work. I just checked that it was using the scope instead of the model in the items method.
i would appreciate any thoughts on it. Not sure whether it merits a pull request.

What is Rails ActiveRecord find(x) method that is equivalent to a lazy-evaluated-scope? How to refactor ActiveRecord finder?

Is Rails' find(x) method on a model lazy? If not, what is the equivalent?
I am new to Rails, so I found myself writing scopes like this:
class Course < ActiveRecord::Base
scope :by_instructor_id, lambda { |instructor_id| where(:instructor_id => instructor_id) }
scope :by_course_template_id, lambda { |course_template_id| where(:course_template_id => course_template_id ) }
scope :by_company_id, lambda { |company_id| joins(:instructor).merge(CompanyUser.by_company_id(company_id)) }
end
It's not a lot of work, but now I'm asking myself... if Rails provided these with a scope, I wouldn't have to write them.
So, does Rails offer them? Can I do something like the below code and only make it do 1 database call?
Company.find(params[:id]).instructors.courses
instead of
Course.by_company_id(params[:id])
Which is correct? I know Course.by_company_id(params[:id]) is only 1 database call. It is very familiar to writing SQL or queries in Hibernate. But if you can write it the other way, maybe one should?
However, I don't want to write Company.find(params[:id]).instructors.courses if it results in more than 1 database call. I can see the advantage though because it means never having to write the 3 scopes I showed you above, but I am worried that Company.find(x) is not lazy. Is it?
Try using #scoped method on a model before calling #find:
user = User.scoped.instructors.courses.find(params[:id])
To make find by id query lazy you can add new method to your controller and then add this method as helper.
# company
def company
#company ||= Company.find(params[:id])
end
helper :company
#view
<%= company.name %>
To get more information you can check great RailsCast - Decent Exposure

Resources