Rails, gem with functionality in between enum and many to many - ruby-on-rails-3

i have im my rails app model which has few options (no more than 10 i think).
Something like Product - Category, where product can be part of 1 or many categories.
But i think i have too few categories to engage fully fledged many-to-many construct.
Moreover the list of categroies is predefined and will almost never change.
I think from sql side this could look like string field categories with such content:"Fruits|Vegetables|..."
Maybe someone know preexisting gem for such functionality, or maybe it is no real advantage doing so and i should choose standart many-to-many ?
I checked acts-as-taggable-on plugin, but it is i think fits not very well for this task.
Enum gems like enumerize i think fit just best, but they are allow only single single value to be choosen.

Currently came out with following combination:
This gem:
https://github.com/pboling/flag_shih_tzu
In model:
class Product < ActiveRecord::Base
KINDS = { 1 => :fruit, 2 => :vegetable }
include FlagShihTzu
attr_accessible *KINDS.values
as_flags KINDS
Then in view (haml):
=form_for [#product] do |f|
-Product::KINDS.each do |k, v|
=f.check_box v
=f.label v
UPDATE:
Yet another gem adressing this problem: https://github.com/joelmoss/bitmask_attributes

Related

How can I query a rails 3 app efficiently?

I have a search form that queries one table in the database but there are many parameters (language, level, creator etc). The code below works provided the fields in question are filled in but I want to change it to:
a) add more parameters (there are several);
b) allow for a field to be empty
Here's the code in the controller:
#materials = Material.find(:all, :conditions => {:targ_lang => params["targ_lang"],
:inst_lang => params["inst_lang"],
:level => params["level"]})
Totally new to this I'm afraid but a lot of the documentation suggests I should be using "where".
Since Rails 3 you can use the where() function:
#materials = Material.where(targ_lang: params["targ_lang"], inst_lang: params["inst_lang"], level: params["level"])
Also, you could take a look at scopes
These allow you to set what you want to do in the model and call it in the controller for example:
class Material < ActiveRecord::Base
scope :active, where(active_state: true)
end
Then in the controller you do something like:
#active_materials = Material.active
This can be useful if you are joining several models and want to keep your controllers less messy.
To conclude, like #RVG said, seachlogic is quite useful as well as, there are others like Sphinx and Elastic Search. You should take a quick look at these and use the one you feel most confortable with.
If you are using search functionality in your app I suggest using SearchLogic gem
It is easy to use and effective..
SearchLogic
RailsCasts for searchlogic

Migrating from Rails 2.3.11 to Rails 3.0.11: Now unable to use find with a collection of AR records

