I have the models User and StoredItem:
class UserData < ActiveRecord::Base
has_many :stored_items, :dependent => :destroy
end
class StoredItem < ActiveRecord::Base
belongs_to :user
named_scope :lookup, lambda { |id| { :conditions => ['qid = ?', id]}}
end
I need to have two methods to add and remove the items to StoredItem for current user. I put this code to User model:
class UserData < ActiveRecord::Base
has_many :stored_items, :dependent => :destroy
def save_item(params)
if(!self.stored_items.lookup(params[:qid]).exists?)
item = self.stored_items.new(:sid => params[:qid],
:name => params[:qti],
:url => params[:qur],
:group_id => params[:title],
:rating => Integer(params[:rating]))
item.save
end
end
def remove_item(qid)
item = self.stored_items.lookup(qid).first()
item.destroy
end
end
So here is the StoredItem controller:
def save_item
#user = UserData.find_by_login(session[:cuser])
#user.save_item(params)
# ...
end
Is it good architectural decision or it will be better to put this code to StoredItem model and pass the current user into it?
This is a good architectural decision. You need to keep it in the user since the User is the owner of the StoredItem. The user is responsible for its stored items, not the other way around.
Related
I am building an API for a webbapp using Ruby On Rails, i am using Jbuilder for API/JSON and Geokit.
I have this models:
Campaign
class Campaign < ActiveRecord::Base
belongs_to :company
belongs_to :venue
belongs_to :category
has_and_belongs_to_many :venues
has_many :cities, through: :venues
has_many :uses
end
Company
class Company < ActiveRecord::Base
has_many :venues
has_many :campaigns
validates :name, presence: true
end
Venue
class Venue < ActiveRecord::Base
acts_as_mappable :default_units => :kms,
:lat_column_name => :lat,
:lng_column_name => :lng
belongs_to :company
belongs_to :city
has_and_belongs_to_many :campaigns
end
I am trying to list all campaigns by given latitude and longitude coordinates, like this:
class API::CampaignsController < ApplicationController
respond_to :json
def index
#campaigns = filter
respond_with #campaigns
end
def filter
if params[:city_id]
Campaign.includes(:venues, :cities).where('cities.id' => params[:city_id])
elsif params[:lat] && params[:lng]
lat = params[:lat].to_f
lng = params[:lng].to_f
Campaign.includes(:venues, :cities).within(10, :origin => [lat, lng])
end
end
end
But all I get is a empty hash:
{
campaigns: [ ]
}
Any ides on what I am doing wrong and how I can fix this?
As per the server log, GET "/api/campaigns?lat=55.563466?lng=12.973509", you are not passing params[:city_id] so your BOTH conditions in filter method will fail and no query would be executed. Hence, empty campaigns.
acts_as_mappable is on Venue model so within is available only for that model. I think you want filter method as below:
def filter
if params[:city_id]
Campaign.includes(:venues, :cities).where('cities.id' => params[:city_id])
elsif params[:lat] && params[:lng]
lat = params[:lat].to_f
lng = params[:lng].to_f
Venue.within(10, :origin => [lat, lng]).includes(:campaigns, :city).each do |venue|
campaigns << venue.campaigns
end
campaigns
end
end
Also, the url should be /api/campaigns?lat=55.563466&lng=12.973509.
NOTICE:
Pass lng as a separate query param with & prefix as &lng and not as ?lng
So I have a User model, and a Group model which has several users thanks to the GroupUserAssociation model. Here's how my relationships are defined:
class Group < ActiveRecord::Base
has_many :group_users, :class_name => 'GroupUserAssociation', :foreign_key => :group_id
has_many :group_admins, :class_name => 'GroupUserAssociation', :foreign_key => :group_id, :conditions => ['level = 1']
has_many :group_not_admins, :class_name => 'GroupUserAssociation', :foreign_key => :group_id, :conditions => ['level = 0']
has_many :users, :through => :group_users, :source => :user
has_many :admins, :through => :group_admins, :source => :user
has_many :not_admins, :through => :group_not_admins, :source => :user
end
If I want to add/remove users to group, there is an elegant way to write it (elegant because it doesn't involves the GroupUserAssociation object):
Group.first.users << User.first # Adds to group
Group.first.users.delete(User.first) # Removed from group
But if I do
Group.first.admins << User.first
Group.first.admins.delete(User.first)
it also deletes the association (hence has the same effect as the first lines).
Is there an elegant way (without handling the GroupUserAssociation object to promote/demote admin (= to update GroupUserAssociation.level from 1 to 0) ?
I could do
Group.first.users.delete(User.first) # Removed from group
Group.first.admins << User.first
But that would mean 2 times commiting to DB which is not really good...
I read there are some nice things for this in Rails 4, but unfortunately I'm using Rails 3.2...
Thanks
We do this using this code:
#config/routes.rb
resources :entries do
post :category
delete ":category_id", to: :category, as: "remove_category"
end
#Categories
def category
entry = #entry = Entry.find(params[:entry_id])
category = #category = Category.find(params[:category_id])
#Actions
entry.categories << category if request.post? && !entry.categories.include?(category)
entry.categories.delete(category) if request.delete?
#Return
respond_to do |format|
format.html { redirect_to collection_path }
format.js
end
end
I'm working on a project where there are tasks that make up a scavenger hunt. When a user creates a new hunt, I'd like the hunts/show.html.erb file to show the hunt as well as the tasks associated with that hunt. But the models are giving me trouble. I've got the hunt model setup to that it accepts nested attributes for the tasks model. So when the user creates a new hunt, she also creates three tasks automatically. I can get the new hunt to save, but I can't get those new tasks to save. Here are my models.
What's missing? Do I need an "attr accessible" statement in the HunTasks.rb file?
class Hunt < ActiveRecord::Base
has_many :hunt_tasks
has_many :tasks, :through => :hunt_tasks
accepts_nested_attributes_for :tasks, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class Task < ActiveRecord::Base
has_many :hunt_tasks
has_many :hunts, :through => :hunt_tasks
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class HuntTask < ActiveRecord::Base
belongs_to :hunt # the id for the association is in this table
belongs_to :task
end
Here's what my Hunt controller looks like:
class HuntsController < ApplicationController
def index
#title = "All Hunts"
#hunts = Hunt.paginate(:page => params[:page])
end
def show
#hunt = Hunt.find(params[:id])
#title = #hunt.name
#tasks = #hunt.tasks.paginate(:page => params[:page])
end
def new
if current_user?(nil) then
redirect_to signin_path
else
#hunt = Hunt.new
#title = "New Hunt"
3.times do
hunt = #hunt.tasks.build
end
end
end
def create
#hunt = Hunt.new(params[:hunt])
if #hunt.save
flash[:success] = "Hunt created!"
redirect_to hunts_path
else
#title = "New Hunt"
render 'new'
end
end
....
end
The major difference between your example and the railscast is that you are doing many-to-many instead of one to many (I think his was Survey had many Questions). Based on what you described, I wonder if the HuntTask model is necessary. Are the tasks for one hunt ever going to be resused in another hunt? Assuming they are, then looks like your answer is here:
Rails nested form with has_many :through, how to edit attributes of join model?
You'll have to modify your new action in the controller to do this:
hunt = #hunt.hunt_tasks.build.build_task
Then, you'll need to change your Hunt model to include:
accepts_nested_attributes_for :hunt_tasks
And modify your HuntTask model to include:
accepts_nested_attribues_for :hunt
I have a three models:
class Feed < ActiveRecord::Base
has_many :filters, :dependent => :destroy
has_many :keywords, :through => :filters, :uniq => true
end
class Filter < ActiveRecord::Base
belongs_to :feed
belongs_to :keyword
validates_uniqueness_of :keyword_id, :scope => :feed_id
end
class Keyword < ActiveRecord::Base
has_many :filters, :dependent => :destroy
has_many :feeds, :through => :filters
end
What I want is to have only unique entries in the database for keywords. For example, if two feeds both have a keyword 'hello', there should be two filters (one for each feed) both pointing to the same keyword.
What I am having trouble with is the controller code. Perhaps I am looking for too simple a solution, but I figure there must be an easy way to do this. This is what I have in my create action so far:
def create
#feed = Feed.find(params[:feed_id])
#keyword = #feed.keywords.create(params[:keyword])
redirect_to feed_keywords_path(#feed), notice: 'Keyword added successfully.'
end
With this controller code, the previous example would result in a duplicate keyword in the database, one for each feed/filter. Is there a straight-forward solution to this or do I need to do a check beforehand to see if there is already a keyword and in that case just create the filter?
Use a dynamic finder find_or_create_by :
def create
#feed = Feed.find(params[:feed_id])
#keyword = Keyword.find_or_create_by_keyword(params[:keyword]) # I assume here that you have a column 'keyword' in your 'keywords' table
#feed.keywords << #keyword unless #feed.keywords.all.include?(#keyword)
redirect_to feed_keywords_path(#feed), notice: 'Keyword added successfully.'
end
class Job < ActiveRecord::Base
has_many :employments, :dependent => :destroy
has_many :users, :through => :employments
class User < ActiveRecord::Base
has_many :employments
has_many :jobs, :through => :employments
class Employment < ActiveRecord::Base
belongs_to :job
belongs_to :user # Employment has an extra attribute of confirmed ( values are 1 or 0)
In my view i am trying to update the confirmed fied from 0 to 1 on user click.
<%= link_to "Confirm Job", :action => :confirmjob, :id => job.id %>
In my job Controller I have
def confirmjob
#job = Job.find(params[:id])
#job.employments.update_attributes(:confirmed, 1)
flash[:notice] = "Job Confirmed"
redirect_to :dashboard
end
I am sure this is all wrong but I seem to be guessing when it comes to has_many: through.
How would I do update the confirmed field in a joined table?
I think that a job is assigned to a user by the employment. Thus, updating all employments is not a good idea, as Joel suggests. I would recommend this:
class Employment
def self.confirm!(job)
employment = Employment.find(:first, :conditions => { :job_id => job.id } )
employment.update_attribute(:confirmed, true)
end
end
from your controller
#job = Job.find(params[:id])
Employment.confirm!(#job)
This implies that one job can only be taken by one user.
Here is a stab at it (not tested):
def confirmjob
#job = Job.find(params[:id])
#jobs.employments.each do |e|
e.update_attributes({:confirmed => 1})
end
flash[:notice] = "Job Confirmed"
redirect_to :dashboard
end