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
Related
I'm having a bit of trouble understanding how to setup the contributions controller and the form in the view. I've set some forms in the view so i know the join tables work.
As of right now a post belongs_to user && a user has_many posts
Objective:
1. user1 creates post - which belongs to user1
2. user2 requesting to join the user1_post as a contributor
3. user1 accepts or declines request
4. user2 is now a contributor to user1_post
5. user1 can remove user2 as a contributor
Got the has_many :through setup properly and have tested it in the console
contribution.rb
class Contribution < ActiveRecord::Base
belongs_to :post
belongs_to :user
def accept
self.accepted = true
end
end
post.rb
class Post < ActiveRecord::Base
belongs_to :author, class_name: 'User'
has_many :contribution_requests, -> { where(accepted: false) }, class_name: 'Contribution'
has_many :contributions, -> { where(accepted: true) }
has_many :contributors, through: :contributions, source: :user
end
user.rb
class User < ActiveRecord::Base
has_many :posts, foreign_key: 'author_id'
has_many :contribution_requests, -> { where(accepted: false) }, class_name: 'Contribution'
has_many :contributions, -> { where(accepted: true) }
has_many :contributed_posts, through: :contributions, source: :post
end
contributions_controller.rb
class ContributionsController < ApplicationController
def create
#contribution = current_user.contributions.build(:user_id => params[:id])
if #contribution.save
flash[:notice] = "Added contributor."
redirect_to posts_path(#post)
else
flash[:error] = "Unable to add contributor."
redirect_to posts_path(#post)
end
end
def destroy
#contribution = current_user.contributions.find(params[:id])
#contribution.destroy
flash[:notice] = "Removed contributor."
redirect_to root_url
end
end
Without much context, this is what I'd do:
#config/routes.rb
resources :posts do
resources :contributions, only: [:create, :destroy] #-> can use posts#edit to add extra contributions
end
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def edit
#post = Post.find params[:id]
end
end
#app/views/contributions/edit.html.erb
<%= form_for #post do |f| %>
# #post form
<% end %>
## contributor add / remove form (select boxes)
#app/controllers/contributions_controller.rb
class ContributionsController < ApplicationController
def create
#post = Post.find params[:post_id]
#contribution = current_user.contributions.new contribution_params
#contribution.post = #post
notice = #contribution.save ? "Added Contributor" : "Unable to add contributor"
redirect_to #post, notice: notice
end
def destroy
#contribution = current_user.contributions.find params[:id]
#contribution.destroy
redirect_to root_url, notice: "Removed Contributor"
end
private
def contribution_params
params.require(:contribution).permit(:user, :post, :accepted)
end
end
As an aside, you should look at an ActiveRecordExtension to give you some methods for your conbtributions association (instead of having multiple associations):
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :contributions, -> { extending ContributionExtension }
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :contributions, -> { extending ContributionExtension }
end
#app/models/concerns/contribution_extension.rb
class ContributionExtension
def requests(status=false)
where accepted: status
end
def accepted(status=true)
where accepted: status
end
end
#post.contirbutions.requets
#post.contributions.accepted
#user.contributions.requests
#user.contributions.accepted
--
And also, you should look at implementing a state_machine for your Contribution model:
#app/models/contribution.rb
class Contribution < ActiveRecord::Base
state_machine :accepted, initial: :pending do
event :accept do
transition [:pending, :denied] => :accepted
end
event :deny do
transition [:pending, :accepted] => :denied
end
end
end
Great article about it here.
This will allow you to call:
#contribution = current_user.contributions.find params[:id]
#contribution.accept
It will also give you several other cool methods:
#contribution.accepted?
#contribution.state
In my Rails project I have a User that can have many Projects which in turn can have many Invoices. Each Invoice can have many nested Items.
class Invoice < ActiveRecord::Base
attr_accessible :number, :date, :recipient, :project_id, :items_attributes
belongs_to :project
belongs_to :user
has_many :items, :dependent => :destroy
accepts_nested_attributes_for :items, :reject_if => :all_blank, :allow_destroy => true
validates :project_id, :presence => true
def build_item(user)
items.build(:price => default_item_price(user), :tax_rate => user.preference.tax_rate)
end
def set_number(user)
self.number ||= (user.invoices.maximum(:number) || 0).succ
end
end
class InvoicesController < ApplicationController
def new
#invoice = current_user.invoices.build(:project_id => params[:project_id])
#invoice.build_item(current_user)
#invoice.set_number(current_user)
#title = "New invoice"
end
def create
#invoice = current_user.invoices.build(params[:invoice])
if #invoice.save
flash[:success] = "Invoice created"
redirect_to invoices_path
else
render :new
end
end
end
Now, I am trying to test the creation of invoices with RSpec and FactoryGirl and all tests pass except for the ones related to the POST create action, such as:
it "saves the new invoice in the database" do
expect {
post :create, invoice: attributes_for(:invoice, project_id: #project, items_attributes: [ attributes_for(:item) ])
}.to change(Invoice, :count).by(1)
end
It keeps giving me this error:
Failure/Error: expect {
count should have been changed by 1, but was changed by 0
Can anybody tell me why this happens?
This is my factories.rb which I use to fabricate objects:
FactoryGirl.define do
factory :invoice do
number { Random.new.rand(0..1000000) }
recipient { Faker::Name.name }
date { Time.now.to_date }
association :user
association :project
end
factory :item do
date { Time.now.to_date }
description { Faker::Lorem.sentences(1) }
price 50
quantity 2
tax_rate 10
end
end
Can anybody help?
Thanks...
I have this relationship where User can create a document(trip) and invite other users to a group that belongs to that document. My relationship indicates that "Group" has a user_id and trip_id column, so for every user I invite, a new Group record will be created in the database.
When I am inviting other users, I only want users who are NOT in the group to appear. Users who are already in the group should not show up, but my view still shows the users.
I've been playing around with <% if !friend.trips.include?(#trip)%>, but I can't seem to get the correct view. The record is being created in the database correctly.
Also, when I am viewing groups/new.html.erb, this is the url http://localhost:3000/groups/new?id=2, where the id is the trip_id.
My question:
Am I using restful convention? That is, should I be using the new method here (as is) or should I be using the index method instead?
How do I iterate through each friend's groups to make sure that none of the group's trip_id is equivalent to #trip.id?
Thanks!
view (/groups/new.html.erb)
<% if !#friends.blank? %>
<% #friends.each do |friend| %>
<% if !friend.trips.include?(#trip)%>
<%= link_to groups_path(:user_id => friend.id, :trip_id => #trip.id),
:method => :post, :action => 'create' do %>
<div id="addfriend_totrip_button_groupsnew">add friend to trip</div>
<% end %>
<% end %>
<% end %>
<% end %>
groups_controller.rb
class GroupsController < ApplicationController
before_filter :authenticate, :only => [:update, :create, :destroy]
def new
#trip = Trip.find(params[:id])
#user = User.find(current_user)
#group = Group.new
#friends = #user.friends.all
end
def create
#trip = Trip.find(params[:trip_id])
#user = User.find(params[:user_id])
#group = Group.create(:user_id => #user.id, :trip_id => #trip.id)
if #group.save
flash[:success] = "Friend added to group."
redirect_to groups_path(:id => #trip.id)
else
flash[:error] = "Could not add friend."
redirect_to root_path
end
end
end
user.rb
class User < ActiveRecord::Base
has_many :trips, :through => :groups
has_many :trips, :dependent => :destroy
has_many :groups
end
trip.rb
class Trip < ActiveRecord::Base
belongs_to :user
belongs_to :traveldeal
has_many :groups
has_many :users, :through => :groups
end
group.rb
class Group < ActiveRecord::Base
belongs_to :trip
belongs_to :user
end
First of all, you have has_many :trips called twice in your User model. I understand you have two different types of User-Trip relationships (one directly, and one through Group), but you can't give both the same name, otherwise one will hide the other. Try defining your User model like this:
class User < ActiveRecord::Base
has_many :group_trips, :through => :groups,
:class_name => "Trip"
has_many :trips, :dependent => :destroy
has_many :groups
def all_trips
Trip.joins(:groups).where({:user_id => self.id} | {:groups => {:user_id => self.id}})
end
end
There's also the problem that you're searching the friend's list of groups for a Trip object. Try changing that line to:
<% if !friend.all_trips.include?(#trip) %>
Or without the new method, something like this should work:
<% if !friend.groups.where(:trip_id => #trip.id).first %>
I don't see anything un-RESTful about your approach. RESTful in general means stateless. I.e. the only thing a response depends on is the HTTP method and the address. So as long as your not keeping state information in, say, the session, you should be following REST.
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
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.