Rails association extension throws undefined method - ruby-on-rails

I'm trying to create an Active Record anonymous association extension has the documentation explains here. And I've got this models
class Program < ActiveRecord::Base
has_many :reports do
def asdf
"Hello extension"
end
end
end
class Report < ActiveRecord::Base
belongs_to :program
end
But when I test this on my rails c console I've an undefined method error.
Loading development environment (Rails 4.2.5)
2.1.0 :001 > Program.first.reports.asdf
Program Load (0.3ms) SELECT `programs`.* FROM `programs` ORDER BY `programs`.`id` ASC LIMIT 1
NoMethodError: Report Load (0.3ms) SELECT `reports`.* FROM `reports` WHERE `reports`.`program_id` = 1
undefined method `asdf' for #<Report::ActiveRecord_Associations_CollectionProxy:0x000001035e0098>
from /Users/mariowise/.rvm/gems/ruby-2.1.0/gems/activerecord-4.2.5/lib/active_record/relation/delegation.rb:136:in `method_missing'
from /Users/mariowise/.rvm/gems/ruby-2.1.0/gems/activerecord-4.2.5/lib/active_record/relation/delegation.rb:99:in `method_missing'
from (irb):1
from /Users/mariowise/.rvm/gems/ruby-2.1.0/gems/railties-4.2.5/lib/rails/commands/console.rb:110:in `start'
from /Users/mariowise/.rvm/gems/ruby-2.1.0/gems/railties-4.2.5/lib/rails/commands/console.rb:9:in `start'
from /Users/mariowise/.rvm/gems/ruby-2.1.0/gems/railties-4.2.5/lib/rails/commands/commands_tasks.rb:68:in `console'
from /Users/mariowise/.rvm/gems/ruby-2.1.0/gems/railties-4.2.5/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
from /Users/mariowise/.rvm/gems/ruby-2.1.0/gems/railties-4.2.5/lib/rails/commands.rb:17:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
I'm not trying to share the same extension between many associations, just trying to make this work through the anonymous module (block).
Any ideas?

That's because you retrieved many reports and called the method asdf on them, whereas your method asdf is defined for a single report.
Try this
Program.first.reports.first.asdf

Related

Elasticsearch - undefined method `as_indexed_json'

I'm able to do the bare bones keyword search with no issues in my app using elasticsearch, but as_indexed_json isn't working apparently and I have no idea why. I'm following this tutorial, and the section in question is almost halfway down.
Please let me know if I need to provide any additional info. I'm pretty new to this, so I apologize if this is a really dumb question.
Rails console:
2.2.4 :011 > Term.first.as_indexed_json
Term Load (0.4ms) SELECT "terms".* FROM "terms" ORDER BY "terms"."id" ASC LIMIT 1
NoMethodError: undefined method `as_indexed_json' for #<Term:0x007fa6cd9a6408>
from /Users/emplumb/.rvm/gems/ruby-2.2.4/gems/activemodel-4.2.5/lib/active_model/attribute_methods.rb:433:in `method_missing'
from (irb):11
from /Users/emplumb/.rvm/gems/ruby-2.2.4/gems/railties-4.2.5/lib/rails/commands/console.rb:110:in `start'
from /Users/emplumb/.rvm/gems/ruby-2.2.4/gems/railties-4.2.5/lib/rails/commands/console.rb:9:in `start'
from /Users/emplumb/.rvm/gems/ruby-2.2.4/gems/railties-4.2.5/lib/rails/commands/commands_tasks.rb:68:in `console'
from /Users/emplumb/.rvm/gems/ruby-2.2.4/gems/railties-4.2.5/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
from /Users/emplumb/.rvm/gems/ruby-2.2.4/gems/railties-4.2.5/lib/rails/commands.rb:17:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
Model:
require 'elasticsearch/model'
class Term < ActiveRecord::Base
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
def as_indexed_json(options = {})
as_json(
only: [:name, :definition, :etymology1, :etymology2, :uses, :romance_cognates, :notes1, :notes2, :quote1, :quote2]
)
end
end
Based on that tutorial i hope you opened the irb using rails c.
But open console like this bundle exec rails c and try again

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

