getting NoMethodError Undefined method in Ruby - ruby-on-rails

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

Related

Rspec: Failure of spy to detect method call (my pattern is wrong)

How do I test that a method is being called within another method?
I need to test that ListingQuery.call() calls the base_filter method
Code
module ListingsQuery
class Search
def self.call(current_user, params, query_object_override=nil)
new(current_user, params, query_object_override).call
end
def initialize(current_user, params, query_object_override=nil)
#params = params
#type = params[:market] || params[:type] || "all"
#current_user = current_user
#type = if #type.present?
#type.downcase.singularize
else
"listing"
end
#query_object = query_object_override
end
def call
relation_for(query_object)
end
private
def relation_for(query_object_instance)
if query_object_instance.class == ListingsQuery::Car
car_filters(query_object_instance).relation
else
raise ArgumentError, "ListingsQuery 'Class: #{query_object_instance.class}' does not exist"
end
end
def car_filters(query_object)
base_filters(query_object)
other_filters(query_object)
query_object
end
def query_object
#query_object ||= query_object_for(#type, #current_user) # => ListingQuery::Car
end
end
end
### Test
current_user = spy("User")
query_object_class = ListingsQuery::Car
query_object = spy(query_object_class.name)
allow_any_instance_of(RSpec::Mocks::Double).to receive(:class) { query_object_class }
allow_any_instance_of(ListingsQuery::Search).to receive(:base_filters) { query_object }
so = ListingsQuery::Search.call(current_user, {}, query_object)
expect(so).to have_received(:base_filters)
Error
1) ListingsQuery::Search#car_filters should call base_filters
Failure/Error: expect(so).to have_received(:base_filters)
(Double "ListingsQuery::Car").base_filters(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
UPDATED ALSO FAILS:
current_user = spy("User")
query_object_class = ListingsQuery::Car
search_object_class = ListingsQuery::Search
query_object = spy(query_object_class.name)
allow_any_instance_of(RSpec::Mocks::Double).to receive(:class) { query_object_class }
allow_any_instance_of(search_object_class).to receive(:base_filters) { query_object }
expect(search_object_class).to have_received(:base_filters)
search_object_class.call(current_user, {}, query_object)
Error:
Failure/Error: expect(search_object_class).to have_received(:base_filters)
#<ListingsQuery::Search (class)> expected to have received base_filters, but that object is not a spy or method has not been stubbed.
I think you're assigning the variable so to the result of ListingsQuery::Search.call(current_user, {}, query_object). This might work:
so = ListingsQuery::Search
so.call(current_user, {}, query_object)
expect(so).to have_received(:base_filters)
or the way I normally write them:
so = ListingsQuery::Search
expect(so).to receive(:base_filters)
so.call(current_user, {}, query_object)
This works:
it "should call base_filters" do
current_user = spy("User")
query_object_class = ListingsQuery::Car
search_object_class = ListingsQuery::Search
query_object = spy(query_object_class.name)
allow_any_instance_of(RSpec::Mocks::Double).to receive(:class) { query_object_class }
so = search_object_class.new(current_user, {}, query_object)
allow(so).to receive(:base_filters) { query_object }
so.call
expect(so).to have_received(:base_filters)
end
For some reason allow_any_instance_of did not work, I had to stub the specific instance. I would be curious to know why, if anyone knows.

NoMethodError undefined method `[]=' for nil:NilClass

I created multiple classes with one test method to test wither the ruby objects get serialized correctly.
The error returned:
undefined method `[]=' for nil:NilClass
from /Users/horse/workspace/queryapi/app/models/query_model.rb:193:in `serialize'
I run the below test_query method through the rails console by initializing QueryModelTester and then invoking test_query() method on that object.
My code:
class QueryModelTester
def test_query
must = Must.new
range_criteria = RangeCriteria.new
range_criteria.gte = 20140712
range_criteria.lte = 1405134711
range = RangeBuilder.new
range.search_field = "created_time"
range.range_criteria = range_criteria
must.range = range
bool = Bool.new
bool.must = must
main_query = bool.serialize
puts main_query
end
end
Here are the model classes the above class is testing:
class RangeCriteria
#query_hash = Hash.new
attr_accessor :gte, :lte
def serialize
if(#gte.present?)
#query_hash[:gte] = #gte
end
if(#lte.present?)
#query_hash[:lte] = #lte
end
if(#gte.present? || #lte.present?)
return #query_hash
end
end
end
class RangeBuilder
#query_hash = Hash.new
attr_accessor :search_field, :range_criteria
def serialize
if(#search_field.present?)
#query_hash[#search_field] = #range_criteria.serialize
return #query_hash[:range] = #query_hash
end
end
end
class Bool
#query_hash = {}
attr_accessor :must
def serialize
if( #must.present? )
#query_hash[:must] = #must.serialize
return #query_hash[:bool] = #query_hash
end
end
end
The problem is when you initialize your #query_hash. In all your classes they are initialized in wrong scope. To fix it, you should move #query_hash = Hash.new to initialize method, like:
class RangeCriteria
def initialize
#query_hash = Hash.new
end
# ...
end
class RangeBuilder
def initialize
#query_hash = Hash.new
end
# ...
end
class Bool
def initialize
#query_hash = Hash.new
end
# ...
end
Hope that helps.
Good luck!

How to parse variable in wunderground api url with HTTPParty?

Use wunderground API to show weather forecast on my city pages.
city_controller.rb
def show
#region = Region.find(params[:region_id])
#city = City.find(params[:id])
#weather_lookup = WeatherLookup.new
end
weather_lookup.rb
class WeatherLookup
attr_accessor :temperature, :icon, :condition
def fetch_weather
HTTParty.get("http://api.wunderground.com/api/a8135a01b8230bfb/hourly10day/lang:NL/q/IT/#{#city.name}.xml")
end
def initialize
weather_hash = fetch_weather
end
def assign_values(weather_hash)
hourly_forecast_response = weather_hash.parsed_response['response']['hourly_forecast']['forecast'].first
self.temperature = hourly_forecast_response['temp']['metric']
self.condition = hourly_forecast_response['condition']
self.icon = hourly_forecast_response['icon_url']
end
def initialize
weather_hash = fetch_weather
assign_values(weather_hash)
end
end
show.html.haml(city)
= #weather_lookup.temperature
= #weather_lookup.condition.downcase
= image_tag #weather_lookup.icon
To fetch to correct weather forecast i thought that i can place the #city variable in the HTTParty.get URL as i did in the example, But i get the error message undefined method `name'
What am I doing wrong here?
If you need the city in WeatherLookup you are going to need to pass it to the initializer. Instance variables are only bound to their respective views.
#weather_lookup = WeatherLookup.new(#city)
attr_accessor :city # optional
def initialize(city)
#city = city
weather_hash = fetch_weather
end

Contextual Logging with Log4r

Here's how some of my existing logging code with Log4r is working. As you can see in the WorkerX::a_method, any time that I log a message I want the class name and the calling method to be included (I don't want all the caller history or any other noise, which was my purpose behind LgrHelper).
class WorkerX
include LgrHelper
def initialize(args = {})
#logger = Lgr.new({:debug => args[:debug], :logger_type => 'WorkerX'})
end
def a_method
error_msg("some error went down here")
# This prints out: "WorkerX::a_method - some error went down here"
end
end
class Lgr
require 'log4r'
include Log4r
def initialize(args = {}) # args: debug boolean, logger type
#debug = args[:debug]
#logger_type = args[:logger_type]
#logger = Log4r::Logger.new(#logger_type)
format = Log4r::PatternFormatter.new(:pattern => "%l:\t%d - %m")
outputter = Log4r::StdoutOutputter.new('console', :formatter => format)
#logger.outputters = outputter
if #debug then
#logger.level = DEBUG
else
#logger.level = INFO
end
end
def debug(msg)
#logger.debug(msg)
end
def info(msg)
#logger.info(msg)
end
def warn(msg)
#logger.warn(msg)
end
def error(msg)
#logger.error(msg)
end
def level
#logger.level
end
end
module LgrHelper
# This module should only be included in a class that has a #logger instance variable, obviously.
protected
def info_msg(msg)
#logger.info(log_intro_msg(self.method_caller_name) + msg)
end
def debug_msg(msg)
#logger.debug(log_intro_msg(self.method_caller_name) + msg)
end
def warn_msg(msg)
#logger.warn(log_intro_msg(self.method_caller_name) + msg)
end
def error_msg(msg)
#logger.error(log_intro_msg(self.method_caller_name) + msg)
end
def log_intro_msg(method)
msg = class_name
msg += '::'
msg += method
msg += ' - '
msg
end
def class_name
self.class.name
end
def method_caller_name
if /`(.*)'/.match(caller[1]) then # caller.first
$1
else
nil
end
end
end
I really don't like this approach. I'd rather just use the existing #logger instance variable to print the message and be smart enough to know the context. How can this, or similar simpler approach, be done?
My environment is Rails 2.3.11 (for now!).
After posting my answer using extend, (see "EDIT", below), I thought I'd try using set_trace_func to keep a sort of stack trace like in the discussion I posted to. Here is my final solution; the set_trace_proc call would be put in an initializer or similar.
#!/usr/bin/env ruby
# Keep track of the classes that invoke each "call" event
# and the method they called as an array of arrays.
# The array is in the format: [calling_class, called_method]
set_trace_func proc { |event, file, line, id, bind, klass|
if event == "call"
Thread.current[:callstack] ||= []
Thread.current[:callstack].push [klass, id]
elsif event == "return"
Thread.current[:callstack].pop
end
}
class Lgr
require 'log4r'
include Log4r
def initialize(args = {}) # args: debug boolean, logger type
#debug = args[:debug]
#logger_type = args[:logger_type]
#logger = Log4r::Logger.new(#logger_type)
format = Log4r::PatternFormatter.new(:pattern => "%l:\t%d - %m")
outputter = Log4r::StdoutOutputter.new('console', :formatter => format)
#logger.outputters = outputter
if #debug then
#logger.level = DEBUG
else
#logger.level = INFO
end
end
def debug(msg)
#logger.debug(msg)
end
def info(msg)
#logger.info(msg)
end
def warn(msg)
#logger.warn(msg)
end
def error(msg)
#logger.error(msg)
end
def level
#logger.level
end
def invoker
Thread.current[:callstack] ||= []
( Thread.current[:callstack][-2] || ['Kernel', 'main'] )
end
end
class CallingMethodLogger < Lgr
[:info, :debug, :warn, :error].each do |meth|
define_method(meth) { |msg| super("#{invoker[0]}::#{invoker[1]} - #{msg}") }
end
end
class WorkerX
def initialize(args = {})
#logger = CallingMethodLogger.new({:debug => args[:debug], :logger_type => 'WorkerX'})
end
def a_method
#logger.error("some error went down here")
# This prints out: "WorkerX::a_method - some error went down here"
end
end
w = WorkerX.new
w.a_method
I don't know how much, if any, the calls to the proc will affect the performance of an application; if it ends up being a concern, perhaps something not as intelligent about the calling class (like my old answer, below) will work better.
[EDIT: What follows is my old answer, referenced above.]
How about using extend? Here's a quick-and-dirty script I put together from your code to test it out; I had to reorder things to avoid errors, but the code is the same with the exception of LgrHelper (which I renamed CallingMethodLogger) and the second line of WorkerX's initializer:
#!/usr/bin/env ruby
module CallingMethodLogger
def info(msg)
super("#{#logger_type}::#{method_caller_name} - " + msg)
end
def debug(msg)
super("#{#logger_type}::#{method_caller_name} - " + msg)
end
def warn(msg)
super("#{#logger_type}::#{method_caller_name} - " + msg)
end
def error(msg)
super("#{#logger_type}::#{method_caller_name} - " + msg)
end
def method_caller_name
if /`(.*)'/.match(caller[1]) then # caller.first
$1
else
nil
end
end
end
class Lgr
require 'log4r'
include Log4r
def initialize(args = {}) # args: debug boolean, logger type
#debug = args[:debug]
#logger_type = args[:logger_type]
#logger = Log4r::Logger.new(#logger_type)
format = Log4r::PatternFormatter.new(:pattern => "%l:\t%d - %m")
outputter = Log4r::StdoutOutputter.new('console', :formatter => format)
#logger.outputters = outputter
if #debug then
#logger.level = DEBUG
else
#logger.level = INFO
end
end
def debug(msg)
#logger.debug(msg)
end
def info(msg)
#logger.info(msg)
end
def warn(msg)
#logger.warn(msg)
end
def error(msg)
#logger.error(msg)
end
def level
#logger.level
end
end
class WorkerX
def initialize(args = {})
#logger = Lgr.new({:debug => args[:debug], :logger_type => 'WorkerX'})
#logger.extend CallingMethodLogger
end
def a_method
#logger.error("some error went down here")
# This prints out: "WorkerX::a_method - some error went down here"
end
end
w = WorkerX.new
w.a_method
The output is:
ERROR: 2011-07-24 20:01:40 - WorkerX::a_method - some error went down here
The downside is, via this method, the caller's class name isn't automatically figured out; it's explicit based on the #logger_type passed into the Lgr instance. However, you may be able to use another method to get the actual name of the class--perhaps something like the call_stack gem or using Kernel#set_trace_func--see this thread.

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