Is there any way to disable the automatic passing of Rails instance variables to the views when they render? I'd like to be able to turn this off and then see where things fail in order to target refactoring.
You should be able to do that by overriding view_assigns in your controller:
class SomeController < ApplicationController
protected
def view_assigns
{} #an empty hash
end
end
Related
I have some helpers that are defined on runtime that are specific for a single call, e.g. a single instance of a controller (the next call could have different helper methods). Is there a robust way to add a helper method to an instance of a controller and it's view only, without adding the helper to other instances and views of this controller?
To define a helper for ALL instances, you could use the .helper_method method, e.g.
class Article < ApplicationController
helper_method :my_helper
def my_helper
# do something
end
end
I digged around in the source code, and found the (fairly private looking) #_helpers method which returns a module that contains all helpers for this instance. I could now use some meta programming to define my methods on this module
def index
_helpers.define_singleton_method(:my_helper) do
# do something
end
end
But I don't like this approach because I'm using a clearly private intended method that could easily change in the future (see the leading _).
If I only needed the helper inside the controller instance only, I could just call #define_singleton_method on the instance directly, but this doesn't make it available to the view.
So I'm looking for an official "Rails way" to define a helper for a single instance of a controller and it's view, like Rails provides with it's class method .helper_method.
I'm not sure if there is an official Rails way of doing this.
You could create an anonymous module and extend from that. Since this solution uses pure Ruby, you'll have to extend both the controller and view.
before_action :set_helpers, only: :index
def index
# ...
end
private
def set_helpers
#helpers = Module.new do |mod|
define_method(:my_helper) do
# do something
end
end
extend(#helpers)
end
<% extend(#helpers) %>
The rails scaffolds give you the resource_params method as private by default:
private
def person_params
params.require(:person).permit(:name, :age)
end
I understand why strong_parameters is a good thing. I also understand that this prevents the method from being accessed outside the controller, but are there any real dangers to making this method public, or what is the reasoning behind having this as a private method? It would be nice to be able to send that method to the controller from a gem that extends ActionController.
In other words, why not access the method outside of the controller? For example, if I have a separate controller that handles authorization and I want to pass an instance variable back to the original controller that contains the initialized object.
Because this method is not called from any external objects.
Mass assignment protection is not connected with 'person params' method visibility, it is just best practice for application design
Controllers using to handless only one request by app design. You should not call methods from one controller in another. If you want to share methods for several controllers, you can use inheritance, mixins, or service objects
inheritance
class BaseController < ApplicationController
private
def shared_method
end
end
class UsersController < BaseController
def index
shared_method
end
end
mixin
module SomeMixin
extend ActiveSupport::Concern
included do
def shared_method
end
end
end
class UsersController < ApplicationController
include SomeMixin
def index
shared_method
end
end
service object
class SomeService
def shared_method(params)
# process params
end
end
class UsersController < ApplicationController
include SomeMixin
def index
SomeService.new.shared_method(params)
end
end
It wasn't always this way and it's there to protect you. Check out this great blog post on the subject.
Here are some relevant snippets:
Problem with Mass-Assignment: Security vulnerability
Mass-assignment saves us the need to assign values to each attribute
of the model, but it can create problems. Since we aren’t restricting
which attributes can be set nor are we checking the valus of these
attributes, a malicious hacker could assign any value to any
attribute. In our example, he could set the value of admin true,
making himself a super user.
Here is what the url might look like
http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1
Users are able to exploit this if they know even a little bit about your models and cause issues.
In most languages there are classifications in a class for methods, attributes or whatever else it may contain.
Depending on language or inheritence, default behaviour might be public, private...
In ruby, classes by default contain public methods and attributes. You need to specify if a method is private.
The public method or attribute:
This is accessible from out of the class or the instance of the class. So, if you have class:
class Foo
def my_id
10
end
private
def my_class
"Bar"
end
public
def my_friend
"Zonk"
end
end
Then:
2.0.0p247 :001 > #foo = Foo.new
2.0.0p247 :002 > #foo.my_id
=> 10
2.0.0p247 :003 > #foo.my_class
NoMethodError: undefined method `my_class' for #<Foo:0x000000045a53f8>
2.0.0p247 :004 > #foo.my_friend
=> "Zonk"
You see than you can change from private to public as you wish, though maybe not a very good idea.
I'm slightly new to Rails (i.e. stupid and need some teachin').
I have a controller (call it ControllerFoo) that performs a particular task (theMethod) which could be useful in other controllers (say, from within ControllerBar). So, of course, the method is defined as self.theMethod in ControllerFoo (which means it's a class method, right?), and access in ControllerBar as ControllerFoo.theMethod. Confused yet?
Here's the problem: the ControllerFoo.theMethod uses session data, and when called from ControllerBar, session is nil. In fact, it seems that session is also nil when being called from itself. I guess what I'm saying is class methods can't access session data?
<rant>I hate how session data can't simply be accessed anywhere like in PHP</rant>
So for now, since I'm not smart enough to know how to do this correctly, I've just duplicated the logic in several places throughout my app. But this is not DRY at all, and I hate it.
So how can I create a method in a controller that's accessible to other controllers and can also access session data?
class ControllerFoo < ApplicationController
def self.theMethod (greeting)
p "#{greeting} #{session[:user]}!"
end
end
class ControllerBar < ApplicationController
def show
ControllerFoo.theMethod("Hello,")
end
end
Couple of options...
Put the shared method in the shared parent ApplicationController
Create a module that both ControllerFoo and ControllerBar will include
e.g.
module SharedModule
def theMethod (greeting)
p "#{greeting} #{session[:user]}!"
end
end
class ControllerFoo < ApplicationController
include SharedModule
end
class ControllerBar < ApplicationController
include SharedModule
def show
theMethod("Hello,")
end
end
The way you would do this is Ruby would be to create a module containing the class (or instance) methods you wish to share and include it in the classes you need to have those methods defined in.
is there any way to persist (preserve) parameters in Rails controller? It should be passed to every action, then to every view and every link.
Example situation:
I have entity A with its controller. Besides, I have another entity B which is dependent on A. I need to access the "parent" A entity very often, so I'd like to have it still as
http://some_url/b_controller/b_action?a_entity=xyz
You should be able to do everything from your controller, using a combination of before_filter and default_url_options :
class MyController < ApplicationController
before_filter :set_a_entity
def set_a_entity
#a_entity = params['a_entity']
# or #a_entity = Entity.find(params['a_entity'])
end
# Rails 3
def url_options
{:a_entity => #a_entity}.merge(super)
end
# Rails 2
def default_url_options
{:a_entity => #entity}
end
end
This doesn't solve the problem of setting the initial value of #a_entity, but this can be done from anywhere (view, controller, etc).
If you want this parameter passed around in multiple controllers, you can replace MyController < ApplicationController with ApplicationController < ActionController::Base and it should work as well.
Hope this helps.
why not put it in a session parameter then?
session["a_entity"] = "xyz"
that way you can access it in all your other controllers too until you clear it or it expires.
more info here:
http://api.rubyonrails.org/classes/ActionController/Base.html
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.