I have a controller that counts the number of times the user has been to a page. I'm trying to extract that count to a getter and setter which set a session variable. Getting works, but setting doesn't. This is the controller:
class StoreController < ApplicationController
def index
#products = Product.order(:title)
v = store_visits + 1
store_visits = v # tests fail if I do it like this
# store_visits += 1 # Undefined method '+' for NilClass if i do it like this
#visits = store_visits
end
def store_visits
if session[:store_counter].nil?
session[:store_counter] = 0
end
session[:store_counter]
end
def store_visits=(value)
session[:store_counter] = value
end
end
And here's a failing test:
require 'test_helper'
class StoreControllerTest < ActionController::TestCase
test "should count store visits" do
get :index
assert session[:store_counter] == 1
get :index
assert session[:store_counter] == 2
end
end
Why isn't it setting, and why is store_visits returning nil if I use += ? Any help is appreciated.
Note: Originally I extracted the methods to a concern, but I've edited this to remove the concern, because the problem isn't with the concern, it's with the setter and/or getter.
Update: After adding logging statments, it's obvious that the inside of the store_visits=() method is never reached (but somehow an error is not thrown). However, if I rename it to assign_store_visits(), it does get called, and does update the session variable. So I'm guessing this is either a bug where setter methods don't work in controllers (this is Rails 4.0.0) or they're intentionally blocked (in which case, an exception would be nice).
try switch to include ActiveSupport::Concern
this will provide instance methods instead class methods
You need to wrap the methods inside of your concern inside of an included block like:
module Visits
extend ActiveSupport::Concern
included do
#private
def store_visits
if session[:store_counter].nil?
session[:store_counter] = 0
end
session[:store_counter]
end
def store_visits=(value)
session[:store_counter] = value
end
# private
end
end
end
Doing that will make those methods available as instance methods inside of your controller.
Related
I want to create a function to set the instance variable like attr_reader
class Base
def exec
# get all functions to check
# if all functions return true
# I will do something here
end
end
And then I have a class inherit Base.
class SomeClass < Base
check :check_1
check :check_2
def check_1
# checking
end
def check_2
# checking
end
end
class Some2Class < Base
check :check_3
check :check_4
def check_3
# checking
end
def check_4
# checking
end
end
Because I only need 1 logic for executing in all classes but I have a lot the different checks for each class, I need to do it flexibly.
Please, give me a keyword for it.
Many thanks.
In order to have check :check_1 you need to define check as a class method:
class Base
def self.check(name)
# ...
end
end
Since you want to call the passed method names later on, I'd store them in an array: (provided by another class method checks)
class Base
def self.checks
#checks ||= []
end
def self.check(name)
checks << name
end
end
This already gives you:
SomeClass.checks
#=> [:check_1, :check_2]
Some2Class.checks
#=> [:check_3, :check_4]
Now you can traverse this array from within exec and invoke each method via send. You can use all? to check whether all of them return a truthy result:
class Base
# ...
def exec
if self.class.checks.all? { |name| send(name) }
# do something
end
end
end
SomeClass.new.exec # doesn't do anything yet
The self.class part is needed because you are calling the class method checks from the instance method exec.
I was wondering if it was possible to pass as parameter the if condition statement as string or symbol. Because method name or if statement name could change and if I need it to refacto things it's better to work with a variable,
here an example inside a simple update method.
#within any controller
class FooController < ApplicationController
include RedirectAfterFooUpdate
# other methods
def update
#foo.update(place_params)
if #foo.save
action_after_update_foo(some_parameters)
else
# error redirection...
end
end
end
#within a module need to set correct action after update foo
module RedirectAfterFooUpdate
def action_after_update_foo(some_parameters)
if condiction_1
do_something(condiction_1.to_s.to_sym) #do_something(:condiction_1)
elsif condiction_2
do_something_else(condiction_2.to_s.to_sym) #do_something_else(:condiction_2)
elsif condiction_3
do_something_else_again(condiction_3.to_s.to_sym) #do_something_else_again(:condiction_3)
else
#casual code
end
end
end
the code above is clearly simplified and i actually have many other paramaters, but the thing is really focus on the "if statement" => condiction_1 or condiction_2 or condiction_3. How could we get the name of it .
The question Get the name of the currently executing method is not really helpful in that case because I do not need the root method name action_after_update_foo.
If the conditions are just method invocations, you could use the following approach that uses Ruby's send method to evaluate each condition:
module RedirectAfterFooUpdate
def action_after_update_foo(some_parameters)
# Declare the conditions that represents method invocations
conditions = [
:only_refund_changed,
:another_condition_x,
:another_condition_y
]
conditition_executed = false
conditions.each do |condition|
# Executes each if / elsif block
if !conditition_executed && send(condition)
# Invoke do_something with the condition as a symbol
do_something(condition)
conditition_executed = true
end
end
# Executes the else block
if !conditition_executed
#casual code
end
end
end
I created a controller which called 'internal releases'.
I want to check that the multi-select objects contains at least one selection each.
In my controller I have:
class InternalReleasesController < ApplicationController
def show
if params[:run].nil?
logger.error "Attempt to get trend result without going through the internal_releases_trend_selection_url"
flash[:no_arguments] = 'You have tried accessing trend results without selecting parameters.'
redirect_to internal_releases_trend_selection_url
else
all_options = Array.new(params[:run][:category_id])
missing_selections = validate_arguments params[:run]
all_options = Array.[]params[:run][:category_id]
logger.debug "all_options is: #{all_options.class}"
end
end
end
I created a simple helper method:
module InternalReleasesHelper
def validate_arguments multiselect_hash
answer = Array.new
multiselect_arr.each do |key, val_arr|
if val_arr.length==1 # therefore, no selection made in this multiselect- the first arg will always be ""
answer << key
end
end
answer
end
end
For some reason I get:
undefined method `validate_arguments' for #<InternalReleasesController:0x007faf08bf9f78>
What might cause this?
Include helper module InternalReleasesHelper into InternalReleasesController class
class InternalReleasesController
include InternalReleasesHelper
end
Helper's method are just available into Views by default, so you should include your helper into controller:
Navigate on internal_releases_controller.rb file and insert following:
include InternalReleasesHelper
I have a class like so:
Railsapp/lib/five9_providers/record_provider.rb:
class Five9Providers::RecordProvider < Five9Providers::BaseProvider
def add_record_to_list
variable = 'test'
end
end
Then, in a controller I have this:
Railsapp/app/controllers/five9_controller.rb:
class Five9Controller < ApplicationController
def import
record_provider = Five9Providers::RecordProvider.new()
record_provider.add_record_to_list
puts Five9Providers::RecordProvider::variable
end
end
However, calling my controller method import just returns:
NoMethodError (undefined method 'variable' for Five9Providers::RecordProvider:Class)
How can I access variable from the recover_provider.rb class in my five9_controller.rb class?
EDIT:
Even when using ##variable in both my record_provider and my five9_controller, I still can't access that variable. I am calling it like so: puts ##variable.
As written, you cannot. variable is local to the instance method and can't be accessed by any Ruby expression from outside the method.
On a related point, the term "class variable" is typically used to refer to variables of the form ##variable.
Update: In response to your "Edit" statement, if you change variable to ##variable in your class, then there are techniques available to access that variable from outside the class, but a naked reference to ##variable isn't one of them. Carefully read the answers to the question you cited in your comment for more information.
Best way is to set and get the value using methods. Below is a sample code
class Planet
##planets_count = 0
def initialize(name)
#name = name
##planets_count += 1
end
def self.planets_count
##planets_count
end
def self.add_planet
##planets_count += 1
end
def add_planet_from_obj
##planets_count += 1
end
end
Planet.new("uranus")
Plant.add_planet
obj = Planet.new("earth")
obj.add_planet_from_obj
How do you define a method for an attribute of an instance in Ruby?
Let's say we've got a class called HtmlSnippet, which extends ActiveRecord::Base of Rails and has got an attribute content. And, I want to define a method replace_url_to_anchor_tag! for it and get it called in the following way;
html_snippet = HtmlSnippet.find(1)
html_snippet.content = "Link to http://stackoverflow.com"
html_snippet.content.replace_url_to_anchor_tag!
# => "Link to <a href='http://stackoverflow.com'>http://stackoverflow.com</a>"
# app/models/html_snippet.rb
class HtmlSnippet < ActiveRecord::Base
# I expected this bit to do what I want but not
class << #content
def replace_url_to_anchor_tag!
matching = self.match(/(https?:\/\/[\S]+)/)
"<a href='#{matching[0]}'/>#{matching[0]}</a>"
end
end
end
As content is an instance of String class, redefine String class is one option. But I don't feel like to going for it because it overwrites behaviour of all instances of String;
class HtmlSnippet < ActiveRecord::Base
class String
def replace_url_to_anchor_tag!
...
end
end
end
Any suggestions please?
The reason why your code is not working is simple - you are working with #content which is nil in the context of execution (the self is the class, not the instance). So you are basically modifying eigenclass of nil.
So you need to extend the instance of #content when it's set. There are few ways, there is one:
class HtmlSnippet < ActiveRecord::Base
# getter is overrided to extend behaviour of freshly loaded values
def content
value = read_attribute(:content)
decorate_it(value) unless value.respond_to?(:replace_url_to_anchor_tag)
value
end
def content=(value)
dup_value = value.dup
decorate_it(dup_value)
write_attribute(:content, dup_value)
end
private
def decorate_it(value)
class << value
def replace_url_to_anchor_tag
# ...
end
end
end
end
For the sake of simplicity I've ommited the "nil scenario" - you should handle nil values differently. But that's quite simple.
Another thing is that you might ask is why I use dup in the setter. If there is no dup in the code, the behaviour of the following code might be wrong (obviously it depends on your requirements):
x = "something"
s = HtmlSnippet.find(1)
s.content = x
s.content.replace_url_to_anchor_tag # that's ok
x.content.replace_url_to_anchor_tag # that's not ok
Wihtout dup you are extending not only x.content but also original string that you've assigned.