Rails/ruby undefined method - ruby-on-rails

I have simple class with class's and private's methods
class XmlConnection
def self.guitarmania
request = HTTParty.get(site_url)
handle_errors(request)
end
private
def handle_errors(request)
if request.code == 200
request
else
raise 'Connection error'
end
end
end
when i call XmlConnection.guitarmania i get
NoMethodError: undefined method 'handle_errors' for
XmlConnection:Class
How i can fix it?

When you're using a self. for a class method, all private methods also need to use self to be accessed from within it. The following will work:
class XmlConnection
def self.guitarmania
request = HTTParty.get(site_url)
handle_errors(request)
end
private
def self.handle_errors(request)
if request.code == 200
request
else
raise 'Connection error'
end
end
end
If all your methods will be class methods, you could wrap them all in a self to make it a bit more readable. This works exactly as above:
class XmlConnection
class << self
def guitarmania
request = HTTParty.get(site_url)
handle_errors(request)
end
private
def handle_errors(request)
if request.code == 200
request
else
raise 'Connection error'
end
end
end
end

You have to define handle_errors on the class as well:
def self.handle_errors(request)

Related

getting NoMethodError Undefined method in Ruby

getting NoMethodError Undefined method service_account_id under valid_restriction? method.
Can anybody check why am I getting this error?
If you have links to resolve this, that would be helpful too. Thanks.
Error:
ERROR:
<NoMethodError: undefined method `service_account_id' for #String:0x0000560784713130>
authentication_request.rb:19:in `valid_restriction?'
Code Snippet below:
module Authentication
module AuthnGcp
class DecodedToken
PROJECT_ID_TOKEN_CLAIM_NAME = "google/compute_engine/project_id"
INSTANCE_NAME_TOKEN_CLAIM_NAME = "google/compute_engine/instance_name"
SUB_TOKEN_CLAIM_NAME = "sub"
EMAIL_TOKEN_CLAIM_NAME = "email"
AUDIENCE_TOKEN_CLAIM_NAME = "aud"
attr_reader :project_id, :instance_name, :service_account_id, :service_account_email, :audience
def initialize(decoded_token_hash:, logger:)
#decoded_token_hash = decoded_token_hash
#logger = logger
initialize_required_claims
initialize_optional_claims
end
private
def initialize_required_claims
#audience = required_token_claim_value(AUDIENCE_TOKEN_CLAIM_NAME)
#service_account_id = required_token_claim_value(SUB_TOKEN_CLAIM_NAME)
end
def initialize_optional_claims
#service_account_email = optional_token_claim_value(EMAIL_TOKEN_CLAIM_NAME)
#project_id = optional_token_claim_value(PROJECT_ID_TOKEN_CLAIM_NAME)
#instance_name = optional_token_claim_value(INSTANCE_NAME_TOKEN_CLAIM_NAME)
end
def required_token_claim_value(required_token_claim)
required_token_claim_value = token_claim_value(required_token_claim)
if required_token_claim_value.nil? || required_token_claim_value.empty?
raise Errors::Authentication::Jwt::TokenClaimNotFoundOrEmpty, required_token_claim
end
log_claim_extracted_from_token(required_token_claim, required_token_claim_value)
required_token_claim_value
end
def optional_token_claim_value(optional_token_claim)
optional_token_claim_value = token_claim_value(optional_token_claim)
if optional_token_claim_value.nil? || optional_token_claim_value.empty?
optional_token_claim_value = nil
#logger.debug(LogMessages::Authentication::Jwt::OptionalTokenClaimNotFoundOrEmpty.new(optional_token_claim))
else
log_claim_extracted_from_token(optional_token_claim, optional_token_claim_value)
end
optional_token_claim_value
end
def token_claim_value(token_claim)
token_claim_path = token_claim.split('/')
#decoded_token_hash.dig(*token_claim_path)
end
def log_claim_extracted_from_token(token_claim, token_claim_value)
#logger.debug(
LogMessages::Authentication::Jwt::ExtractedClaimFromToken.new(
token_claim,
token_claim_value
)
)
end
end
end
end
==========================================================================
module Authentication
module AuthnGcp
# This class is responsible for retrieving the correct value from the GCP token
# of the requested attribute.
class AuthenticationRequest
def initialize(decoded_token:)
#decoded_token = decoded_token
end
def valid_restriction?(restriction)
token_value =
case restriction.name
when Restrictions::PROJECT_ID
#decoded_token.project_id
when Restrictions::INSTANCE_NAME
#decoded_token.instance_name
when Restrictions::SERVICE_ACCOUNT_ID
#decoded_token.service_account_id
when Restrictions::SERVICE_ACCOUNT_EMAIL
#decoded_token.service_account_email
end
raise Errors::Authentication::AuthnGcp::JwtTokenClaimIsMissing, restriction.name if token_value.blank?
token_value == restriction.value
end
end
end
end

Stubbing a class with a block as a parameter in constructor

If I have a class like
class MyClass
def initialize(&block)
#myBlock = block
end
def process
#...
#myBlock.call
#..
end
and I want to test a DifferentClass which needs to use MyClass and thus receive a block in the new.
How can I stub that block? What can I do in this situation?
describe DifferentClass do
it 'zomfg' do
allow(MyClass).to receive(:new) do |&block|
expect(block).to receive(:call)
my_object = double
allow(my_object).to receive(:process) { block.call }
my_object
end
foo = MyClass.new {}
# Here you should be passing the foo object to
# whatever method DifferentClass uses it in
foo.process
end
end

Ruby method as block

