Is it possible to validate on custom method? - ruby-on-rails

I'm trying to use some validation only if a specific method in my controller is being called:
validates_presence_of :reasons, :on => :update_description
However I get this error:
TypeError in RegistrationsController#create
nil is not a symbol
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/validations.rb:586:in `send'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/validations.rb:586:in `validates_presence_of'
/Users/blah/Desktop/testApp/app/models/registration.rb:6
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:380:in `load_without_new_constant_marking'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:380:in `load_file'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:521:in `new_constants_in'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:379:in `load_file'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:259:in `require_or_load'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:425:in `load_missing_constant'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:80:in `const_missing'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:92:in `const_missing'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:437:in `load_missing_constant'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:96:in `const_missing'
/Users/blah/Desktop/testApp/app/controllers/registrations_controller.rb:81:in `create'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/base.rb:1331:in `send'
Am I going about this the wrong way? Basically I have a multi-page form and I've broken up the pages into multiple update methods which they submit. In this case I'm updating the registration object using a method I've defined called update_description. I only want the validation to occur when this method is called. Possible?
Update:
Adding error line:
def create
#registration = Registration.new(params[:registration]) //error is here
[nav logic]
end

The :on parameter specifies when this validation is active (default is :save, other options :create, :update). This is relative to the model, not the controller.

It looks like you want a wizard plugin. The two that I know of are:
acts_as_wizard
wizardly
Hopefully these should get you started.

Related

Rails monkey patch only on certain columns

I am trying to include a module only if the subclass has a certain column,
and I am doing this in an initializer:
class ActiveRecord::Base
def self.inherited(subclass)
subclass.include(MultiTenancy) if subclass.new.respond_to?(:tenant_id)
end
end
I keep getting this error:
NoMethodError: undefined method `[]' for nil:NilClass
Here is the module that I am importing:
module MultiTenancy
class TenantNotSetError < StandardError ; end
def self.included(model)
model.class_eval do
belongs_to :tenant
validates :tenant_id, presence: true
default_scope -> {
raise TenantNotSetError.new unless Tenant.current_tenant
where(tenant_id: Tenant.current_tenant.id)
}
def multi_tenanted?
true
end
end
end
end
What am I doing wrong. I can include the module on any subclass separate eg. Klass.include(MultiTenancy) successfully, so the problem is with the initializer.
Below is the stack trace:
from -e:1:in `<main>'2.1.1 :002 > Klass.all
NoMethodError: undefined method `[]' for nil:NilClass
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activerecord-4.1.4/lib/active_record/relation/delegation.rb:9:in `relation_delegate_class'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activerecord-4.1.4/lib/active_record/relation/delegation.rb:112:in `relation_class_for'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activerecord-4.1.4/lib/active_record/relation/delegation.rb:106:in `create'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activerecord-4.1.4/lib/active_record/model_schema.rb:133:in `table_name='
from /home/lee/Code/mobifit/app/models/klass.rb:25:in `<class:Klass>'
from /home/lee/Code/mobifit/app/models/klass.rb:22:in `<top (required)>'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:443:in `load'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:443:in `block in load_file'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:633:in `new_constants_in'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:442:in `load_file'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:342:in `require_or_load'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:480:in `load_missing_constant'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:180:in `const_missing'
from (irb):2
from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/console.rb:90:in `start'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/console.rb:9:in `start'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/commands_tasks.rb:69:in `console'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands.rb:17:in `<top (required)>'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:247:in `require'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:247:in `block in require'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:232:in `load_dependency'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:247:in `require'
from /home/lee/Code/mobifit/bin/rails:8:in `<top (required)>'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:241:in `load'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:241:in `block in load'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:232:in `load_dependency'
from /home/lee/.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:241:in `load'
from /home/lee/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from /home/lee/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from -e:1:in `<main>'2.1.1 :003 >
If you follow the stack trace back a bit, you can see that the thing that's nil that isn't supposed to be is is the class's #relation_delegate_cache. Looking at the source, you can see that this normally gets initialized in the initialize_relation_delegate_cache method, which is called in the inherited hook for ActiveRecord::Delegation::DelegateCache.
ActiveRecord::Base extends this module, so normally a class that inherits from ActiveRecord::Base would call this inherited hook. So the problem with your code is that you're monkey-patching inherited on ActiveRecord::Base, which means that the hook in ActiveRecord::Delegation::DelegateCache is no longer being called.
Two comments:
This is why it's a bad idea to monkey-patch, especially something as fundamental and complex as active record. Is it possible to do what you want to do in a more straightforward way?
If it's not, you should take some notes on how ActiveRecord::Delegation::DelegateCache works and rewrite your monkey patch to mix in a module of your own, rather than just over-writing ActiveRecord::Base's inherited method.
The suggestion in (2) would look something like this:
module MultitenancyConcern
def self.inherited(subclass)
subclass.include(MultiTenancy) if subclass.column_names.include?("tenant_id")
super #this is critical for avoiding the error you're getting
end
end
then your monkey-patch becomes just
ActiveRecord::Base.extend(MultitenancyConcern)

Rails before_destory only works with destroy and not with delete

I am building a rails applications, where in a part of it, I save to the database, payments information. I need to make these payments un-deleteable.
I have created a before_destroy function that kinda works.. but I am having an issue:
here is my code:
class StripePayment < ActiveRecord::Base
belongs_to :user
belongs_to :stripe_card
before_destroy :fail
private
def fail
return false
end
end
When I create a payment and try out my code when delete:
StripePayment.first.destroy returns false and rollback... Which is exactly what I want.
However,
StripePayment.first.delete passes and deletes the object.
I know the deference between delete and destroy. however, I want to be able to prevent this object from being deleted on the DB (on both delete() and destroy() calls.
I tried before_delete and rails gave me back this error:
NoMethodError: undefined method `before_delete' for #<Class:0x007fc1abc37c50>
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-4.0.0/lib/active_record/dynamic_matchers.rb:22:in `method_missing'
from /Users/alybadawy/developing/repos/finish/finish/app/models/stripe_payment.rb:7:in `<class:StripePayment>'
from /Users/alybadawy/developing/repos/finish/finish/app/models/stripe_payment.rb:1:in `<top (required)>'
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-4.0.0/lib/active_support/dependencies.rb:423:in `load'
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-4.0.0/lib/active_support/dependencies.rb:423:in `block in load_file'
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-4.0.0/lib/active_support/dependencies.rb:615:in `new_constants_in'
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-4.0.0/lib/active_support/dependencies.rb:422:in `load_file'
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-4.0.0/lib/active_support/dependencies.rb:323:in `require_or_load'
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-4.0.0/lib/active_support/dependencies.rb:462:in `load_missing_constant'
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-4.0.0/lib/active_support/dependencies.rb:183:in `const_missing'
from (irb):1
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/railties-4.0.0/lib/rails/commands/console.rb:90:in `start'
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/railties-4.0.0/lib/rails/commands/console.rb:9:in `start'
from /Users/alybadawy/.rvm/gems/ruby-2.0.0-p247/gems/railties-4.0.0/lib/rails/commands.rb:64:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
Any help will be appreciated. Thanks :)
The easiest way is, to define your own delete method
class StripePayment < ActiveRecord::Base
belongs_to :user
belongs_to :stripe_card
before_destroy :fail
def delete
false
end
private
def fail
return false
end
end
If you have conditions, where you want to allow deleteion, you can check for it iy your delete method and call super
that's because delete is one of the functions that skips callbacks in rails
check this out for more info on the subject http://edgeguides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks

