I want to stub set_user_tokens which is executed on the initialized (not saved) ActiveRecord object. This method assigns a token to the login object.
class AwareLogin < Authenticatable
def authenticate!
login = Login.find_or_initialize_for_authentication(params[:login][:email], 'aware')
class << login
include AwareAuth
end
if login.valid_password?(password) || (set_token = login.id.nil? && aware_response.success?)
login.set_user_tokens(aware_response) if set_token
success!(login)
else
aware_response.success? ? fail!(:aware_auth) : raise(ActiveRecord::Rollback)
end
end
end
end
So I want to stub setu_user_tokens method:
login.set_user_tokens(aware_response) if set_token
to receive login ActiveRecord object with attributes of oauth_tokens like below:
login.oauth_token
=> {"access_token" => return_token,"refresh_token" => nil,"token_expiration" => 1200 }
I've tried:
allow_any_instance_of(Login).to receive(:set_user_tokens).with(status: 200, body: { access_token: return_token }.to_json, success?: true).and_return(
oauth_tokens: {
"access_token" => return_token,
"refresh_token" => nil,
"token_expiration" => 1200 },
)
But I'm getting an error:
Login does not implement #set_user_tokens
I would be willing to bet your issue is set_user_tokens is part of AwareAuth.
Since you are only including this module in the eigenclass of the instance (login) as part of the AwareLogin#authenticate! method, the Login class does not implement that method at any point in time.
Is there a reason you are doing it this way rather than just including AwareAuth in the Login class in the first place?
Either way, while your question appears to lack context for the test itself, if I understand correctly, we should be able to resolve these issues as follows:
it 'sets user tokens' do
login = Login
.find_or_initialize_for_authentication('some_email#example.com', 'aware')
.tap {|l| l.singleton_class.send(:include, AwareAuth) }
allow(Login).to receive(:find_or_initialize_for_authentication).and_return(login)
allow(login).to receive(:set_user_tokens).and_return(
oauth_tokens: {
"access_token" => return_token,
"refresh_token" => nil,
"token_expiration" => 1200 }
)
#perform your action and expectations here
end
By using partial doubles you can stub the specific methods you need to without impacting any other functionality of the object itself.
I have a Notifications module which have classes like 1)car 2)bike 3)Aeroplane. I have a serialized column in UserFeature model.And I have a module 'Notifications' which has list of 11 classes in it.
Notifications
1)car
2)bike
3)Aeroplane
The hash structure of the column notifications in UserFeature model must be
{:car => {:mirror => :true, :door => :true}
:bike => {:x=> :true, :x => :true}
:Aeroplane => {:p => :true, :q => :true}
}
I can access user_object.Notifications
But so as to access user_object.car and also user_object.mirror I need to write getter/setter methods { Defining getter/setter dynamically because I dont want to write getter/setter for every method and also I am unsure about the number of methods I have -> which in future may extend }
Notifications.constants.each do |notification_class|
class_methods = "Notifications::#{notification_class}".constantize.methods(false)
class_methods.each do |method|
method_name = method[0..-4].split('(')[0]
setter_getter_name = "#{notification_class.to_s.underscore}_#{method_name}"
define_method("#{setter_getter_name}=") do |value|
self.notifications = GlobalUtils.form_hash(self.notifications, "#{notification_class}".to_sym, "#{method_name}".to_sym)
self[:notifications]["#{notification_class}".to_sym][ "#{method_name}".to_sym] = value
end
define_method("#{setter_getter_name}") do
self.notifications.fetch("#{notification_class_name}".to_sym, {}).fetch("#{method_name}".to_sym)
end
end
end
But still when i try to access user_object.mirror,
undefined method for #<UserFeature000043645345>
What I am doing wrong?
I need to do this using getter/setter method only
An OpenStruct is a data structure, similar to a Hash, that allows the definition of arbitrary attributes with their accompanying values. This is accomplished by using Ruby’s metaprogramming to define methods on the class itself.
example:
require 'ostruct'
hash = { "country" => "Australia", :population => 20_000_000 }
data = OpenStruct.new(hash)
p data # -> <OpenStruct country="Australia" population=20000000>
Use Ruby OpenStruct class. It will fulfill your requirements without defining such bunch of code.
Edit1, example:
require 'ostruct'
class Aeroplane < OpenStruct; end
a = Aeroplane.new(:p => :true, :q => :true)
a.p # => true
I have the following method in my model which uses find_or_create_by to find or create a new product.
def self.save_prod(product)
Product.find_or_create_by_prod_id(product)
product_data = ItemData.get_product_data(product)
p.update_attributes(
:prod_id => product,
:upc => product_data[:upc],
:title => product_data[:title]
)
end
The ItemData.get_product_data() method is a module method which calls an API to fetch product data:
def self.get_product_data(product)
url_raw = URI.parse("http://www.api.com/v1/itemid=#{product}")
url = Net::HTTP.get_response(url_raw).body
#resp = JSON.parse(url)
#title = Sanitize.clean(#resp["serviceResult"]["itemName"]).strip
#upc = #resp["serviceResult"]["uPC"]
{:title => #title, :upc => #upc}
end
This works as expected, however I know it can be a LOT more efficient, by not calling the ItemData.get_product_data() method every time the save_prod() method is called. How can I add new product data without having to call the ItemData.get_product_data() if a product already exists.
Another way to doing it. This would return the Product object if it is already present otherwise it will create it from api and return the new object.
def self.save_prod(product)
Product.find_by_prod_id(product) || Product.create( ItemData.get_product_data(product) )
end
Modify the api call to return a hash with prod_id. Not sure why you are converting title and upc to class variables here. It could lead to problems if they are used extensively.
def self.get_product_data(product)
url_raw = URI.parse("http://www.api.com/v1/itemid=#{product}")
url = Net::HTTP.get_response(url_raw).body
#resp = JSON.parse(url)
#title = Sanitize.clean(#resp["serviceResult"]["itemName"]).strip
#upc = #resp["serviceResult"]["uPC"]
{:title => #title, :upc => #upc, :prod_id => product}
end
Instead of doing a find or create use find or initialize by . Change your code to following :
prod = find_or_initialize_by_prod_id(product)
if prod.new_record?
prod.save!
product_data = ItemData.get_product_data(product)
prod.update_attributes(
:prod_id => product,
:upc => product_data[:upc],
:title => product_data[:title]
)
end
by using find_or_initalize you can distinguish whether the record was created or found by using new_record method. If new you can save and make an API call and do whatever you want.
I have been using ruby to make API calls and operating strictly in the terminal for some time. I am now in the process of learning more about rails and trying to get out of my terminal. How can I, using rails 4.0, put a variable to the screen from an already existing .rb file? I am confused as to where I should write the API request to get the variable- Is it a controller, can I write it directly in a view, etc.
Sample idea:
#test.rb
call= "/api/v2/surveys/"
auth = {:username => "test", :password => "password"}
url = HTTParty.get("https://surveys.com#{call}",
:basic_auth => auth,
:headers => { 'ContentType' => 'application/json' } )
response = JSON.parse(url.body)
survey_ids = response["surveys"].map { |s| s["id"] }
survey_ids.each do |i|
puts i
end
That is a sample .rb script I already have. The difference is I would like for puts i to happen on a web app when a page is loaded instead of me running the script in my terminal. What would I use in rails to make that happen?
It depends entirely on how your application is going to be set up but here's a basic example:
Say you have a Survey model:
class Survey < ActiveRecord::Base
attr_accessible :survey_id
end
You can place your call for a list of surveys (I'm assuming that's what your code does) in the SurveysController:
class SurveysController < ApplicationController
def index
#surveys = Survey.all
end
def show
#survey = Survey.find(params[:id])
end
def pull_surveys
call= "/api/v2/surveys/"
auth = {:username => "test", :password => "password"}
url = HTTParty.get("https://surveys.com#{call}",
:basic_auth => auth,
:headers => { 'ContentType' => 'application/json' } )
response = JSON.parse(url.body)
survey_ids = response["surveys"].map { |s| s["id"] }
survey_ids.each do |i|
Survey.create(survey_id: i)
end
end
After calling the pull_surveys method, you'll actually have surveys your view can load so in your views for the Survey Model you can use #surveys or #survey (depending on which view you're in) and serve up whatever you want (e.g #survey.survey_id in show to show that specific survey's ID).
Note that you'll want to be careful about where you place your API call methods - I placed it in the controller for simplicity's sake but you may not want to do this.
There's lots of useful info in the rails guides to get you started: http://guides.rubyonrails.org/index.html
I have only a vague idea on phrasing this, so question as needed:
I have a set of values I'm passing in my rails controller on a regular basis to a widget that differs slightly from page to page, from what I pass to it. This is is starting to get unwieldy for every controller, so I added a small class to help concatenate that process a bit (basic starting gist below).
#return dashcontroller hash from more succinct cues
module DashControl
class DashControl
attr_accessor :title, :instance, :actions
def initialize(title='default title', instance='default instance', actions={})
#title = title
#instance = instance
initialize_actions(actions)
end
def initialize_actions(actions)
actions.kind_of?(Hash) ? #actions = actions : initialize_tag(actions)
end
def initialize_tag(tag)
case tag
when :manage_default
#actions = {:statusactions => [],
:formactions => [ ['#tabaccount', 'addaccount'],
['#tabuser', 'addusers'],
['#tabadd','adddomain'] ],
:linkactions => [ [] ],
:filteractions => [ [] ] }
when :none
#actions = {}
#when
# #actions = {}
else
#actions = #actions
end
end
def dashcontroller
{:title => #title, :instance => #instance, :actions => #actions }
end
end
end
So basically I just need to pass an instance of this.dashcontroller and I get the hash I need with a lot less chaos in my controllers . The issue is with the #instance variable. I want to pass in the instance I'm using e.g. #book, #account, etc, and have it come out as #book, #account, etc. Instead, I get the contents of whatever I put into there as :instance => (contents of that instance). It doesn't seem right to me as before I was just using e.g. #account, and then using that, but looking at it might not make any sort of difference in the widget, as I juggle things and work on my code-fu.
Basically my question is how to push an instance variable through a class like this, and still have it accessibile as it went in without having to do any backflips and transformations on the other side. There is probably a better way, but this is what I'm working with at the moment.
edit: pseudo-code
DashControl::DashControl.new("Catchy Title", #book, :none).dashcontroller
#=> {:title => "Catchy Title", :instance => #book, :actions => {} }
I think I can work with it, like I said its more an issue of my understanding of how things flow than an actual bug or anything difficult. I'd like to not have to do more gymnastics on the other end with the instance stuff, though the contents are there and that is all I really need, I just need some input on thinking it through to be less of a mess. I really need to refine what I'm sending through this, or use this to further refine what I'm sending on is the bottom line lesson to take away right now.
edit:
I ended up tossing this, but it was a learning experience...I went back the widget and I know more than when I originally set up the widget, so I've been able to set that up to take only the instance variable and bootstrap what it needs without adding another class, cleaning up my controllers and handing a lot back to the widget where I suspect it should/could have been to start.
Based on your code and example, this fits:
# No need to put a class in a namespace of the same name, just make the module a class
# Also, if you inherit from a struct, it can save you a lot of typing. It defines the setters and getters for you.
class DashControl < Struct.new(:title, :instance, :actions)
# since it looks like you always access it the same way, create a class method
# which does this initialization and invocation
def self.for(*args)
new(*args).dashcontroller
end
def initialize(title='default title', instance='default instance', actions=:none)
# here, we can use our own defaults and normalization and pass the results up to the struct
super title, instance, normalize(actions)
end
# didn't make sense to call this initialize_tag, as it was initializing actions
# also there was already an initialize actions method which just checked for the case of a hash
# but then elsewhere you checked for other things. Better to just put it all in one method and return it
# (then you aren't setting it every time you want to ask it to calculate that value)
# also using guard clauses (the if statements that return early) instead of the case, as they are easier to understand
def normalize(actions)
return Hash.new if actions == :none
return actions unless actions == :manage_default
default_actions
end
# the value of default_actions is complicated and noisy, separate it out to its own method
# this prevents it from cluttering the code around it, and also allows us to access,
# and to do this without the side effects of setting values.
def default_actions
{ :statusactions => [],
:formactions => [ ['#tabaccount', 'addaccount'],
['#tabuser', 'addusers'],
['#tabadd','adddomain'] ],
:linkactions => [ [] ],
:filteractions => [ [] ] }
end
# use the getters instead of the ivars (I consider this a generally best practice -- and you could have
# done it before, since you declared the attr_accessor, even though I'm accessing it through the struct)
def dashcontroller
{:title => title, :instance => instance, :actions => actions }
end
end
DashControl.for # => {:title=>"default title", :instance=>"default instance", :actions=>{}}
DashControl.for('Catchy Title', '#book', :none) # => {:title=>"Catchy Title", :instance=>"#book", :actions=>{}}
DashControl.for('Catchy Title', '#book', :manage_default) # => {:title=>"Catchy Title", :instance=>"#book", :actions=>{:statusactions=>[], :formactions=>[["#tabaccount", "addaccount"], ["#tabuser", "addusers"], ["#tabadd", "adddomain"]], :linkactions=>[[]], :filteractions=>[[]]}}
DashControl.for('Catchy Title', '#book', a: 'b') # => {:title=>"Catchy Title", :instance=>"#book", :actions=>{:a=>"b"}}
DashControl.for('Catchy Title', '#book', 123) # => {:title=>"Catchy Title", :instance=>"#book", :actions=>123}