To wrap up a exercise I'm working on, I'm trying to test an association through a join table. What I'd like to do is to test the association that Artists have many collections through Art Pieces and Collections have many Artists through Art Pieces. Below is my code:
context 'associations' do
let(:artist){ Artist.create(first_name: "Daniel", last_name: "Rubio",
email: 'drubs#email.com', birthplace: 'Mexico',
style: 'Contemporary') }
let(:art_piece) { ArtPiece.new(date_of_creation: DateTime.parse('2012-3-13'),
placement_date_of_sale: DateTime.parse('2014-8-13'),
cost: 250, medium: 'sculpture', availability: true, artist_id:
artist.id, customer_id: customer.id,
collection_id: collection.id)}
let(:customer) { Customer.create(first_name: 'Carmen', last_name: 'Dent', email:
'cdent#email.com', no_of_purchases: 10, amount_spent: 100000) }
let(:collection) { Collection.create(name: 'Romanticism') }
it 'has many collections through art pieces' do
expect(artist).to respond_to(:collections)
art_piece.save!
expect(artist.collections).to eql(collection)
end
end
Now I'm positive my associations are setup correctly on my validations:
class Collection < ActiveRecord::Base
validates_presence_of :name
has_many :art_pieces
has_many :artists, through: :art_pieces
end
class Artist < ActiveRecord::Base
validates_presence_of :first_name
validates_presence_of :last_name
validates :email, presence: true, uniqueness: true
validates_presence_of :style
validates_presence_of :birthplace
has_many :art_pieces
has_many :collections, through: :art_pieces
end
I followed activerecord's guidelines and it makes sense to me. The only two things that I think is happening is either A. I have a syntax error somewhere or B. my variables are not being chained properly. Any insight?
Below is the error message I get:
1) Artist associations has many collections through art pieces
Failure/Error: expect(artist.collections).to eql(collection)
expected: #<Collection id: 20, name: "Romanticism", created_at: "2014-04-06 16:57:42", updated_at: "2014-04-06 16:57:42">
got: #<ActiveRecord::Associations::CollectionProxy [#<Collection id: 20, name: "Romanticism", created_at: "2014-04-06 16:57:42", updated_at: "2014-04-06 16:57:42">]>
(compared using eql?)
Diff:
## -1,2 +1,2 ##
-#<Collection id: 20, name: "Romanticism", created_at: "2014-04-06 16:57:42", updated_at: "2014-04-06 16:57:42">
+[#<Collection id: 20, name: "Romanticism", created_at: "2014-04-06 16:57:42", updated_at: "2014-04-06 16:57:42">]
artist.collection returns array-like Relation, while you test it to return single ActiveRecord object. You should have:
expect(artist.collections).to eql([collection])
in your test.
Try changing
expect(artist.collections).to eql(collection)
to
expect(artist.collections).to eql([collection])
Currently your rspec is expecting a single collection object but what your query returns is an array like object with a single element in it. Changing the expectation to a list of your collection should do the trick.
Related
I'm inheriting a codebase for a Rails app that uses a blog engine -- and I am not making sense of how the models interact.
What I want to do is show the author who is associated with a specific article.
There is a table for articles called LinesArticle. An example entry:
<LinesArticle id: 2, title: "Example Article", sub_title: "Example Title",
content: "Example Content", published: true, published_at: "2017-08-22 00:00:00",
created_at: "2017-08-23 06:15:33", updated_at: "2017-08-23 06:15:36", slug: "whatever",
featured: false, document: nil, short_hero_image: "", teaser: "">
Next, there is a table for Authors called LinesAuthor. An example entry that should be associated:
#<LinesAuthor id: 1, name: "John Doe", email: "jd#examplesitedotcom", created_at: "2017-08-19 07:46:04", updated_at: "2017-08-19 07:46:04">
So if I compare these two tables, there isn't a connection between the data that would make sense for the models. So then I found LinesAuthorable that I think connects them. An entry:
<LinesAuthorable id: 2, author_id: 1, article_id: 2,
created_at: "2017-08-23 06:15:33", updated_at: "2017-08-23 06:15:33">
So my thinking is, if my controller calls an article like #articles = LinesArticle.last and I want to show the author who wrote that article by matching article_id to the matching result in LinesAuthorable and then query LinesAuthor for the matching author_id.
Reading through the documentation, I have now created models where an Article model belongs_to :lines_authorable and Author has_many :Lines_Authorable. If that is the right approach, how would you call that in the view to actually show the Author?
The LinesAuthorable table is acting as a through table, you can use a has_many through relationship to connect the data, something like:
class LinesArticle < ApplicationRecord
has_many :lines_authorables, foreign_key: :article_id
has_many :lines_authors, through: :lines_authorables
end
class LinesAuthor < ApplicationRecord
has_many :lines_authorables, foreign_key: :author_id
has_many :lines_articles, through: :lines_authorables
end
class LinesAuthorable < ApplicationRecord
belongs_to :line_article, foreign_key: :article_id
belongs_to :line_author, foreign_key: :author_id
end
You can then access an article's authors, and likewise an author's articles directly through the relationship:
#article = LinesArticle.first
#article.lines_authors #=> #<LinesAuthor::ActiveRecord_Relation...>
#author = LinesAuthor.first
#author.lines_articles #=> #<LinesArticle::ActiveRecord_Relation...>
Does Rails allow conditional validations for validates_associated? I'm seeing the following on Rails 4.2.0. Am I trying to do it incorrectly?
Models:
class User < ActiveRecord::Base
has_many :books
validates_associated :books, if: :should_validate_book?
def should_validate_book?
return false
end
end
class Book < ActiveRecord::Base
belongs_to :user
validates_presence_of :title
end
The presence validation on Book's title attribute still runs (Rails console):
> u = User.create!
=> #<User id: 2, created_at: "2015-02-24 19:34:51", updated_at: "2015-02-24 19:34:51">
> u.books.build
=> #<Book id: nil, user_id: 3, title: nil, created_at: nil, updated_at: nil>
> u.valid?
=> false
> u.books.first.errors
=> #<ActiveModel::Errors:0x007fa256b210d8 #base=#<Book id: nil, user_id: 3, title: nil, created_at: nil, updated_at: nil>, #messages={:title=>["can't be blank"]}>
It turns out that validates_associated is ON by default for has_many relationships. To make it conditionally, you'd need to add validate: false to the has_many declaration:
has_many :books, validate: false
In Rails since time immemorial validates_associated has only taken a list of attributes. Besides that, you'd kind of be mixing up behavior between your models from what I gather from the criteria you've pasted. A better approach would be to adjust your validations in the Book model to account for the variation and let Book decide for itself whether an object should be validated or not.
I have three models, Employee, Job, LeaveDay. like below:
class Employee
field :name
belongs_to :job
end
class Job
field :job_title
has_many :employees
has_many :leave_days
end
class LeaveDay
belongs_to :job
field :no_of_leave_days
end
I want to establish an relationship in which I want to track no_of_leave_days of employee related to their job. How is it possible. Thanks in advance.
i think u need to add 'belongs_to: employee' in LeaveDay and 'has_many: leave_days' in Employee. so, #emp.leave_days will get u all the leave for all the jobs of a particular employee. similarly, #emp.job.leave_days will return the leave_days for the current job of the cuttent employee.
I'm not really familiar with Mongoid. You may read here more about relations in mongoid.
It's very easy example. Hope it'll help.
This is models definition:
class Employee
include Mongoid::Document
field :name
belongs_to :job
end
class Job
include Mongoid::Document
field :job_title
has_many :employees
embeds_many :leave_days
end
class LeaveDay
include Mongoid::Document
embedded_in :job
field :no_of_leave_days
end
And example of usage:
pry(main)> j = Job.create(job_title: "Test Job")
=> #<Job _id: 510633e0784931179a000001, _type: nil, job_title: "Test Job">
pry(main)> LeaveDay.create(job: j, no_of_leave_days: 1)
=> #<LeaveDay _id: 5106348f784931179a000002, _type: nil, no_of_leave_days: 1>
pry(main)> LeaveDay.create(job: j, no_of_leave_days: 2)
=> #<LeaveDay _id: 51063687784931179a000003, _type: nil, no_of_leave_days: 2>
pry(main)> LeaveDay.create(job: j, no_of_leave_days: 3)
=> #<LeaveDay _id: 51063689784931179a000004, _type: nil, no_of_leave_days: 3>
pry(main)> Job.first.leave_days.map(&:no_of_leave_days)
=> [1, 2, 3]
pry(main)> Employee.create(name: 'Employee name', job: Job.first)
=> #<Employee _id: 510637cf784931179a000005, _type: nil, name: "Employee name", job_id: "510633e0784931179a000001">
pry(main)> Employee.first.job.leave_days.map(&:no_of_leave_days)
=> [1, 2, 3]
I've created two models with the below associations
class User < ActiveRecord::Base
has_many :roles, :dependent => :destroy
end
class Role < ActiveRecord::Base
belongs_to :user
end
class Student < Role
end
class Tutor < Role
end
However when I create a new child role, I assume it would get associated to the model it has the belongs to for.
Such as:
Tutor.create(:user_id => user_id)
I would expect:
#some user #user
#user.roles
to have an array containing a Tutor. However, it doesn't seem to be working. Any ideas what I'm doing wrong?
Once you start using Single Table Inheritance, than the Tutor that you created isn't a role, as far as active-record is concerned for this type of query.
class User < ActiveRecord::Base
has_many :roles
has_many :tutors
end
#user = User.first
#user.roles
=> []
#user.tutors
=> [#<Tutor id: 1, user_id: 1, type: "Tutor", created_at: "2012-10-26 18:15:16", updated_at: "2012-10-26 18:15:16">]
If you want to get a list of all roles that your users may have:
Role.where(user_id: #user.id).all
[#<Tutor id: 1, user_id: 1, type: "Tutor", created_at: "2012-10-26 18:15:16", updated_at: "2012-10-26 18:15:16">, #<Student id: 2, user_id: 1, type: "Student", created_at: "2012-10-26 18:18:32", updated_at: "2012-10-26 18:18:32">]
I'm new to rails and testing and have a problem with loading my fixtures.
payment.yml
payment:
name: something
amount: 1.5
event: some_event
user: some_user
description: long text
users: some_user, some_user2
users.yml
some_user:
email: test#test.com
nick: name
some_user2:
email: test#test.com
nick: name
okey, so the problem is that when I'm doing the functionaltest for creating a payment
test "should create payment" do
assert_difference('Payment.count') do
post :create, :payment => #payment.attributes
end
it just sends
< Payment id: nil, name: "something", amount: 1.5, event_id: 972288058, user_id: 63246679, created_at: "2010-11-05 19:56:53", updated_at: "2010-11-05 19:56:53", description: "long text" >
and not the users array with multiple users. I use the "user" to define who owns the payment and users (in a seperate join table) as a list of users sharing the payment.
Any idea what I'm doing wrong?
here is the model for payments
class Payment < ActiveRecord::Base
has_many :comments, :as => :commentable, :dependent => :destroy
belongs_to :event
belongs_to :user
has_and_belongs_to_many :users
end
Can you put the model for payment? I had something similar and the problem was that the model was different. For example, if it's a ActiveRecord class, you have to check how it and its relationships. For example, if the event is actually an "event_id" that belongs_to an "event" class, then you should put something similar to this:
payment.yml
payment:
name: something
amount: 1.5
event_id: 1
user_id: 1
...
user.yml
id: 1
name: David Smith
status: Branch Manager
...
event.yml
id: 1
name: overdraft charge