Factory girl create list count not right - ruby-on-rails-3

I use FactoryGril created two users, both with 3 events:
let (:user) {FactoryGirl.create :user,:with_events}
let (:user1) {FactoryGirl.create :user,:with_events}
But i get all events count 6, but i got 3,
Failure/Error: expect(json["events"].count).to eq 6
expected: 6
got: 3
(compared using ==)
Thanks!
Factories.rb
FactoryGirl.define do |f|
factory :user do
password "123123"
sequence(:name){|n| "张三#{n}" }
end
factory :event do
end_date "2015-05-30"
is_countdown "1"
end
trait :with_events do
after :create do |user|
FactoryGirl.create_list :event, 3, :user => user
end
end
end

Note that let is lazy-evaluated: it is not evaluated until the first time
the method it defines is invoked. You can use let! to force the method's
invocation before each example.
Change let! replace let make it!

Related

Rails ActiveRecord querying

So I have the following three models: Assignment.rb, Submission.rb, User.rb
And here are the relationships:
class Assignment
has_many :submissions
end
class Submission
belongs_to :assignment
belongs_to :user
# submission has a boolean column called submitted with val true or false
end
class User
has_many submissions
end
I want to know how can I query the assignments that a user has not submitted (in a clean way)? If a user submits an assignment, a new submission for that assignment and user will be created.
Not sure if I provided enough info for anyone to answer, so please comment if anything else is needed.Thx!
The logic that #Norly Canarias is using is correct, but I would alter it to use methods on the User class, and I would also modify it to make it database-agnostic (for example, using 'submissions.submitted = true' will not work at all in Postgres).
class User < ApplicationRecord
has_many :submissions
has_many :assignments, through: :submissions
def submitted_assignments
assignments.where(submissions: {submitted: true})
end
def unsubmitted_assignments
Assignment.where.not(id: submitted_assignments)
end
end
I have tested this and it works as expected. For a user who has a Submission for Assignment 1 with submitted == true, and who has a Submission for Assignment 2 with submitted == false, and assuming there are two more Assignments (3 and 4) for which no Submission exists, you will get:
>> user.submitted_assignments.ids
#=>[1]
>> user.unsubmitted_assignments.ids
#=>[2, 3, 4]
I think something like this could work (I haven't tested though):
class Assignment
has_many :submissions
end
class Submission
belongs_to :assignment
belongs_to :user
end
class User
has_many :submissions
has_many :assignments, through: :submissions
end
user = User.first
submitted = user.assignments.where('submissions.submitted = true')
not_submitted = Assignment.where.not(id: submitted)
You can also make it a scope
class Assignment
has_many :submissions
scope :not_submitted_by_user, ->(user) do
where.not(id: user.assignments.where('submissions.submitted = true'))
end
end
user = User.first
not_submitted = Assignment.not_submitted_by_user(user)
To get all the Assignments that are not from a specific user
#assignments = Assignment.where.not(user_id: user_id)
A clean way to do it is to create a scope in the Assignment Model
class Assignment
has_many :submissions
scope :not_from_user, ->(user_id) {where.not(user_id: user_id) }
end
And then calling
#assignments = Assignment.not_from_user 1

ruby on rails inconsistency in model persistance during has_many through association

I'm trying to figure out what is happening when I create a has_many through association.
models:
class Foo
has_many :bars
has_many :bazes, through: :bars
acceptes_nested_attributes_for :bars
acceptes_nested_attributes_for :bazes
end
class Bar
belongs_to :foo
belongs_to :baz
before_create :update_some_attr
def update_some_attr
if self.baz.new_record?
raise "baz is new record"
else
raise "baz is not new record"
end
end
end
class Baz
has_many :bars
end
form:
= form_for :foo do |f|
= f.fields_for :bazes do |baz_form|
# form fields for baz
= f.submit
If I raise an error within update_some_attr, and inspect the class, self.baz.new_record? returns true; and yet the ELSE condition fires, which means the Baz model is already persisted before the Bar record is created. I'm just trying to figure out why I'm getting this inconsistency while debugging.
Imgur link to _better_errors console output
Well first of all I have no direct answer to why the console output and the conditional statement both deliver inconsistent behaviour. However I do see opportunity to rewrite the method so the .new_record? condition can be circumvented:
You are applying a before_create callback which means that only on Model.create() the callback is fired so in that case why would one want to condition whether the instance is new? if the method should be used on existing records to update a certain attribute I would just stick to the update_attributes() method. Another method is to always require an exisiting record and change the callback to after_create this will make more sense as your method is called 'update'.
after_create :update_some_attr
def update_some_attr(*args={attribute: :name, value: "John Doe"})
args.assert_valid_keys(:attribute, :value)
attribute, value = [args[:attribute], args[:value]]
self.update_attributes(attribute => value)
end
I think tha you might expect something like this to happen:
Bar belongs_to Foo and to Baz, so before create Bar, ActiveRecord has to create Foo and Baz, get the ids and set it on the associations ids (foo_id and baz_id).
But i believe that the associations are giving you trouble:
Since Foo has many bars and bazs, ActiveRecord will try to save this associations before create Foo and maybe thats why Baz is already persisted on your method.
One solution (if thats the case) is to change the association between Bar and Baz to something like:
class Bar
has_many :bazs
end
class Baz
belongs_to :bar
end
But as i just realized now, you need Baz to have many Bars, so you can try to remove the association on Foo and the accepts_nested_attributes
has_many :basz, through: :bars
acceptes_nested_attributes_for :bazes
And accept the nested attributes on the Bar model:
acceptes_nested_attributes_for :baz
And you will be with something like:
class Foo
has_many :bars
acceptes_nested_attributes_for :bars
end
class Bar
belongs_to :foo
belongs_to :baz
acceptes_nested_attributes_for :baz
before_create :update_some_attr
def update_some_attr
if self.baz.new_record?
raise "baz is new record"
else
raise "baz is not new record"
end
end
end
class Baz
has_many :bars
end

Rails 3: As json with include option does not takes into account as_json redefinition for included association

I've got two models.
Class ModelA < ActiveRecord::Base
has_many :model_bs
end
Class ModelB < ActiveRecord::Base
belongs_to :model_a
def as_json(options = {})
{
:whatever => 'hello world'
}
end
end
When I call model_a.as_json(:include => :model_b), I want it to return a json which includes all model_bs, which it does, but employing my as_json redefinition, which it does not as it just uses the default one. Is there any way to use my own method rather than the original one? Thanks
In Rails 3, as_json method invokes serializable_hash to obtain the attributes hash. And they share the same 'options' parameter. In your case, overwritting serializable_hash would give the expected result.
def serializable_hash(options = {})
{:whatever => 'hello world'}
end
But, My suggestion is that instead of overwriting the convention, operate on the result of "super", which is like:
def serializable_hash(options = {})
hash = super
has[:name] = "hello world"
hash
end

Rails 3: Find parent of polymorphic model in controller?

I'm trying to find an elegant (standard) way to pass the parent of a polymorphic model on to the view. For example:
class Picture < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
end
class Employee < ActiveRecord::Base
has_many :pictures, :as => :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, :as => :imageable
end
The following way (find_imageable) works, but it seems "hackish".
#PictureController (updated to include full listing)
class PictureController < ApplicationController
#/employees/:id/picture/new
#/products/:id/picture/new
def new
#picture = imageable.pictures.new
respond_with [imageable, #picture]
end
private
def imageable
#imageable ||= find_imageable
end
def find_imageable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end
Is there a better way?
EDIT
I'm doing a new action. The path takes the form of parent_model/:id/picture/new and params include the parent id (employee_id or product_id).
I'm not sure exactly what you're trying to do but if you're trying to find the object that 'owns' the picture you should be able to use the imageable_type field to get the class name. You don't even need a helper method for this, just
def show
#picture = Picture.find(params[:id])
#parent = #picture.imagable
#=> so on and so forth
end
Update
For an index action you could do
def index
#pictures = Picture.includes(:imagable).all
end
That will instantiate all 'imagables' for you.
Update II: The Wrath of Poly
For your new method you could just pass the id to your constructor, but if you want to instantiate the parent you could get it from the url like
def parent
#parent ||= %w(employee product).find {|p| request.path.split('/').include? p }
end
def parent_class
parent.classify.constantize
end
def imageable
#imageable ||= parent_class.find(params["#{parent}_id"])
end
You could of course define a constant in your controller that contained the possible parents and use that instead of listing them in the method explicitly. Using the request path object feels a little more 'Rails-y' to me.
I just ran into this same problem.
The way I 'sort of' solved it is defining a find_parent method in each model with polymorphic associations.
class Polymorphic1 < ActiveRecord::Base
belongs_to :parent1, :polymorphic => true
def find_parent
self.parent1
end
end
class Polymorphic2 < ActiveRecord::Base
belongs_to :parent2, :polymorphic => true
def find_parent
self.parent2
end
end
Unfortunately, I can not think of a better way. Hope this helps a bit for you.
This is the way I did it for multiple nested resources, where the last param is the polymorphic model we are dealing with: (only slightly different from your own)
def find_noteable
#possibilities = []
params.each do |name, value|
if name =~ /(.+)_id$/
#possibilities.push $1.classify.constantize.find(value)
end
end
return #possibilities.last
end
Then in the view, something like this:
<% # Don't think this was needed: #possibilities << picture %>
<%= link_to polymorphic_path(#possibilities.map {|p| p}) do %>
The reason for returning the last of that array is to allow finding the child/poly records in question i.e. #employee.pictures or #product.pictures

How can I map between strings and attributes automatically?

I have a tiny logical error in my code somewhere and I can't figure out exactly what the problem is. Let's start from the beginning. I have the following extension that my order class uses.
class ActiveRecord::Base
def self.has_statuses(*status_names)
validates :status,
:presence => true,
:inclusion => { :in => status_names}
status_names.each do |status_name|
scope "all_#{status_name}", where(status: status_name)
end
status_names.each do |status_name|
define_method "#{status_name}?" do
status == status_name
end
end
end
end
This works great for the queries and initial setting of "statuses".
require "#{Rails.root}/lib/active_record_extensions"
class Order < ActiveRecord::Base
has_statuses :created, :in_progress, :approved, :rejected, :shipped
after_initialize :init
attr_accessible :store_id, :user_id, :order_reference, :sales_person
private
def init
if new_record?
self.status = :created
end
end
end
Now I set a status initially and that works great. No problems at all and I can save my new order as expected. Updating the order on the other hand is not working. I get a message saying:
"Status is not included in the list"
When I check it seems that order.status == 'created' and it's trying to match against :created. I tried setting the has_statuses 'created', 'in_progress' etc but couldn't get some of the other things to work.
Anyway to automatically map between string/attribute?
from your description, looks like you're comparing a string to a symbol. Probably need to add:
define_method "#{status_name}=" do
self.status = status_name.to_sym
end
or do a #to_s on the status_names