Arbitrary attributes error with has_one association and Factory Girl - ruby-on-rails-3

I'm trying to build a basic shopping cart for a Rails app I'm working on.
Nothing special,
- the shopping cart has many line_items
- each line_item has_one product associated and a quantity with it
class Cart < ActiveRecord::Base
attr_accessible :line_items
has_many :line_items, :dependent => :destroy
end
class LineItem < ActiveRecord::Base
attr_accessible :quantity, :product
belongs_to :cart
has_one :product
end
I'm trying to use RSpec to test this association, but i'm doing something wrong as I'm getting an error that says: DEPRECATION WARNING: You're trying to create an attribute 'line_item_id'. Writing arbitrary attributes on a model is deprecated, and I'm not sure why.
In my factories.rb file I'm defining the line_item factory as follows:
factory :line_item do
quantity { Random.rand(1..5) }
product
end
factory :cart do
factory :cart_with_two_line_items do
ignore do
line_item_count 2
end
after(:create) do |cart, evaluator|
FactoryGirl.create_list(:line_item, evaluator.line_item_count, cart_id: cart) # < 104
end
end
end
Any pointers where I'm going wrong, it's probably something basic, but I'm still quite new to Rspec. Thanks in advance.
EDIT: line_item_spec.rb
require 'spec_helper'
describe LineItem do
before do
#line_item = FactoryGirl.create(:line_item)
end

Maybe you forgot to declare the association in the Product model.
class Product < Activerecord::Base
belongs_to :line_item
belongs_to will expect that your products table has a column :line_item_id. Have you run your migration and modified the models?

Related

Get related object has_and_belongs_to_many in rails

Let's say I got tasks and lists with an has_and_belongs_to_many relationship:
class Task < ActiveRecord::Base
attr_accessible :content, :due_date
has_and_belongs_to_many :lists
end
class List < ActiveRecord::Base
attr_accessible :title, :user_id, :space_free_title
has_and_belongs_to_many :tasks
end
Also I got the related model/table since a task can be on many lists:
class ListsTasks < ActiveRecord::Base
attr_accessible :list_id, :task_id
end
Now I know how to get all ListsTasks by the list_id:
ListsTasks.find_all_by_list_id(1)
But how do I get the contents of a task based on ListsTasks?
Your ListsTasks model is unnecessary and is not being used by the two associations in your List and Task models.
Are you looking for something like List.find(1).tasks to get the tasks on that list?

Rails: decrement attribute in table when other record is created