undefined method `yaml' for nil:NilClass when trying to save duplicated object

I have an Active Record model object called Event, which has many event_things. I want to be able to duplicate the event, so that it gets a new id. I'm using Rails 3.2 and in rails console am able to successfully call
event_copy = event.dup
event_copy.save
However, I also want to duplicate each of the Event's event_things.
copy = event_thing.dup
copy.event_id = event_copy.id
copy.save
But that gives me this error stack:
NoMethodError: undefined method `yaml' for nil:NilClass
from /Users/Ed/.rvm/rubies/ruby-1.9.3-p125/lib/ruby/1.9.1/psych.rb:204:in `dump_stream'
from /Users/Ed/.rvm/rubies/ruby-1.9.3-p125/lib/ruby/1.9.1/psych/core_ext.rb:35:in `psych_y'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activemodel-3.2.6/lib/active_model/dirty.rb:143:in `attribute_change'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activemodel-3.2.6/lib/active_model/dirty.rb:117:in `block in changes'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activemodel-3.2.6/lib/active_model/dirty.rb:117:in `map'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activemodel-3.2.6/lib/active_model/dirty.rb:117:in `changes'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/attribute_methods/dirty.rb:23:in `save'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/transactions.rb:241:in `block (2 levels) in save'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/transactions.rb:295:in `block in with_transaction_returning_status'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/transactions.rb:208:in `transaction'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/transactions.rb:293:in `with_transaction_returning_status'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/transactions.rb:241:in `block in save'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/transactions.rb:252:in `rollback_active_record_state!'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/transactions.rb:240:in `save'
from (irb):18
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.6/lib/rails/commands/console.rb:47:in `start'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.6/lib/rails/commands/console.rb:8:in `start'
from /Users/Ed/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.6/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
I ran into the same issue as this. It turns out that there's a Kernel.y extension method that is getting in your way, based on this article. Here is what I did to fix my problem:
class Event < ActiveRecord::Base
# alias the attributes
alias_attribute :x_pos, :x
alias_attribute :y_pos, :y
after_initialize :init
# after_initialize will still work, with the aliases
def init
self.x_pos ||= 0
self.y_pos ||= 0
end
# override Kernel.y
def y; end
# override self.y_pos to read out of the :attributes, since we blew away :y
def y_pos
attribute['y']
end
end
After doing this, I was finally able to use my model without having to rename the y database column.
While I normally don't advocate using gems liberally, if you have a lot of relations that need to be preserved in the duplication, or find yourself doing these manual duplications a lot, take a look at deep_clonable.
As for your specific error, try surrounding each line of code with a nil check (one line at a time). I'm sure there's just some case where you're ending up with a nil object that you're overlooking.

