I'm using Rails 5.0.5 with Devise 4.3.0 for authentication. This app has been running smoothly for months, until I added a 'type'=>'string' attribute to my User model and attempted to create a new user. Submitting the form gives me a 500 internal server error. In this example, the User.type = 'hunter'.
NameError - wrong constant name hunter:
activesupport (5.0.5) lib/active_support/inflector/methods.rb:268:in `const_get'
activesupport (5.0.5) lib/active_support/inflector/methods.rb:268:in `block in constantize'
activesupport (5.0.5) lib/active_support/inflector/methods.rb:266:in `each'
activesupport (5.0.5) lib/active_support/inflector/methods.rb:266:in `inject'
activesupport (5.0.5) lib/active_support/inflector/methods.rb:266:in `constantize'
activesupport (5.0.5) lib/active_support/dependencies.rb:583:in `get'
activesupport (5.0.5) lib/active_support/dependencies.rb:614:in `constantize'
activerecord (5.0.5) lib/active_record/inheritance.rb:177:in `find_sti_class'
activerecord (5.0.5) lib/active_record/inheritance.rb:209:in `subclass_from_attributes'
activerecord (5.0.5) lib/active_record/inheritance.rb:55:in `new'
devise (4.3.0) lib/devise/models/registerable.rb:20:in `new_with_session'
app/models/user.rb:58:in `new_with_session'
user.rb:
def self.new_with_session(params, session)
if session['devise.user_attributes']
new(session['devise.user_attributes']) do |user|
user.attributes = params
user.valid?
end
else
super
end
end
Is Rails think this attribute value is a ClassName?? Can't seem to figure this one out. Any help greatly appreciated.
ActiveRecord uses the type column for Single Table Inheritance (STI) by default and the type value is expected to name a class. Presumably you don't have a hunter class so you get a confusing NameError from deep inside the guts of ActiveRecord.
From the fine manual:
inheritance_column()
Defines the name of the table column which will store the class name on single-table inheritance situations.
The default inheritance column name is type, which means it's a reserved word inside Active Record. To be able to use single-table inheritance with another column name, or to use the column type in your own model for something else, you can set inheritance_column:
self.inheritance_column = 'zoink'
Either rename your type column to something else or tell ActiveRecord to use some other column name for STI:
class User < ApplicationRecord
self.inheritance_column = 'there_is_no_sti_here' # Or whatever you're not using for a column name.
end
Using self.inheritance_column = nil also works.
If you're doing this a lot then you could make it declarative by adding a concern:
module STISuppression
extend ActiveSupport::Concern
included do
self.inheritance_column = nil
end
end
and then say things like:
class SomeModel < ApplicationRecord
include STISuppression
end
Same effect but it makes it clear what you're up to.
Related
I'm trying to make my tests robust and really solid, and I've been breaking down some complex queries and associations into smaller ones, or refactoring and moving the data into scopes.
Given the following classes:
class Item < ActiveRecord::Base
belongs_to :location
scope :in_location, ->(location) { where(location: location) }
scope :findable, ->(location, not_ids) {
in_location(location).where.not(id: not_ids)
}
end
class Container < ActiveRecord::Base
belongs_to :location
# THIS IS WHAT I WANT TO TEST
has_many :findable_items, ->(container) {
findable(container.location, container.not_findable_ids)
}, class_name: 'Item'
end
How would you test a variable has_many relationship like this without hitting the database to a significant degree? I know I can test the Item.findable method on it's own; what I'm interested in is the container.findable_items method.
Note: the actual association being tested is more complex than this, and would require pretty extensive set-up; it's running through a few other nested associations and scopes. I'd like to avoid this setup if possible, and just test that the scope is called with the correct values.
Relevant parts of my Gemfile:
rails (4.2.3)
shoulda-matchers (2.6.2)
factory_girl (4.5.0)
factory_girl_rails (4.5.0)
rspec-core (3.3.2)
rspec-expectations (3.3.1)
rspec-its (1.2.0)
rspec-mocks (3.3.2)
rspec-rails (3.3.3)
I have shoulda-matchers in my project, so I can do the basic sanity test:
it { should have_many(:findable_items).class_name('Item') }
but this fails:
describe 'findable_line_items' do
let(:container) { #container } # where container is a valid but unsaved Container
let(:location) { #container.location }
it 'gets items that are in the location and not excluded' do
container.not_findable_ids = [1,2]
# so it doesn't hit the database
expect(Item).to receive(:findable).with(location, container.not_findable_ids)
container.findable_items
end
end
This spec fails with the following error:
1) Container findable_line_items gets items that are in the location and not excluded
Failure/Error: container.findable_items
NoMethodError:
undefined method `except' for nil:NilClass
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/association_scope.rb:158:in `block (2 levels) in add_constraints'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/association_scope.rb:154:in `each'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/association_scope.rb:154:in `block in add_constraints'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/association_scope.rb:141:in `each'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/association_scope.rb:141:in `each_with_index'
# /[redacted]/activerecord-4.2.3/lib/active_record/associations/association_scope.rb:141:in `add_constraints'
# /[redacted]/activerecord-4.2.3/lib/active_record/associations/association_scope.rb:39:in `scope'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/association_scope.rb:5:in `scope'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/association.rb:97:in `association_scope'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/association.rb:86:in `scope'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/collection_association.rb:423:in `scope'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/collection_proxy.rb:37:in `initialize'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/relation/delegation.rb:106:in `new'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/relation/delegation.rb:106:in `create'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/collection_association.rb:39:in `reader'
# /[redacted]/gems/activerecord-4.2.3/lib/active_record/associations/builder/association.rb:115:in `pickable_items'
# ./spec/models/container_spec.rb:25:in `block (3 levels) in <top (required)>'
How would you get this spec to pass, without actually setting up an Item that meets all the requirements?
I ended up going with a solution like this:
describe 'findable_line_items' do
let(:container) { #container } # where container is a valid but unsaved Container
let(:location) { #container.location }
it 'gets items that are in the location and not excluded' do
# so it doesn't hit the database
expect(Item).to receive(:findable).with(location, container.not_findable_ids).and_call_original
expect(container).to receive(:location).and_call_original
expect(container).to receive(:not_findable_ids).and_call_original
container.findable_items
end
end
The error that was occurring was somewhere in the ActiveRecord association setup; it was trying to instantiate an ActiveRecord array on a nil object which was being returned from my Item stub. Adding .and_call_original solved that error.
I don't really care to test that the correct objects are being returned from this association, since that scope is being tested elsewhere, just that the scope is being used. It still hits the database in this scenario, but not the 15 times that would be required to set up a full test.
I keep getting the error ArgumentError: wrong number of arguments (0 for 1) for my default_scope which is default_scope { where("#{table_name}.tenant_id IS NULL") }
It keeps giving me this error and I don't understand why. I have the default scope in my users model.
Update:
Error output if using rails console:
ArgumentError: wrong number of arguments (0 for 1)
from /home/evan/Apps/demo-application/app/models/user.rb:18:in `hash'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activerecord-4.0.4/lib/active_record/scoping.rb:64:in `value_for'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activesupport-4.0.4/lib/active_support/per_thread_registry.rb:40:in `public_send'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activesupport-4.0.4/lib/active_support/per_thread_registry.rb:40:in `block in method_missing'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activerecord-4.0.4/lib/active_record/scoping/default.rb:123:in `ignore_default_scope?'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activerecord-4.0.4/lib/active_record/scoping/default.rb:134:in `evaluate_default_scope'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activerecord-4.0.4/lib/active_record/scoping/default.rb:110:in `build_default_scope'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activerecord-4.0.4/lib/active_record/relation.rb:554:in `with_default_scope'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activerecord-4.0.4/lib/active_record/relation.rb:582:in `exec_queries'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activerecord-4.0.4/lib/active_record/relation.rb:471:in `load'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activerecord-4.0.4/lib/active_record/relation.rb:220:in `to_a'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/activerecord-4.0.4/lib/active_record/relation.rb:573:in `inspect'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/railties-4.0.4/lib/rails/commands/console.rb:90:in `start'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/railties-4.0.4/lib/rails/commands/console.rb:9:in `start'
from /home/evan/.rvm/gems/ruby-2.0.0-p195/gems/railties-4.0.4/lib/rails/commands.rb:62:in `<top (required)>'
from bin/rails:4:in `require'
The hash is in my user model as below.
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.hash(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.hash(User.new_remember_token)
end
Error output if using rails server:
ArgumentError - wrong number of arguments (0 for 1):
activerecord (4.0.4) lib/active_record/scoping.rb:70:in `set_value_for'
activesupport (4.0.4) lib/active_support/per_thread_registry.rb:40:in `block in method_missing'
activerecord (4.0.4) lib/active_record/scoping/default.rb:127:in `ignore_default_scope='
activerecord (4.0.4) lib/active_record/scoping/default.rb:140:in `ensure in evaluate_default_scope'
activerecord (4.0.4) lib/active_record/scoping/default.rb:140:in `evaluate_default_scope'
activerecord (4.0.4) lib/active_record/scoping/default.rb:110:in `build_default_scope'
activerecord (4.0.4) lib/active_record/relation.rb:554:in `with_default_scope'
activerecord (4.0.4) lib/active_record/relation.rb:582:in `exec_queries'
activerecord (4.0.4) lib/active_record/relation.rb:471:in `load'
activerecord (4.0.4) lib/active_record/relation.rb:220:in `to_a'
activerecord (4.0.4) lib/active_record/relation/finder_methods.rb:316:in `find_take'
activerecord (4.0.4) lib/active_record/relation/finder_methods.rb:66:in `take'
activerecord (4.0.4) lib/active_record/relation/finder_methods.rb:49:in `find_by'
activerecord (4.0.4) lib/active_record/querying.rb:6:in `find_by'
app/helpers/sessions_helper.rb:16:in `current_user'
app/helpers/sessions_helper.rb:19:in `signed_in?'
And the relevant rails methods:
def ignore_default_scope? # :nodoc:
ScopeRegistry.value_for(:ignore_default_scope, self)
end
def ignore_default_scope=(ignore) # :nodoc:
ScopeRegistry.set_value_for(:ignore_default_scope, self, ignore)
end
You should avoid overwriting Ruby core methods like Object#hash, also considering that Object#hash is an essential method in Ruby. From the docs:
Generates a Fixnum hash value for this object. This function must have
the property that a.eql?(b) implies a.hash == b.hash.
The hash value is used along with eql? by the Hash class to determine
if two objects reference the same hash key. Any hash value that
exceeds the capacity of a Fixnum will be truncated before being used.
The hash value for an object may not be identical across invocations
or implementations of ruby. If you need a stable identifier across
ruby invocations and implementations you will need to generate one
with a custom method.
If you really have to overwrite core methods you should guarantee their functionality and do not change their arguments: you will get unexpected behaviours all over your app otherwise, like the error you're referring.
I think that you have not such a variable or method as table_name
I think this will work:
table name = "users"
default_scope { where("#{table_name}.tenant_id IS NULL") }
Also it is cleaner to use callable objects for scopes (they are waiting for it), so it is better to use lambda here:
table name = "users"
default_scope ->{ where("#{table_name}.tenant_id IS NULL") }
# or old syntax:
default_scope lambda { where("#{table_name}.tenant_id IS NULL") }
Am using the save option on mongoid on ruby on rails, its throwing me an error
can't convert Fixnum into String
my model file are
product.rb
class Product
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Versioning
field :product_role, :type => String
field :status_id, type: Moped::BSON::ObjectId
field :status_name,type: String
end
status.rb
class Status
include Mongoid::Document
field :name, type: String
index :name => 1
end
controller code is
product_id = "523741002a6c6e8d1a000002",
status_name = "Pending Review"
product_detail_status = Product.where(id:product_id).first
status = Status.where(name:status_name).first
puts "##############"
puts product_detail_status.id.to_s
puts status.name.to_s
puts status.id.to_s
puts "##############"
product_detail_status.status_name = status.name.to_s
product_detail_status.status_id = status.id
product_detail_status.save
i can able to view the values in puts, but its not saving by the way its throwing error on
product_detail_status.save as "can't convert Fixnum into String"
Completed 500 Internal Server Error in 657ms
TypeError - can't convert Fixnum into String:
mongoid (3.1.4) lib/mongoid/versioning.rb:53:in `revise'
activesupport (3.2.13) lib/active_support/callbacks.rb:407:in `
activesupport (3.2.13) lib/active_support/callbacks.rb:405:in `__run_callback'
activesupport (3.2.13) lib/active_support/callbacks.rb:385:in `_run_save_callbacks'
activesupport (3.2.13) lib/active_support/callbacks.rb:81:in `run_callbacks'
mongoid (3.1.4) lib/mongoid/callbacks.rb:130:in `run_callbacks'
mongoid (3.1.4) lib/mongoid/persistence/modification.rb:24:in `prepare'
mongoid (3.1.4) lib/mongoid/persistence/operations/update.rb:45:in `persist'
mongoid (3.1.4) lib/mongoid/persistence.rb:145:in `update'
mongoid (3.1.4) lib/mongoid/persistence.rb:82:in `save'
thanks in advance
I'm not sure why you are getting that error, but a better approach may be to use relations instead of manually adding a status_id and status_name to the product model
class Product
include Mongoid::Document
field :product_role, :type => String
has_one :status
end
Then in your controller, assign the Product's status as such
product_detail_status = Product.where(id:product_id).first
status = Status.where(name:status_name).first
product_detail_status.status = status
i found the solution, but am not sure its right way, on product model i remove
include Mongoid::Versioning
by that i can save it on product model by command save.
After upgrade to Rails 3.0.10 I'm getting strange error if I set config.cache_classes=true
NoMethodError (undefined method increment_counter' for ActiveRecord::Relation:Class):
activerecord (3.0.10) lib/active_record/associations.rb:1553:inbelongs_to_counter_cache_after_create_for_form'
activesupport (3.0.10) lib/active_support/callbacks.rb:424:in _run_create_callbacks'
activerecord (3.0.10) lib/active_record/callbacks.rb:277:increate'
activerecord (3.0.10) lib/active_record/persistence.rb:257:in create_or_update'
activerecord (3.0.10) lib/active_record/callbacks.rb:273:increate_or_update'
activesupport (3.0.10) lib/active_support/callbacks.rb:444:in _run_save_callbacks'
activerecord (3.0.10) lib/active_record/callbacks.rb:273:increate_or_update'
activerecord (3.0.10) lib/active_record/persistence.rb:60:in save!'
activerecord (3.0.10) lib/active_record/validations.rb:49:insave!'
activerecord (3.0.10) lib/active_record/attribute_methods/dirty.rb:30:in `save!'
...
I'm saving Answer model which has belongs_to relation with counter cache:
belongs_to :form, :counter_cache => true
Strangest thing is that after server restart first save works fine and after that I start getting such exceptions.
Maybe someone could advise where I could start digging for a cause?
The problem was caused by outdated acts_as_taggable_on_steroids gem which was causing class cache issues with Rails3. Switched to acts-as-taggable-on gem - problem solved.
Trying to queue a job with delayed_job as follows:
Delayed::Job.enqueue(BackgroundProcess.new(current_user, object))
current_user and object are not nil when I print them out. The weird thing is that sometimes refreshing the page or running the command again works!
Here is the exception trace:
Delayed::Backend::ActiveRecord::Job Columns (44.8ms) SHOW FIELDS FROM `delayed_jobs`
TypeError (wrong argument type nil (expected Data)):
/Users/.rvm/rubies/ruby-1.9.1-p378/lib/ruby/1.9.1/yaml.rb:391:in `emit'
/Users/.rvm/rubies/ruby-1.9.1-p378/lib/ruby/1.9.1/yaml.rb:391:in `quick_emit'
/Users/.rvm/rubies/ruby-1.9.1-p378/lib/ruby/1.9.1/yaml/rubytypes.rb:86:in `to_yaml'
vendor/plugins/delayed_job/lib/delayed/backend/base.rb:65:in `payload_object='
activerecord (2.3.9) lib/active_record/base.rb:2918:in `block in assign_attributes'
activerecord (2.3.9) lib/active_record/base.rb:2914:in `each'
activerecord (2.3.9) lib/active_record/base.rb:2914:in `assign_attributes'
activerecord (2.3.9) lib/active_record/base.rb:2787:in `attributes='
activerecord (2.3.9) lib/active_record/base.rb:2477:in `initialize'
activerecord (2.3.9) lib/active_record/base.rb:725:in `new'
activerecord (2.3.9) lib/active_record/base.rb:725:in `create'
vendor/plugins/delayed_job/lib/delayed/backend/base.rb:21:in `enqueue'
I would guess that it is caused by the fact that you send the objects as arguments to your jobs (at least I assume that current_user and object are in fact objects and not id's). Send the id's instead and start with loading the objects when perform starts.
For example:
Delayed::Job.enqueue(BackgroundProcess.new(current_user.id, object.id))
class BackgroundProcess < Struct.new(:user_id, :object_id)
def perform
#current_user = User.find(user_id)
#object = Object.find(object_id)
...
end
end
This way, it does not risk any problem with serializing an ActiveRecord into the database and you will always load the latest changes when the job is run.
Ran into this same problem as well. I still don't know what is causing it, but for some reason cloning the object seems to solve it
u = User.find 123
u.to_yaml
=> TypeError: wrong argument type nil (expected Data)
u.clone.to_yaml
=> works like normal
Very frustrating. Would be better to know the root cause, but this might help if you are desperate.