So I have two models: Product and Sale. they are related via a has_many :trough association and both have the same attributes and attribute names (sale has one more tough):
THE CODE SNIPPETS:
# Product Model
class Product < ActiveRecord::Base
attr_accessible :qty, :color, :name, :price, :size, :type, :code
has_many :orders
has_many :sales, :through => :orders
end
# Sale Model
class Sale < ActiveRecord::Base
attr_accessible :qty, :color, :name, :salesman_name, :price, :size, :type, :code
has_many :orders
has_many :productos, :through => :orders
end
What I want to do is to decrement the Products>Quantity attribute of a specific record (product) every time a new Sale is created with it's name on it... The substraction must be equal to the value of "qty" set in the new sale creation in relation with the created product...
The app is a very simple inventory system where I track stock items and sales, let's give a pratical example: Imagine I have a product called "Socks" in the Products Database and I have "30" as the qty attribute of that product, Then someone sells two socks (creates a new sale with 2 socks in the app): in that case I want the value of "Socks" to be updated to 28 in the Products database automatically.
I've been reading and I think this may can be accomplished with an after_create callback in the sale model with an ActiveRecord transaction, but I'm not sure how to implement this, Can someone help me?
By the way, I was thinking about using something similar to this thing here:
after_create :decrement_stock
def decrement_stock
Sale.transaction do
Product.transaction do
#substraction = product.sale(params[:qty])
product.update_attributes!(:qty => #qty - #substraction)
end
end
end
But I'm pretty sure it isn't going to work, Please Point me in the right directions... I've been struggling with this one a little.
Thanks and have a nice day.
I think you should rebuild this associations and models
What I'd do is to have this models (a little bit different than yours):
Salesman
class Salesman < ActiveRecord::Base
attr_accessible :last_name, :name
has_many :sales
end
Product
class Product < ActiveRecord::Base
attr_accessible :name, :qty
has_many :product_sales
end
Sale
class Sale < ActiveRecord::Base
belongs_to :salesman
has_many :product_sales
end
And here's the trick
Model ProductSale
class ProductSale < ActiveRecord::Base
belongs_to :sale
belongs_to :product
attr_accessible :qty
after_create :decrement_stock
def decrement_stock
self.product.update_attribute("qty", (product.qty - self.qty))
end
end
There you have Sale that belongs_to salesman.
Sale has_many product_sales
and product_sales belongs_to product and sales that way in one sale you have multiple product
Then when a ProductSale is saved there's the callback after_create :decrement_stock
And you can update the attribute for that specific product.
Remember that you'll have a quantity for each product and not for the whole sale.
Here's a little example I set up for you:
https://github.com/Mr-Nizzle/inventory

Implementing a has_many :through

I am thinking of creating the following models using 'has_many :through':
class Contract < AR::Base
has_many :clientlines
has_many :codelines
has_many :clients, :through => :clientlines
has_many :codes, :through => :codelines
end
class clientlines < AR::Base
belongs_to :contract
belongs_to :client
end
class Client < AR::Base
has_many :clientlines
has_many :contracts, :through => :clientlines
end
class codeline < AR::Base
belongs_to :contract
belongs_to :code
units_alloc -------**I would like to add this attribute after this intermediate
end has been created?
class Code < AR::Base
has_many :codelines
has_many :contracts, :through => :codelines
end
Do I first create the models with 'rails generate model Contract authnum:string, client_id:integer, st_date:date, end_date:date' for example.
Then fill in all of the associations before the migrations?.
Also, my understanding is that all of the join tables are created automatically by rails when using the has_many :through association. When does that happen?
Lastly, as indicated by the **, can I have this attribute in codelines, and do I create a 'rails generate migration add_units_alloc_to_codelines units_alloc:number' in order to add this attribute to the join table? I was also wondering how I declare the number to be too two decimal places?
If you have the time and inclination could you please comment on my proposed design for my database?
Thanks.
by using has_many :through you use a third model that makes the connection between the other two, so rails doesn't automatically build that model, you build it yourself and reference the foreign keys for the other two models.
don't name your models at their plural, always singular. if you can't name them at singular, you're doing it wrong
The order in which you create your models shouldn't matter too much (rails generates some migrations which you can easily modify later)
That units_alloc attribute, just add it to the model when you create it, simple as that!
for 2 decimals use something like t.decimal :amount, :precision => 6, :scale => 2 in your migration (that example gives you 6 digits and 2 decimals)
Read the Rails Guides, it will really help you get out a lot of trouble

Table and Ruby ActiveRecord Class design for (sub)categories

I guess i have a rather simple question since I'm new to Ruby and even newer to ActiveRecords.
What I want to achieve is a class representation with ActiveRecords (and the corresponding SQL schema) that models the following problem:
There exist categories and subcategories (modeled by parent_id)
Products belong to only one category
Each product can have 0..inf features
Features simply have some data fields and are only referenced by the products
My current schema is shown below in the picture:
Is this schema suitable for ActiveRecords? How would the classes look like? I simply cant figure out how the JoinTable fits into the ActiveRecord structure.
Further, how can i model the link from parent_id->categories.id?
Any help appreciated!
cheers
To model the relationships you described you would do:
models/category.rb
class Category < ActiveRecord::Base
has_many :products
has_many :subcategories, :class_name => "Category", :foreign_key => :parent_id
end
models/product.rb
class Product < ActiveRecord::Base
belongs_to :product
has_many :features, :through => :product_features
has_many :product_features
end
models/feature.rb
class Feature < ActiveRecord::Base
has_many :product_features
has_many :products, :through => :product_features
end
models/productfeature.rb
class ProductFeature < ActiveRecord::Base
belongs_to :product
belongs_to :feature
end
Given this structure then you have the join modelled as a Many-to-Many relation. This is useful since the HABTM style of join is going away in Rails 3.1
To get the information, I often use the console rails console for testing and this would allow you do do
#category = Category.first #get the first category
#category.subcategories #returns an array of categories
The traversal of the links is via the relations that you setup in the models, with the intention that its readable, in the context of using sensible names. The self-joins, as per your question, is also covered in Rails Guides: Associations with a good example. The rest of this guide also details the other relationships.
One other thing to remember is to create your migrations so that the join table is created with the id's which are the foreign keys.
My models would look like this:
class Category < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :category
has_many :product_features
has_many :features, :through => :product_features
end
class ProductFeature < ActiveRecord::Base
belongs_to :product
belongs_to :feature
end
class Feature < ActiveRecord::Base
has_many :product_features
has_many :products, :through => :product_features
end
Rails has an association called has_and_belongs_to_many. Rails expects a table with two columns to store the join data. I usually use dual has_many to achieve the same results as it gives you flexibility to add additional information in the join table.
Sample code
product.category
product.category = category1
category.products
category.products << product1
product.features
product.features << feature1
feature.products
feature.products << product1
Here is the API for ActiveRecord::Associations::ClassMethods
There are a lot of examples in there of different relationships and how to construct them. It's worth taking the time to understand how/why you construct these associations.
For the Many-to-many join you will want to look at
has_many ..., :through => ...
has_and_belongs_to_many ...
The docs explain when and why to use each.

How to build the following query

I'm developing a web application about gift lists in Rails 3. I'd would like to get all the purchases for a certain list. These are the models and their relationships:
class Gift < ActiveRecord::Base
belongs_to :list
has_many :purchases
class List < ActiveRecord::Base
has_many :gifts
class Purchase < ActiveRecord::Base
belongs_to :gift
So, basically, I've tried the following code (I don't think it's the best way to do this at all), and eventhough the results are correct, I've realized that I get a Gift object instead of a Purchase:
#purchases = List.find(params[:id]).gifts.joins(:purchases).select("purchases.*")
Any ideas?
#purchases = List.find(params[:id]).
gifts.joins(:purchases).
map(&:purchases).flatten
or just refactor your models:
class List < ActiveRecord::Base
has_many :gifts
has_many :purchases, :through => :gifts
so
List.find(params[:id]).purchases
If you want all the purchases maybe...
#purchases= List.find(params[:id]).gifts.collect{|g| g.purchases}
Although you may want to split it up and check that List.find returns a valid List.