I'm In the process of migrating from Rails 2.3.11 to Rails 3.1.3 and I am now on Rails 3.0.11 and sorting out all the issues that this brings.
The first one I can't solve is: in Rails 2.3.11, I could do the following and get the required records back
#event_type_time_units = TimeUnit.find(#event.event_type.time_units)
In Rails 3.0.11, I've tried using
#event_type_time_units = TimeUnit.find_with_ids(#event.event_type.time_units)
and
#event_type_time_units = TimeUnit.find_some(#event.event_type.time_units)
The code for both of those doesn't do anything magical and I expected them not to work.
Does anyone have a pointer for me, please.
Thank you
edit: the error I get is TypeError in MeetingsController#create
Cannot visit TimeUnit
Rails 3 uses Arel aka relational algebra to fetch associations. Assuming your EventType model has an association to has_many :time_units, you can just do the following:
#event_type_time_units = #event.event_type.time_units
Furthermore, you can optimize your queries using EventType as a join model (ish):
# app/models/event.rb
belongs_to :event_type
has_many :time_units, :through => :event_type
# app/models/event_type.rb
has_many :events
has_many :time_units
Now, you can query directly, saving a SQL call:
#event_time_units = #event.time_units
In short, there's no reason to do a find on an association. The association returns an "Array" of the records. (I use "Array" in quotes, because it's not really an array, but an ActiveRecord::Association which behaves much like an array)
Aside
I highly recommend just migrating to Rails 3.1.3. It's just as difficult to migrate from Rails 2 -> 3 as 3 => 3.1. Save yourself the middle headache. In fact, given the legacy of your application, I recommend the following:
Create a new, empty Rails 3.1.3 application
Copy all your models, views, controllers, and libs to this new application
Search your existing projects for gems -> add them to your Gemfile
Review everything in config/*. This is where a LOT of changes have take place. If you have application-specific code in your existing application, port it over to the new one. Otherwise, leave it alone.

Rails: Find by related model attribute?

So, I tried to search for an example of how to do this, and I'm not sure I even know how to describe what I'm trying to do. I'm an utter noob when it comes to SQL, and I'm sure this is really basic but I'm totally lost:
I have a model, Photo, which has_many :tags, :through => :taggings. Tags have a name and an id.
I want to do something like: Photo.where( #tag_name in [array] )
... but like I said I have no idea how to write something like that, or what to search to see an example on Google.
Can anyone give me an example of that kind of query, and what it might be called?
Thanks!
Just tried this on a similar model of my own and seemed to work fine:
Photo.joins(:tags).where('tags.name' => ['herp','derp']).group(:id)
Also, here's a great resource on the AREL querying interface rails 3 uses, with information on these and other ActiveRecord calls related to querying.
So, as a twist on Brett's method, it turns out the following works without breaking PostgreSQL:
def self.tagged_with( string )
array = string.split(',').map{ |s| s.lstrip }
select('distinct photos.*').joins(:tags).where('tags.name' => array )
end
Problem solved! See this article for a great explanation as to why this is a better idea than 'group'.

Do Rails 3 Active Record dynamic find_or_create finder methods have some undocumented inconsistencies?

Apologies for the long title, but this is bothering me. I'm new to Rails, so this is my first project. Rails 3.0.3.
In my model, a User may or may not have read many Entries; this is tracked in a model called ReadEntries. This many-to-one relationship is properly defined in the code, I think.
User.rb:
has_many :read_entries
Entry.rb:
has_many :read_entries
ReadEntry.rb:
belongs_to :entry
belongs_to :user
This table has to be populated at some point. If I try to do this:
user.read_entries.find_or_create_by_entry_id(entry.id, :read => false)
I get the error Unknown key(s): read. Leave out trying to set :read, and it works.
However, if I create the same row with this, it works:
ReadEntry.find_or_create_by_entry_id_and_user_id(entry.id, user.id, :read => false)
Logically, these methods should be identical, right? Thanks.
I've also had weird experiences with find_or_create. I would love it if it worked, but it seems inconsistent.
I'm currently having the same issue as you, and I think it may be due to calling find_or_create on an association as opposed to the model directly. Here's my example:
permission_assignments.find_or_create_by_role_id(:role_id => role_id, :is_allowed => false)
This works to create the assignment, except the "is_allowed" field gets set to it's default of "true". This code works for me (in the Permission model, hence the self reference)
PermissionAssignment.find_or_create_by_permission_id_and_role_id(:permission_id => self.id, :role_id => role_id, :is_allowed => false)
It's more verbose, unfortunately, but it works. The only problem that I still notice is that the object that is returned has no id assigned (the record does get created in the database, however, but if I wanted to update any more attributes I wouldn't be able to without the id). Don't know if that's a separate issue or not.
Rails 3.0.4 here with Postgres 8.4
You cannot pass in other fields like that as Rails will assume they are options for the find. Instead, you will need to make your method call longer:
user.read_entries.find_or_create_by_entry_id_and_read(entry.id, false)
Or alternatively use a shorter, custom syntax for that.
For your final example, my thoughts are that Rails will take the second argument and use that as options. Other than that, I am not sure.

Constructing a has-and-belongs-to-many query

I have a rails app (running on version 2.2.2) that has a model called Product. Product is in a has-and-belongs-to-many relationship with Feature. The problem is that I need have search functionality for the products. So I need to be able to search for products that have a similar name, and some other attributes. The tricky part is that the search must also return products that have the exact set of features indicated in the search form (this is represented by a bunch of checkboxes). The following code works, but it strikes me as rather inefficient:
#products = Product.find(:all, :conditions=>["home=? AND name LIKE ? AND made_by LIKE ? AND supplier LIKE ? AND ins LIKE ?",hme,'%'+opts[0]+'%','%'+opts[1]+'%','%'+opts[3]+'%','%'+opts[4]+'%'])
#see if any of these products have the correct features
if !params[:feature_ids].nil?
f = params[:feature_ids].collect{|i| i.to_i}
#products.delete_if {|x| x.feature_ids!=f}
end
I'm sorry that my grasp of rails/sql is so weak, but does anyone have any suggestions about how to improve the above code? Thanks so much!
First, i would recommend you to manually write a FeatureProduct model (and not use the default 'has_and_belongs_to_many')
EG
class FeatureProduct
belongs_to :feature
belongs_to :product
end
class Product
has_many :feature_products
has_many :features, :through => :feature_products
end
class Feature
has_many :feature_products
has_many :products, :through => :feature_products
end
For the search: You may find the gem SearchLogic to be exactly what you need. It has support for 'LIKE' conditions (it means that you can write in a more 'Rails way' your query). It also has support for performing a search with conditions on a related model (on your Feature model, to be more precise).
The solution would be something like:
search = Product.search
search.name_like = opt[0]
search.made_by_like = opt[1]
...
search.feature_products_id_equals = your_feature_ids
..
#product_list = search.all
There is also an excellent screencast explaining the use of this gem.
Good luck :)