Currently there is a class in my Rails application which calls blocks as event handlers (callbacks):
class MyClass
#Event handler hooks:
def on_event1(&block)
#on_event1 = block
end
def on_event2(&block)
#on_event2 = block
end
#Event triggers:
def do_event1
#on_event1.call if #on_event1
end
def do_event2
#on_event2.call if #on_event2
end
end
Initializer:
mc = MyClass.new
#===== Event handlers: =====
mc.on_event1 do
#do some stuff
end
mc.on_event2 do
#do some stuff
end
I would like to put event handlers into separate class transforming them from blocks to methods:
class MyClassHandlers
def self.event1_handler
#do some stuff
end
def self.event2_handler
#do some stuff
end
end
How to call a method in a place where block should be given?
I would like to see event binding as something like:
mc = MyClass.new
mc.on_event1 = MyClassHandlers.event1_handler
mc.on_event2 = MyClassHandlers.event2_handler
You could use lambdas:
mc = MyClass.new
mc.on_event1 = lambda { MyClassHandlers.event1_handler }
mc.on_event2 = lambda { MyClassHandlers.event2_handler }

"method missing" error on Rails/Ruby metaprogramming attempt

I'm trying my first foray into metaprogramming and it's not going very well! It's a Rails 4.1 application and I'm trying to refactor an active record model (User) to combine two methods that are very similar. The original methods are slightly complex DB calls and work as expected.
The original code:
def retweet_count(league)
celebrity_ids = Roster.
where("user_id = ? and league_id = ?", self.id, league.id).
select(:celebrity_id).map { |r| r.celebrity_id }
Tweet.where({
tweet_date: league.start_date..league.end_date,
celebrity_id: celebrity_ids
}).select(:retweet_count).inject(0) do |sum, n|
sum + ( n.retweet_count || 0 )
end
end
def favorite_count(league)
celebrity_ids = Roster.
where("user_id = ? and league_id = ?", self.id, league.id).
select(:celebrity_id).map { |r| r.celebrity_id }
Tweet.where({
tweet_date: league.start_date..league.end_date,
celebrity_id: celebrity_ids
}).select(:favorite_count).inject(0) do |sum, n|
sum + ( n.favorite_count || 0 )
end
end
The new code:
twitter_stats_count :retweet, :favorite
private
def twitter_stats_count(*stats)
stats.each do |statistic|
stat = send(statistic).to_s
define_method "#{stat}_count" do |league|
celebrity_ids = Roster.
where("user_id = ? and league_id = ?", self.id, league.id).
select(:celebrity_id).map { |r| r.celebrity_id }
Tweet.where({
tweet_date: league.start_date..league.end_date,
celebrity_id: celebrity_ids
}).select("#{stat}_count").inject(0) do |sum, n|
sum + ( n.send("#{stat}_count") || 0 )
end
end
end
end
The error the new code produces when I try to start my rails server:
/Users/kiddo/.rvm/gems/ruby-2.1.0/gems/activerecord-4.1.0.rc2/lib/active_record/dynamic_matchers.rb:26:in `method_missing': undefined method `twitter_stats_count' for User (call 'User.connection' to establish a connection):Class (NoMethodError)
I can't seem to figure out what I'm doing wrong, so any pointers would be much appreciated!
FYI, here's the final code I got working. I mainly went with Holger Just's suggestions, but incorporated aspects from several others, so upvotes all around!
def team_ids(league)
Roster.where(user_id: self.id, league_id: league.id).pluck(:celebrity_id)
end
def self.twitter_stats_count(*stats)
stats.each do |statistic|
stat = statistic.to_s
define_method "#{stat}_count" do |league|
Tweet.where({
tweet_date: league.start_date..league.end_date,
celebrity_id: self.team_ids(league)
}).sum("#{stat}_count")
end
end
end
twitter_stats_count :retweet, :favorite
There are a couple of issues with your approach:
You call the twitter_stats_count directly on the class, not an instance of the class. As such, the method needs to be a class method. You can define it as a class method with
def self.twitter_stats_count(*stats)
# ...
end
Additionally, you call the method before having it defined. In Ruby, everything (even method definitions) are executed. As such, you can only call methods after they have been defined. Thus, you need to put the call to your twitter_stats_count method after its definition.
That looks quite complicated. If I'm not mistaken, you can reduce the duplication by refactoring your code:
def retweet_count(league)
league_tweets(league).sum(:retweet_count)
end
def favorite_count(league)
league_tweets(league).sum(:favorite_count)
end
def celebrity_ids(league)
Roster.where(user_id: self.id, league_id: league.id).pluck(:celebrity_id)
end
def league_tweets(league)
Tweet.where(
tweet_date: league.start_date..league.end_date,
celebrity_id: celebrity_ids(league)
)
end
twitter_stats_count should be a class method, but what you did is make it a instance method, maybe you can try this:
# no private here
def self.twitter_stats_count(*status)
#your codes here
end
You are getting this error because, you have define twitter_stats_count as a private method, You can't call this on self. You have to put it in a instance method, than call it.
Check this.
For example following gives same error:
class Foo
baz
private
def baz
puts "baz called"
end
end
However this will work:
class Foo
def dummy
baz
end
private
def baz
puts "baz called"
end
end
foo = Foo.new
foo.dummy

Creating a class method with Ruby problems

Why does the following code result in the error 'undefined local variable or method `foo_client' for Foo::People:Class'
class Foo::People
class << self
def get_account_balance(account_num)
foo_client.request :get_account_balance, :body => {"AccountNum" => account_num}
end
end
def foo_client
##client ||= Savon::Client.new do|wsdl, http|
wsdl.document = PEOPLE_SERVICE_ENDPOINT[:uri] + "?WSDL"
wsdl.endpoint = PEOPLE_SERVICE_ENDPOINT[:uri]
end
end
end
def get_account_balance is inside the class << self block, so it's a class method. def foo_client is not, so it's an instance method. So you can't call foo_client from get_account_balance because you're not calling it on an instance of People.

Resources