Image tagging with Rails 3.2 - ruby-on-rails-3

I have an image model and want to assign differnt, pre-choosen tags to the images. I don't want to use a plugin.
This is my image model
class Image < ActiveRecord::Base
attr_accessible :date, :description, :name, :size, :image, :tag
end
Ideally I want to set check boxes with the different tags while creating or editing the image. Let's say the tags are "blue", "red" and "yellow".
Do I have to create a new model for the different tags? At the moment I have in the new.html.haml file
%div.field
= f.label "Tag"
%br/
= f.select :tag, options_for_select(["blue", "red", "yellow"]), {:multiple => true}
But I can only choose one tag and I prefer to do it one checkbox for each tag (easier to correct the choice). Furthermore I'm not sure if I should create a new model with the different tags and create a relationship, e.g. images belongs_to :tag and tag has_many :images
Is there a simple solution for this?

Ok, as for many other problems the solution is Railscasts:
http://railscasts.com/episodes/167-more-on-virtual-attributes

Related

saving embeds_many in mongoid

I have a embeds_many association and when I save the parent document the children are not being saved.
class User
include Mongoid::Document
embeds_many :user_missions
attr_accessible :user_missions_attributes
accepts_nested_attributes_for :user_missions, allow_destroy: true
end
class UserMission
include Mongoid::Document
embedded_in :user, :inverse_of => :user_missions
has_one :mission, autosave: true
validates_presence_of :mission
attr_accessible :mission_title
def mission_title
mission.try(:title)
end
def mission_title=(title)
self.mission = Mission.find_or_create_by(:title => title) if title.present?
end
end
Here is the spec I'm failing:
it "should save mission to user_mission when created" do
user_mission = UserMission.new
user = create(:user)
user.user_missions << user_mission
user_mission.mission_title = "Created Title"
user.save!
#user_mission.save!
User.first.user_missions[0].mission.title.should == "Created Title"
end
I get:
undefined method `title' for nil:NilClass
When I comment in the line #user_mission.save! it works. The problem is I need this to work in a form and I thought Mongoid automatically saved embedded document's fields.
How do I get the parent User document to save the embedded UserMission's data?
Thanks
UPDATE
Here is the Mission model I forgot to add (wasn't sure if it was important):
class Mission
include Mongoid::Document
belongs_to :user_mission, :inverse_of => :mission
attr_accessible :title
field :title, type: String
validates_presence_of :title
field :lowercase_title
before_create :lower_title_case
field :description, type: String
private
def lower_title_case
self.lowercase_title = self.title.downcase
end
end
AFAIK, embedded documents can't have referenced relations. So the calls to embedded_in and has_one are contradictory.
Other than that, have you tried reordering the lines in your test so that the user gets created first, and then you create user_misison through the user?
user = create(:user)
user_mission = user.user_missions.new
user_mission.mission_title = "Created Title"
user.save!
It looks like what you're trying to do is similar to an SQL JOIN table. If this is what you're trying to achieve, it would be better to take advantage of Mongoid's awesome N-N referenced mapping (unless you need to store extra data in the 'join' collection). I'd do something like:
class User
include Mongoid::Document
has_and_belongs_to_many :missions
end
class Mission
include Mongoid::Document
has_and_belongs_to_many :users
end
If you want to create missions through users, then turn on autosave on the Mission side of the relation.
EDIT: After seeing your Mission model, I realize you're trying to directly reference UserMission (belongs_to :user_mission), an embedded document, directly from your Mission model. Like I said earlier, not doable. You need some relation between the top level docs, User and Mission. You could probably get things to work by changing that line to:
has_many :users
and then changing has_one :mission from the User model to:
belongs_to :mission
You won't be able to use autosave from the User side though.
EDIT: Corrected for proper way to show right way to do 1-n relation from users to missions.

trouble saving to intersect table following railscast

I'm trying to create nested attributes as outlined in this railscast http://railscasts.com/episodes/167-more-on-virtual-attributes?view=asciicast
In my example, I am trying to associate an activity to an image, so I have a structure of activity -> activity_image -> image.
When I save my activity, rails creates the activity and the image, but I'm not getting the intersecting table of activity_image being saved. Of course, I never actually tell rails to save this, but neither does the railscast I'm following.
Is there some way this is supposed to be defined in the models? What have I got wrong?
class Activity > ActiveRecord::Base
attr_accessible :title, :description, :activity_image_url
belongs_to :user
has_many :activity_images
has_many :image_urls, :through => :activity_images
#validates_presence_of :title, :description, :url
attr_accessor :activity_image_url
after_save :assign_image
private
def assign_image
if #activity_image_url
self.activity_image_url = ImageUrl.find_or_create_by_url(#activity_image_url)
end
end
end

Supplying data for a form re-population from a different model

so I have a tricky issue here I'm not sure how to solve.
I have a Provider model that has_many :educational_affiliations
EducationalAffiliation belongs_to :institution & belongs_to
:provider
I have about 9000 universities in my database, so I'm using the handy-dandy rails3-jquery-autocomplete gem to give me type-ahead support. That's all working great - on TOP of that I'm using cocoon to support the nesting of the :educational_affiliations form inside of the provider's edit form.
So here's where the issue comes — This works great for submitting new affiliation records, (I'm using some jquery to set the :institution_id on the :educational_affiliations_attributes object, works great)
BUT when I return to edit this later, of course the :institution_name isn't populated, because it's not part of the :educational_affiliations model, it's in the :institution model.
Here's what I just tried in my :educational_affiliations, which I assume is the right solution:
class EducationalAffiliation < ActiveRecord::Base
attr_accessible :degree, :graduation_year, :honors, :institution_name, :institution_id, :provider_id
belongs_to :institution
belongs_to :provider
# attr_accessor :institution_name
validates :institution_id, :graduation_year, :provider_id, :degree, presence: true
def institution_name
Institution.find(institution_id).name
end
end
(i had it working for saving using the attr_accessor, but I've commented it out for now)
So when I render the edit view with the above, I get a ActiveRecord::RecordNotFound: Couldn't find Institution without an ID error — but when I open a debugger on that statement, the model DOES seem to know the institution_id...so confused why it doesn't pick it up.
Am I just doing this in the worst way possible? I assume there's a dumb solution.. :)
Here's the partial that needs the name populated:
.nested-fields
= f.input :institution_name, url: autocomplete_institution_name_data_path, as: :autocomplete, :input_html => {:id_element => '#provider_educational_affiliations_institution_id'}
= f.input :institution_id, as: :hidden
= f.input :provider_id, as: :hidden, input_html: { value: current_provider.id }
= link_to_remove_association "Remove Degree", f
Instead of the virtual attribute method, try the following to define the attribute:
delegate :name, :name=, :to => :institute, :prefix => true

has_many association with conditions on ActiveRecord

I have this models: Store and Address.
The second model Address I'm using this with other models and has some custom fields inside for different models.
Yes like polymorphic but without the varchar field for Class, I'm using an integer. (optimization stuff)
now on my Store model the association in set like this:
class Store < ActiveRecord::Base
has_many :addresses, :foreign_key => "parent_id", :conditions => ['parent_kind = ?', 2]
accepts_nested_attributes_for :addresses
end
Now in my controller I do:
#store.addresses.build
And I can use f.fields_for :addresses... inside the form.
The problem comes out when I submit the form and the data is saved to the database.
The record in the stores table is saved, the record in addresses is saved with the parent_id of the store in place, but the parent_kind is in 0 which is the default value for that attribute on MySQL.
My Quick fix was this:
#store = Store.new(params[:store])
#store.addresses[0].parent_kind = 2
if #store.save
....
But I know there must be another way.
Anny suggestions?
Thanks.
Did you try using a hidden field in your nested form?
<%= f.fields_for :addresses, Address.new do |ff| %>
<%= ff.hidden_field :parent_kind, :value => 2 %>
...
EDIT
If you don't want to use it due to security concerns, here's another way that might help. You can try using an association callback like so:
class Store < ActiveRecord::Base
has_many :addresses, :foreign_key => "parent_id",
:conditions => ['parent_kind = ?', 2],
:before_add => Proc.new { |store,address| address.parent_kind = 2}
accepts_nested_attributes_for :addresses
end

Generate primary keys without creating the record

I want to be able to do something like
#foo = MyClass.new
5.times do
#foo.things.build
end
But my #foo needs to have a primary key for this to work, Soo what is the best way to generate primary keys without creating the object?
The purpose for this is to be able to use nested forms more easely
form_builder.fields_for :things do ...
I believe the OP is asking for how to initialize a view action property for use in new action for a standard Rails resource. At this point, there is no ID for the main parent. The solution is simple:
The model:
class ParentObject < ActiveRecord::Base
# the child model in this example is called child_objects
has_many :child_objects, :dependent => :destroy
accepts_nested_attributes_for :child_objects
The controller action for new:
#object = Object.new :example_field => "my field"
#object.child_objects.build :name => "value_1" # pretending that name is a field
#object.child_objects.build :name => "value_2"
Then, in the view:
= form_for(#object) do |f| # top level Object
= f.label :example_field
= f.text_field :example_field
=# the next line loops twice in this example
= f.fields_for :child_objects do |child|
= child.label :name
= child.text_field :name
There is also a good gem called nested_form written by Ryan Bates (https://github.com/ryanb/nested_form) which may help you with the rest of the CRUD operations.
What you probable want is NestedAttributes
Nested attributes allow you to save attributes on associated records through the parent. By default nested attribute updating is turned off, you can enable it using the accepts_nested_attributes_for class method. When you enable nested attributes an attribute writer is defined on the model.
The implementation is different between each ORM, here is for sequel and ActiveRecord
NOTE: Full tutorial also available at Nerdgem
Sequel impementation
Imagine there is a Project class that has many tasks
class Project < Sequel::Model
one_to_many :tasks
end
class Task < Sequel::Model
many_to_one :project
end
To enable the nested attributes you will need include two plugins for the Project class
Sequel::Plugins::NestedAttributes: allows you to create, update, and delete associated objects directly by calling a method on the current object. Nested attributes are defined using the nested_attributes class method:
Sequel::Plugins::InstanceHooks: which is a dependency of NestedAttributes
You can find really good doc on the plugin site
Project.plugin :instance_hooks
Project.plugin :nested_attributes
After that is done you can call the nested_attributes method on the desired class
Project.nested_attributes :tasks
Now you can do this
p = Project.new(:title=>'Project')
p.tasks_attributes = [{:title=>'First Task'}, {:title=>'Second Task'}]
puts p.tasks.inspect
# It will output this
# [#<Task #values={:title=>"First Task"}>, #<Task #values={:title=>"Second Task"}>]
When you save the project it will save both the project and the tasks.
If you can even to edit many tasks at the same.
ActiveRecord implementation
Here is how to use it.
Imagine there is a Project class that has many tasks
Project.rb
class Project < ActiveRecord::Base
attr_accessible :title
has_many :tasks
accepts_nested_attributes_for :tasks
end
Task.rb
class Tasks < ActiveRecord::Base
attr_accessible :title, :project_id
belongs_to :project
end
Now you can do this.
p = Project.new
p.tasks_attributes=[{title: "First Task"}]
p.things
# Would output this
#=> [#<Thing id: nil, title: "First Task", created_at: nil, updated_at: nil, bar_id: nil>]
p.save
When you save the project it will save both the project and the tasks.
If you want to edit many project tasks at the same time you can to this
p.tasks_attributes=[{title: "First Task"},{title: "Second Task"}]
NOTE: there is also a Railscasts that can help you out with nested forms. Orginal Railscast, Revised Railscast