I am using Rails 3. I was coding a controller very verbose. So i am trying refactoring the controller.
I coded a class called ProductMaker which make a product and modify session (product task for wizard form as current_step, if the request is a refresh, etc)
This class has method that receiving session as parameter, modify and then return this new session.
Controller action:
def new
#INITIALIZE CODE
session[:refresh] ||= SortedArray.new [1]
#...MORE CODE
end
def create
#...MUCH CODE
unless Utilities.is_refresh(session[:refresh])
#...more code
session = ProductMaker.some_method_which_return_session(session) #KEY PROBLEM LINE
#...more code
end
#... MORE CODE
end
My ProductMaker class in lib folder:
class ProductMaker
def self.some_method_which_return_session(session)
session[:any_key] = "some value"
return session
end
end
However when I write the KEY PROBLEM LINE the session is a nil value. If i comment this line the session is a ActionDispatch::Session::AbstractStore::SessionHash.
Which could be the problem?
How could i refactoring controller logic, that modify many session keys and 'fill' a model depending the session values, to model/class ?
UPDATE:
I am reading about binding in ruby.
How could modify the session using bindings and eval method?
If you have other ideas, please post your answer.
Thanks in advance
You are assigning session = on the "key problem line". This creates a local variable for the create method which shadows the session method on ActionController::Base. The local variable is nil before you assign it (not non-existent!) so the end result is that session == nil for the whole method, which obviously breaks everything. So: don't assign session. Call your return value something else.
I use binding technique. So i pass the context (simulating parameter pass reference variables).
Example
class OwnController < ApplicationController
def my_action
SettingManager.modify("session[:session]", binding)
end
end
class SettingManager
def self.modify(session, binding)
eval "#{session}.any_value = 5" binding
eval "#{session}.other_value = 'value'", binding
end
end
Finally I use bindings.
For example:
class SessionManager
self.update(session, binding)
eval "#{session} = 0", binding
end
end
class SomeController < ApplicationController
def foo_action_1
session[:refresh] = 1
end
def example_ajax_modify_session
a = session[:refresh] == 1 #true
SessionManager.update("session[:refresh]", binding)
b = session[:refresh] == 1 #false
a == b #false because a == 1 and b == 0
end
I had to modify and manage many session values so before I has a very verbose controller.
Now I could refactoring this logic to model.
Related
I'm writing the following test:
let!(:city_areas) { FactoryGirl.create_list(:city_area, 30) }
before {
#city_areas = mock_model(CityArea)
CityArea.should_receive(:where).and_return(city_areas)
}
it 'should assign the proper value to city areas variable' do
get :get_edit_and_update_vars
assigns(:city_areas).should eq(city_areas.order("name ASC"))
end
to test the following method:
def get_edit_and_update_vars
#city_areas = CityArea.where("city_id = '#{#bar.city_id}'").order("name ASC").all
end
However, it fails out, saying that there's no method 'city_id' for nil:NilClass, leading me to believe it's still attempting to use the instance variable #bar.
How do I properly stub out this where statement to prevent this?
Why are you doing #city_areas = mock_model(CityArea) and then you never use #city_areas again?
I would test it this way:
inside the model CityArea create a named scope for this: where("city_id = '#{#bar.city_id}'").order("name ASC")
then in your controller spec you do
describe 'GET get_edit_and_update_vars' do
before(:each) do
#areas = mock('areas')
end
it 'gets the areas' do
CityArea.should_receive(:your_scope).once.and_return(#areas)
get :get_edit_and_update_vars
end
it 'assign the proper value to city areas variable' do
CityArea.stub!(:your_scope => #areas)
get :get_edit_and_update_vars
assigns(:city_areas).should eq(ordered)
end
end
and you should also create a spec for that new scope on the model spec
just a tip, you shouldn't use should_receive(...) inside the before block, use stub! inside before and use should_receive when you want to test that method is called
also, you shouldn't need to use factorygirl when testing controllers, you should always mock the models, the model can be tested on the model spec
My Devise/Warden-based app stores a model ID in the session[] variable. I want destroy the object when the session[] is destroyed.
Is there a callback or some mechanism to notify my app when the session is destroyed?
Is the mechanism dependable, or should I run a nightly cleanup script to vacuum up any orphaned objects?
To make it clear, here's a snippet of my controller code:
class WizardsController < ApplicationController
before_filter :find_or_create_wizard
...
private
def find_or_create_wizard
#wizard = Wizard.find_by_id(session[:wizard_id]) || Wizard.create.tap {|w| session[:wizard_id] = w }
end
end
To restate the question: how and when should I destroy the Wizard object?
Warden::Manager.before_logout do |user,auth,opts|
# callback
end
Use the Warden::Hooks https://github.com/hassox/warden/blob/master/lib/warden/hooks.rb to do things after sign out or authentication.
By session, do you mean when the user logs out?
Try monkey patching the sign_out method in your application_controller.rb
You can find the relevant Gem code in lib/devise/controllers/helpers.rb
def sign_out(resource_or_scope=nil)
Wizard.find_by_id(session[:wizard_id]) || Wizard.create.tap {|w| session[:wizard_id] = w }
super(resource_or_scope)
end
Session data is also cleared whenever a user signs in or signs up via a function called expire_session_data_after_sign_in!, could override that too:
def expire_session_data_after_sign_in!
Wizard.find_by_id(session[:wizard_id]) || Wizard.create.tap {|w| session[:wizard_id] = w }
super
end
I want to use an ActiveModel callback to be called after an object has been voted on, the issue is that the gem I'm using (voteable_mongo) to make the model votable doesnt provide like a vote model or callback in my app, so how can I create a callback for it?
set_callback(:vote, :before) do |object|
object.do_something
end
Obviously that vote action I made up, but the gem I'm using has this method, how would you properly extend this method to trigger a callback?
Taking the plugin example as source here's what you could do:
class Post
include Mongoid::Document
include Mongo::Voteable
extend ActiveModel::Callbacks
define_model_callbacks :vote
# set points for each vote
voteable self, :up => +1, :down => -1
def vote(options, value = nil)
_run_vote_callbacks do
super( options, value )
end
end
end
I did not run this code so I am not sure if this is going to work correctly or not, but in the worst case you could alias the vote method using alias_method_chain or just copy and paste the source to inside the _run_vote_callbacks block (really, really ugly, but it's a solution anyway).
EDIT
This could also be done using alias_method_chain, if the code above does not work:
class Post
include Mongoid::Document
include Mongo::Voteable
extend ActiveModel::Callbacks
define_model_callbacks :vote
# set points for each vote
voteable self, :up => +1, :down => -1
alias_method_chain :vote, :callback
def vote_with_callback(options, value = nil)
_run_vote_callbacks do
vote_without_callbacks( options, value )
end
end
end
I am verifying the existence of "test_results" in the application controller. It is returned as a local variable. I would like to call it once and have it available for the whole session. How do i do that?
"test_results" in application controller:
def test_results
(0 .. 4).each do |x| # looks for answers to the first 4 questions
if #answers[x].nil? || #answers[x] == 0.0
return false
break
end
end
return true
end
other controllers:
before_filter :test_results
if test_results
...do stuff
else
...display "take the test"
end
error message from view:
undefined local variable or method `test_results'
You can try using #
if #instrument_results
Edit
The result of before filter isn't stored. I don't know what you want to do but you can set a controller level variable inside the test_results function and then referer it as #controller.variable_name. But if your intention is to make a conditional view based on test_results return value, I suggest you to make a redirect to another action in the test_results method and put the success content in the current controller.
You can use :only and :except modifier to check which action of the controller will call the :before_filter.
Hope this helps.
All I'm trying to do is spec how a one line helper method for a view should behave, but I'm not sure what kind of mock object, (if any) I should be creating if I'm working in Rails.
Here's the code for events_helper.rb:
module EventsHelper
def filter_check_button_path
params[:filter].blank? ? '/images/buttons/bt_search_for_events.gif' : '/images/buttons/bt_refine_this_search.gif'
end
end
And here's my spec code, in events_helper_spec.rb:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
#Delete this example and add some real ones or delete this file
it "should be included in the object returned by #helper" do
included_modules = (class << helper; self; end).send :included_modules
included_modules.should include(EventsHelper)
end
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {}
params[:filter] = true
# create an instance of the class that should include EventsHelper by default, as the first test has verified (I think)
#event = Event.new
# call method to check output
#event.filter_check_button_path.should be('/images/buttons/bt_search_for_events.gif')
end
end
When I've looked through the docs here - http://rspec.info/rails/writing/views.html, I'm mystified as to where the 'template' object comes from.
I've also tried looking here, which I thought would point me in the right direction, but alas, no dice. http://jakescruggs.blogspot.com/2007/03/mockingstubbing-partials-and-helper.html
What am I doing wrong here?
Thanks,
Chris
You are not doing anything in that spec, just setting a stub, so it will pass, but hasn't tested anything.
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {:filter => true}
helper.stub!(:params).and_return(params)
helper.filter_check_button_path.should eql('/images/buttons/bt_search_for_events.gif')
end
end
I'm running my test without spec_helper (Ruby 1.9)
require_relative '../../app/helpers/users_helper'
describe 'UsersHelper' do
include UsersHelper
...
end
Ah,
I asked this question on the rspec mailing list, and one kind soul (thanks Scott!) explained to me that there's a handy helper object for this, that you should use instead, like so:
Rails has its own helper function
params = {:filter => true}
helper.stub!(:params).and_return(params)
I've now updated the code like so:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
#Delete this example and add some real ones or delete this file
it "should be included in the object returned by #helper" do
included_modules = (class << helper; self; end).send :included_modules
included_modules.should include(EventsHelper)
end
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {}
params[:filter] = true
helper.stub!(:filter_check_button_path).and_return('/images/buttons/bt_search_for_events.gif')
end
end
And it's working. Huzzah!