If I am going about this wrong please let me know I can change it. I have a file in config/initializers/payload_signer.rb. I am trying to use this file in the controller that is called device_enrollment_controller.rb.
PayloadSigner.sign(get_profile)
get_profile is a method in the controller that gets the file I need and returns it. PayloadSigner references the other file. When I try to run this (keeping in mind im sure changes will have to be made in payload_signer for it work right) the error I get is uninitialized constant DeviceEnrollmentController::PayloadSigner. This leads me to believe I am referencing the payload_signer.rb file incorrectly. I have tried things like include and load but so far they are not working.
Any help or guidance is appreciated.
Rails Initializers are called before Controllers or Models. So it won't work. Initializers are not intended for this kind of use. Instead I suggest placing your code in a controller before_filter. Either in the ApplicationController or only in those controllers that require it (e.g. DeviceEnrollmentController). Something like this:
class DeviceEnrollmentController # Or ApplicationController
before_filter :sign_payload
protected
def get_profile
# Magic
end
def sign_payload
PayloadSigner.sign(get_profile)
end
end
EDIT: Another example:
class DeviceEnrollmentController
# The filter is only applied to the sign action
# (that's what the :only parameter does).
before_filter :sign_payload, :only => [:sign]
# Browsing to /show, you render this magic button of yours.
def show
# Render page that holds the button
end
# The magic button is bound to the /sign route.
# Clicking on the button calls this action.
def sign
# When you get here, the #sign_payload method
# has already been called.
end
protected
def get_profile
# Magic
end
def sign_payload
PayloadSigner.sign(get_profile)
end
end
Related
I have a requirement to need to validate presence of some params in certain situations. Here is the example of that :
In my user controller, for update action, I am required to validate the presence of these params. Same deal for car controller, update action as well, you could see recurring theme here. Params are additional_info.
My base controller provides additional_info_params which pulls the right data from the request.
Here is what I tried so far. I created a AR controller concern and included it in the controller, here is some code:
module ClassMethods
def require_additional_info_for(*methods)
binding.pry
return unless methods.include?(action_name)
if additional_info_params.empty?
head 400
end
end
end
My idea was to be able to define methods that require these params on the top of controller file, just like before_action from rails or skip_authorization_check from cancan. Like so:
MyController < BaseController
include Concerns::AdditionalInformation
require_additional_info_for :update
def update
...
end
end
This code above however does not work as I intended, mainly because this fires on the request class without much knowledge about the request (where I need to derive action name from via action_name).
So how can I do something like this?
Yes, you can, but i suggest you to use the before_action callback!
In a 'abstract' controller, register your method like this:
class SameController < ApplicationController
...
protected
def require_additional_params
render status: :unprocessable_entity if additional_info_params.empty?
end
end
After this, all the controllers who will use this methods, must extends SameController, and runs before_action passing the above method for the wanted actions, for example:
class UserController < SameController
before_action :require_additional_params, only: [:action1, :action2]
end
Note: You can put the require_additional_params in a module and include in your controller, or just put it in the ApplicationController
You might also look at making these regular strong params in the respective controller. It looks something like this:
def update_params
params.require(:car).permit(:engine, :wheels, :rims).tap do |car_params|
car_params.require(:engine)
end
end
This would expect a top-level :car key params (which it strips), and require an :engine param, but allow the other 2 (:wheels and :rims). If :engine isn't present, it will raise a ActionController::ParameterMissing (just like if :cars was missing)
This is straight from the action controller strong params docs (last example at bottom)
I'll sometimes throw these into separate private methods on the respective controller, so there would also possibly be a create_params method with different requirements. I prefer this method over using a custom method as a before_action.
I am using apipie for API documentation.
The problem I am facing now is that the large amount of information to be provided for documentation is ruining my code structure in the controller.
How can I write the documentation outside the controller file,
so that the code structure will be readable?
You can find nice explanation on how to use apipie outside controller in this document https://ilyabylich.svbtle.com/apipie-amazing-tool-for-documenting-your-rails-api
To sum things up (example from link):
# app/docs/users_doc.rb
module UsersDoc
# we need the DSL, right?
extend Apipie::DSL::Concern
api :GET, '/users', 'List users'
def show
# Nothing here, it's just a stub
end
end
# app/controller/users_controller.rb
class UsersController < ApplicationController
include UsersDoc
def show
# Application code goes here
# and it overrides blank method
# from the module
end
end
super also needs to be included in the stubbed actions in the docs. In my app I created a controller that inherits from an existing controller defined in a gem, and if I don't include super in the stubbed action, I get unexpected behavior.
So:
# app/docs/users_doc.rb
module UsersDoc
# we need the DSL, right?
extend Apipie::DSL::Concern
api :GET, '/users', 'List users'
def show
super
end
end
# app/controller/users_controller.rb
class UsersController < ApplicationController
include UsersDoc
def show
# Application code goes here
end
end
In Rails you can add before_filters to your controllers. Assuming a base controller has the following:
before_filter :my_base_filter
Given this, assume that you have a whole host of controllers that inherit from this base controller, and that the norm is that this behavior is correct. I now have a small handful of controllers that do the following:
skip_before_filter :my_base_filter, only: [:method1, :method2]
before_action only: [:method1, :method2] do
my_secondary_filter(param1)
end
Given that this code is in a few of my controllers, and that the methods that are passed in (as well as whether or not it uses only or except) are different from one controller to the next, I would ideally like to have a single before_action or before_filter that I could call that would effectively run the above code. The call, in the inherited controller, would ideally look something like:
replace_filter(param1, {only: [:method1, :method2]})
The second parameter (the hash detailing which methods to apply it to) should be able to accept an empty value and apply to all methods. I have created a helper function that (is written alongside these other filters and), syntactically and logically should do this, but can't seem to properly invoke it using a before_action or before_filter without my_base_filter executing first. Is it possible to do something similar to this, and if so, what is the best way to do so?
application_controller.rb
class ApplicationController < ActionController::Base
include ApplicationHelper
before_action :my_base_filter
...
end
inherited_controller.rb
class InheritedController < ApplicationController
# I want to replace these lines with my new helper function
skip_before_filter :my_base_filter, only: [:method1, :method2]
before_action only: [:method1, :method2] do
my_secondary_filter(param1)
end
...
end
application_helper.rb
class ApplicationHelper
def my_base_filter
# Do shit here that is the normal behavior
end
def my_secondary_filter(param1)
# Do shit here that is specific to certain functions INSTEAD
# of running the normal base filter
end
# I want to be able to simply call this function
# as a before_action or before_filter in order
# to DRY up my code
def replace_filter(param1, methods = {})
# Run validation on parameters (including methods) here
# including raising exceptions if necessary
...
# Then run the following
skip_before_filter :my_base_filter, methods
before_action(methods) do
my_secondary_filter(param1)
end
end
end
So... there's this thing in Rails called a Concern. It's intended to be the way you pull out modules of stuff in Rails - while allowing a whole bunch of neato things that you can use.
There's a number of articles out there on what they are and how to use them. I'll let you go explore.
I can't say for certain that the following will fix your problem, but it's how I'd approach it.
Part of your problem is that as you say - by the time you get to running your "replace_filter" method, the bas_filter method has already run.
What you need is to be able to run replace_filter on the first time that your ApplicationHelper is included into the Controller.
This is where ActiveSupport::Concern's included method comes to your aid.
Give something like this a try:
# give this a meaningful name...
class FilterStuff < ActiveSupport::Concern
included do
puts "I'm in included"
# note: not the replace_filter, but the method that will call it
if defined?(:replace_filter_method)
puts "This controller defines replace filter method which I'm now calling"
replace_filter_method
else
puts "this controller does not define the replace method and will default to base behaviour"
before_action :my_base_filter
end
end
def my_base_filter
puts "I'm in my base filter"
end
def my_secondary_filter(param1)
puts "I'm in secondary filter with param: #{param1}"
end
# making this work is another problem...
def replace_filter(param1, methods = {})
puts "I'm in replace filter with: #{param1} and #{methods.inspect}"
# Run validation on parameters (including methods) here
# including raising exceptions if necessary
...
# Then run the following
skip_before_filter :my_base_filter, methods
before_action(methods) do
my_secondary_filter(param1)
end
end
end
class InheritedController < ApplicationController
include FilterStuff
# actions go here...
private
# define this method only on controllers that need it
def replace_filter_method
puts "I'm in this controllers replace filter method"
replace_filter(param1, only: [:method1, :method2])
end
end
I've added a whole bunch of printf debugging - have a run trhough and it'll tell you what is being called when - and that will help you determine what you need to do to get the proper stuff working.
I want to add a method called quick_view, which is basically a show method but much simpler and meant to be served with Ajax on hover on a product.
How can i achieve this ? How can i open the ProductsController of spree and add before_filters and also specify the appropriate routes..
Thanks !
Doco for that is at https://guides.spreecommerce.com/developer/logic.html
Create a class called app/controllers/products_controller_decorator.rb, and put something like this in it:
Spree::ProductsController.class_eval do
before_filter :my_filter, :only => :quick_view
def quick_view
# your code goes here
end
private
def my_filter
# code for your before filter goes here
end
end
As for the routes, you'll be able to add it in your routes.rb file just like any other route, but you'll need to specify that it's a spree route:
Spree::Core::Engine.routes.draw do
# Your route goes inside this block
end
I have a strange behavior of Rails 3.0.10. Got this application controller:
app/controllers/application_controller.rb
require 'api/api_controller'
# rest not important
app/controllers/api/api_controller.rb
class Api::ApiController < ActionController::Base
before_filter :require_user
def require_user
#user = User.find(xxx, yyy)
end
end
and then this controller:
app/controllers/api/ac_controller.rb
class Api::AcController < Api::ApiController
before_filter :find_pool, :only => [:add_pool, :remove_pool]
def add_pool
# some logic that needs #user to be set
end
def remove_pool
# some logic that needs #user to be set
end
def find_pool
# some logic here
end
end
My problem is when I run this in production mode the require_user filter is NOT called. When I try this in the development mode, it works.
Now, I understand in development mode classes are being reloaded, but the question is why the require_user filter does NOT get called?
Edit: Please note AC controller is before API controller lexicographically.
It looks like order of required files problem or the ApiController being loaded twice. Once before AcController and one more time after AcController being loaded. This could cause, that find_pool filter will get evaluated before require_user. ApiController is alse after AcController in lex order.
The problem might be caused by require "api_controller" being present somewhere - it should be handled by Rails and does not need to be put down explicitly. So if there is such a line, removing it could help.
Typically the methods called by filters should NOT be public. Public methods in controllers are treated as actions. Have you tried making require_user a private method?