ActiveRecord custom attributes

How can I assign a custom attribute to an ActiveRecord model?
For example, I can do it when querying via:
select("(CASE WHEN wishlist_items.id IS null THEN 0 ELSE 1 END) AS is_wishlisted_by_me").
This adds a custom is_wishlisted_by_me attribute. But what if I already have an instance of an AR model and I want to do:
model.is_wishlisted_by_me = true
this returns a NoMethodError: undefined method
NoMethodError: undefined method `is_wishlisted_by_me=' for #<SIImage:0x007fc8c41c7ef8>
from /Users/Test/.rvm/gems/ruby-1.9.3-p327/gems/activemodel-3.2.13/lib/active_model/attribute_methods.rb:407:in `method_missing'
from /Users/Test/.rvm/gems/ruby-1.9.3-p327/gems/activerecord-3.2.13/lib/active_record/attribute_methods.rb:149:in `method_missing'
from (irb):8
from /Users/Test/.rvm/gems/ruby-1.9.3-p327/gems/railties-3.2.13/lib/rails/commands/console.rb:47:in `start'
from /Users/Test/.rvm/gems/ruby-1.9.3-p327/gems/railties-3.2.13/lib/rails/commands/console.rb:8:in `start'
from /Users/Test/.rvm/gems/ruby-1.9.3-p327/gems/railties-3.2.13/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
I think you can do it with this way, define attr_accessible in your model:
attr_accessible :user_count
r = User.find_by_sql("select count(*) as user_count from users")
puts r.first.user_count
If a hash will do for you, you may try
User.connection.select_all("select count(*) as user_count from users")
which will return an array of hash like [{:user_count => 1}]
This is what I've done:
after_initialize do
self.is_wishlisted_by_me = nil unless #attributes.key?("is_wishlisted_by_me")
end
def is_wishlisted_by_me
#attributes["is_wishlisted_by_me"]
end
def is_wishlisted_by_me=(value)
#attributes["is_wishlisted_by_me"] = value
end

Simple database association issue in rails

Newbie rails developer here.
I'm having an issue trying to setup some very simple database associations in a new rails project.
In my database I have two tables, one called "Games" and one called "Onlines". Here's what's in them right now
Game.first
#<Game id: 1, name: "Game 1", description: "This is a cool game", url: "http://domain.com">
Online.first
#<Online id: 1, game_id: 1, now: 222>
I'm trying to setup a simple association so I can grab the number of users online by doing something like...
Game.find(1).onlines.now
In my game.rb and online.rb models I have
belongs_to :online
and
belongs_to :games
accordingly.
When I try and run Game.find(1).onlines.now in the rails console I get the following error.
NoMethodError: undefined method `onlines' for #<Game:0x00000101654300>
from /Users/Jon/.rvm/gems/ruby-1.9.2-p180#rails3tutorial/gems/activemodel-3.0.6/lib/active_model/attribute_methods.rb:367:in `method_missing'
from /Users/Jon/.rvm/gems/ruby-1.9.2-p180#rails3tutorial/gems/activerecord-3.0.6/lib/active_record/attribute_methods.rb:46:in `method_missing'
from (irb):5
from /Users/Jon/.rvm/gems/ruby-1.9.2-p180#rails3tutorial/gems/railties-3.0.6/lib/rails/commands/console.rb:44:in `start'
from /Users/Jon/.rvm/gems/ruby-1.9.2-p180#rails3tutorial/gems/railties-3.0.6/lib/rails/commands/console.rb:8:in `start'
from /Users/Jon/.rvm/gems/ruby-1.9.2-p180#rails3tutorial/gems/railties-3.0.6/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
Am I missing something ridiculously obvious here? I've tried everything I can think of.
You should have
has_many :onlines
in your Game model, not belongs_to.

Resources