I have a set of boolean symbols, inside a controller action
These are currently in 1 action, in the following format
def my_action
setup_stages = [:data_entry_completed, :data_validation_completed]
setup_stages.each do |stage|
do stuff
end
end
I've noticed that I need to make use of these symbols in another action but do not want to replicate them. Is there a way to make this list accessible to multiple actions in the controller so that I can iterate through them without having the list twice?
Just define them as constant:
class MyController < AplicationController
SETUP_STAGES = [:data_entry_completed, :data_validation_completed]
I would personally define it as an instance variable:
class MyClass
def initialize
#setup_stages = [:data_entry_completed, :data_validation_completed]
end
def do_action
#setup_stages.each do |stage|
# do stuff
end
end
def show_stages
puts #setup_stages.to_s
end
end
x = MyClass.new
x.do_action
x.show_stages
A constant is also a good way of defining this but should not be altered, so if for whatever reason you want to add other options to the array dynamically you would be able to do this with an instance variable.
Related
If I have a controller
class MyController < ApplicationController
vals = [...]
def new
...
end
def create
if save
...
else
render 'new'
end
end
how can I make the "vals" variable accessible to both methods? In my "new" view I want to use the "vals" variable for a drop-down menu, but rails is giving me errors. Of course, I could just copy the variable twice, but this solution is inelegant.
As Sebastion mentions a before_ hook / callback is one way to go about it, however as you mentioned it is for a dropdown menu, I am guessing it is a non-changing list, if so I would suggest perhaps using a Constant to define the values, perhaps in the model they are specific to, or if it is to be used in many places a PORO would do nicely to keep things DRY. This will then also allow you to easily access it anywhere, for example in models for a validation check, or to set the options of the dropdown menu in the view, or in the controller if you so wish:
class ExampleModel
DROPDOWN_VALUES = [...].freeze
validates :some_attr, inclusion: { in: DROPDOWN_VALUES }
end
class SomeController < ApplicationController
def new
# can call ExampleModel::DROPDOWN_VALUES here
end
def create
# also here, anywhere actually
end
end
You could use a before_* callback, e.g a before_action, this way you sets your vals variable as an instance one and make it to be available for your both new and create methods, something like:
class SomeController < ApplicationController
before_action :set_vals, only: [:new, :create]
def new
...
# #vals is available here
end
def create
if save
...
# and here
else
render 'new'
end
end
private
def set_vals
#vals = [...]
end
end
A different way from the ones before (although probably just having the instance method is preferred as in Sebastian's solution) is, take advantage of the fact that functions and local variables are called in the same way in ruby and just write:
def vals
#vals ||= [...]
end
and you should be able to access it on the controllers (not the views). If you want it on your views as well you can call at the beginning of the controller
helper_method :vals
If you want to be able to modify vals using vals="some value"
def vals= vals_value
#vals = vals_value
end
Take into account that probably using the intance variable as in Sebastian's solution is preferred, but if you, for whatever reason, are settled on being able to call "vals" instead of "#vals" on the view (for example if you are using send or try), then this should be able to do it for you.
Define in corresponding model
Eg :
class User < ActiveRecord::Base
TYPES = %w{ type1 type2 type3 }
end
and use in ur form like
User::TYPES
=> ["type1", "type2", "type3"]
You can reuse this anywhere in the application.
One method in my controller is as follows:
def new_equipment_matches_wanted
#..........Big chucnk of code of about 7 to 8 lines and then..
#receiver
end
Now I am defining another method where I want to access the value of this #receiver instance variable. And it will have a separate template.
def send_receivers_to_admin
#supposedly I am trying send #receiver values here
end
How can I achieve this? Or should I write the same code in the second method also just to access the values in that variable?
Instance variables ( with #) are shared among all the controller's methods.
def new_equipment_matches_wanted
#..........Big chucnk of code of about 7 to 8 lines and then..
#receiver
end
def send_receivers_to_admin
#receiver.do_something
end
You can read more about variables here
However, as Sergio said,
"passing instance variables between requests is impossible...
controller actions backing different requests"
then you'll have to load #receiver on both methods, you can use before_action
class CustomController < ActionController::Base
before_action :load_receiver, only [:send_receivers_to_admin,:new_equipment_matches_wanted]
def new_equipment_matches_wanted
#receiver.do_something
end
def send_receivers_to_admin
#receiver.do_something
end
private
def load_receiver
# Big chucnk of code of about 7 to 8 lines and then..
#receiver
end
I created a class variable instead and accessing that variable from any class necessary.
A class variable is written as ##variable_name inside a class (or controller) before declaring any method
I am rather new to Rails, and would greatly appreciate any bit of help. I have created the following method:
def name_fix
name = self.split
mod_name = []
name.each do |n|
n.split("")
if n[0]
n.upcase
else
n.downcase
end
mod_name.push(n)
end
mod_name.join
end
I would like to use this method in my Controller as such:
def create
#patient = Patient.new(params[:patient])
#patient.name = params[:params][:name].name_fix
if #patient.save
redirect_to patients_path
else
render :new
end
end
How can I go about accomplishing this? Will this method reside within my Model or Controller? Previously, I've run into an undefined method error.
Note: I'm sure that there is a way to better write my code. I am grateful for help with that as well.
#app/models/patient.rb
class Patient < ActiveRecord::Base
protected
def name=(value)
mod_name = []
value.split.each do |n|
n.split("")
type = n[0] ? "up" : "down"
n.send("#{type}case")
mod_name.push(n)
end
#name = mod_name.join
end
end
#app/controllers/patients_controller.rb
class PatientsController < ApplicationController
def create
#patient = Patient.new patient_params
#patient.save ? redirect_to(patients_path) : render(:new)
end
private
def patient_params
params.require(:patient).permit(:name)
end
end
What you're doing is trying to override the setter method, which can be done using the above code. Much more efficient and out of the way.
I have created the following method
Since you're new, let me explain something else.
It is important to note where you're using this method.
You've currently put it in the model, which means you'll have to call it to manipulate some attribute / functionality of any object created with said model.
--
Models - in Rails - build the objects which populate your app. Ruby is an object orientated language, which means that every element of your program should revolve around data objects in some degree.
As you can see above, the method of building objects in your system is really about invoking classes. These classes contain methods which can be called, either at class level (IE invoking the class through the method), or at instance level (IE calling a method on an already invoked object).
This is where you get "class" methods (Model.method) and "instance" methods (#model.method) from:
#app/models/patient.rb
class Patient < ActiveRecord::Base
def explode
#this is an instance method
puts "Instance Explode"
end
def self.explode
#this is a class method
puts "Exploded"
end
end
Thus you can call the following:
#patient = Patient.find params[:id]
#patient.explode #-> "Instance explode"
Patient.explode #-> "Exploded"
--
This is important because it gives you a strict framework of where you should, and shouldn't use methods in your models.
It explains why you have controllers & helpers, and allows you to formulate the best way to structure your application as to get the most out of the least code.
For example...
Your use of #patient.name = params[:params][:name].name_fix is incorrect.
It's wrong because you're calling the instance method .name_fix on a piece of data totally unrelated to your model. If you wanted to use .name_fix in a general sense like this, you'd probably use a helper:
#app/helpers/patients_helper.rb
class PatientsHelper
def name_fix value
# stuff here
end
end
#app/controllers/patients_controller.rb
class PatientsController < ApplicationController
def create
#patient.name = name_fix params[:patient][:name]
end
end
Since you're using the method to populate the .name attribute of your model, it makes sense to override the name= setter. This will not only provide added functionality, but is much smoother and efficient than any other way.
Methods that are called directly are best put in the Controller (or in ApplicationController if you think more than one controller might want to use it).
These are methods like
# app/controllers/my_controller.rb
def foo(bar)
# do something here
end
def create
id = params[:id]
value = foo(id)
end
If you want a chained method that acts as a property method of whatever you're calling it on. Those are characteristic of how Models work - you have your main model and you call attributes or methods on the instance of that model.
# app/models/my_model.rb
def full_name
first_name + " " + last_name
end
# app/controller/my_controller.rb
def create
id = params[:id]
model = MyModel.find(id)
full_name = model.full_name
end
In your case, you want to call name_fix ON whatever is returned by params[:params][:name], which is (I'm guessing) a String.
You have two options
Modify the String class to define a method named name_fix. I highly recommend against this. It's call "monkeypatching" and shouldn't be done without good reason. Just letting you know you can do it in some cases.
Use a direct method in your controller or ApplicationController like the first example above.
#patient.name = name_fix(params[:params][:name])
Edit: As for your request about a better way to write your code... that's difficult to teach or convey in one answer. I'd say read some open source projects out there to see how people write Ruby and some common idioms used to clean up the code. To get you started, here's how I'd re-write your code
def create
#patient = Patient.new(params[:patient])
# 1. Be descriptive with your method names. `name_fix` is vague
# 2. Why is `:name` nested under another `[:params]` hash?
#patient.name = capitalize_name(params[:name])
if #patient.save
# 1. I think `patient_path` has to be singular
# 2. It needs a `Patient` object to know how to construct the URL
# e.g. `/patients/:id`
redirect_to patient_path(#patient)
else
render :new
end
end
def capitalize_name(full_name)
# Example: julio jones
#
# 1. `split` produces an array => ["julio", "jones"]
# 2. `map` applies a function (`capitalize`) to each element
# => ["Julio", "Jones"]
# 3. `join(" ")` rejoins it => "Julio Jones"
full_name.split.map(&:capitalize).join(" ")
end
Assuming your goal with the name_fix method is just to capitalize the first letter of each name, you could just pass name as an argument and store it as a private method on the Controller:
# app/controllers/patient_controller.rb
private
def name_fix(name)
name.split.map(&:capitalize).join(" ")
end
Then you could do
#patient.name = name_fix(params[:params][:name])
in the create method.
OR, you could store this method in the model:
# app/models/patient.rb
def self.name_fix(name)
name.split.map(&:capitalize).join(" ")
end
Then you could do this instead, in the controller:
#patient.name = Patient.name_fix(params[:params][:name])
I would also suggest renaming your name_fix method to something like capitalize_name.
update your create method as below
def create
#patient = Patient.new(params[:patient])
#patient.name = params[:params][:name]
#patient = #patient.name_fix
if #patient.save
redirect_to patients_path
else
render :new
end
end
It should work.
I have a controller which calls out to another class.
class BlahController < ActionController
def index
OtherClass.get_stuff
end
end
In this class I want to be able to write controller style code.
for instance:
class OtherClass
def self.get_stuff
#foo = bar
end
end
However, I would also like #foo to exist when inside my view, but as it's a separate class those variables aren't making it back through into the controller assigns - so question is, how I can make this so?
(Ignore why I'm having to call out to a separate class, I'm trying to get this code fitting in with a legacy codebase without too much butchery)
class BlahController < ActionController
def index
OtherClass.get_stuff(self)
end
end
class OtherClass
def self.get_stuff(that)
that.instance_variable_set(:#foo, bar)
end
end
Please note that I don't agree with this method. I am just answering the question as you stated it.
I would prefer to accomplish this functionality through mixins and thereby decrease parameter coupling that is present within the code above.
Code structured like this will be difficult to read and maintain. Whenever you can, let the controller directly set all of the variables that the view needs:
class BlahController < ActionController
def index
#foo = OtherClass.get_stuff
end
end
class OtherClass
def self.get_stuff
# return the value that should be assigned to #foo
end
end
I would like to add a couple of instance variables to my controller, since the variables in question are required from within more than one action's view. However, the below example does not work as I would expect.
class ExampleController < ApplicationController
#var1 = "Cheese"
#var2 = "Tomato"
def show_pizza_topping
# What I want is the above instance vars from within the view here
end
def show_sandwich_filling
# What I want is the above instance vars from within the view here
end
end
As I understand it, Rails takes the instance variables from the controller and makes them available in the view. If I assign the same variables within the action methods, it works fine - but I don't want to do it twice. Why does my way not work?
(Note: this is a bit of a rubbish example, but I hope it makes sense)
EDIT: I have found the answer to this question here: When do Ruby instance variables get set?
EDIT 2: when is the best time to use filters such as before_filter and the initialize method?
These types of things should be handled in a before_filter. A before filter, like the name implies, is a method that will get called before any actions, or only the ones you declare. An example:
class ExampleController < ApplicationController
before_filter :set_toppings
def show_pizza_topping
# What I want is the above instance vars from within the view here
end
def show_sandwich_filling
# What I want is the above instance vars from within the view here
end
protected
def set_toppings
#var1 = "Cheese"
#var2 = "Tomato"
end
end
Or, you could have your before_filter only work on one of your actions
before_filter :set_toppings, :only => [ :show_pizza_topping ]
Hope this helps.
EDIT: Here's some more information on filters in ActionController.
Those aren't instance variables, are they?
class A
#x = 5
def f
puts #x
end
end
A.new.f
=> nil
You're defining it at the class-level, not the instance-level. As "theIV" points out, you need to assign them inside an instance method.