Rails ActiveSupport Issue with state gems for notifications

I have installed multiple state_machine gems to my app to use them for a notification system but every time I run into an ActiveSupport issue. It usually looks something almost identical to this:
>> m = Message.new
TypeError: wrong argument type nil (expected Module)
from /home/Ryan/appname/app/models/message.rb:2:in `include'
from /home/Ryan/appname/app/models/message.rb:2
from /home/Ryan/.bundle/ruby/1.8/gems/activesupport-2.3.9/lib/active_sup
port/dependencies.rb:406:in `load_without_new_constant_marking'
from /home/Ryan/.bundle/ruby/1.8/gems/activesupport-2.3.9/lib/active_sup
port/dependencies.rb:406:in `load_file'
from /home/Ryan/.bundle/ruby/1.8/gems/activesupport-2.3.9/lib/active_sup
port/dependencies.rb:547:in `new_constants_in'
from /home/Ryan/.bundle/ruby/1.8/gems/activesupport-2.3.9/lib/active_sup
port/dependencies.rb:405:in `load_file'
from /home/Ryan/.bundle/ruby/1.8/gems/activesupport-2.3.9/lib/active_sup
port/dependencies.rb:285:in `require_or_load'
from /home/Ryan/.bundle/ruby/1.8/gems/activesupport-2.3.9/lib/active_sup
port/dependencies.rb:451:in `load_missing_constant'
from /home/Ryan/.bundle/ruby/1.8/gems/activesupport-2.3.9/lib/active_sup
port/dependencies.rb:106:in `rake_original_const_missing'
from /usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2503:in `const_m
issing'
from /home/Ryan/.bundle/ruby/1.8/gems/activesupport-2.3.9/lib/active_sup
port/dependencies.rb:118:in `const_missing'
from (irb):2
I'm on a windows 7 machine using activerecord, bundler to install the gems, and rails 2.3.9..where am I going wrong? Is there some modification to the database I need to be making?
Edit: message.rb
include AlterEgo # include this first
include AlterEgo::ActiveRecordAdapter
state :unread, :default => true do
handle :state do
"unread"
end
transition :to => :read, :on => :view!
end
state :read do
handle :state do
"read"
end
end
I'm not positive, but I am having trouble adapting this for activerecord. do i need to create a new database?
EDIT
I ended up just using oldschool acts_as_state_machine since this is such a simple implementation

problem with uninitialized constant

I have the following controller
class ActiveUsersController < ApplicationController
def edit
end
end
And my routes.rb is like this:
map.resources :active_users
When I try to access the controller using the url http://localhost:3000/active_users/COo8e45RqQAHr6CqSCoI/edit I got the following error:
NameError in Active usersController#edit
uninitialized constant ActiveUsersController
RAILS_ROOT: /Users/vintem/Documents/Projetos/Pessoal/bugfreela
Application Trace | Framework Trace | Full Trace
/Users/vintem/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:443:in `load_missing_constant'
/Users/vintem/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:80:in `const_missing'
/Users/vintem/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:92:in `const_missing'
/Users/vintem/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/inflector.rb:361:in `constantize'
/Users/vintem/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/inflector.rb:360:in `each'
/Users/vintem/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/inflector.rb:360:in `constantize'
/Users/vintem/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/core_ext/string/inflections.rb:162:in `constantize'
/Users/vintem/.gem/ruby/1.8/gems/actionpack-2.3.5/lib/action_controller/routing/route_set.rb:443:in `recognize'
/Users/vintem/.gem/ruby/1.8/gems/actionpack-2.3.5/lib/action_controller/routing/route_set.rb:436:in `call'
Can anyone help me?
Thanks
resources refers to the model and an assumed correlate controller named similarly. Do you have an ActiveUser model class? Or is it something else, say User? E.g.:
map.resources users, :controller => "active_users"
Check out the API docs:
http://api.rubyonrails.org/classes/ActionController/Resources.html
Hard to tell exactly what's wrong from the info you've provided.

Resources