ActiveModel::ForbiddenAttributesError + cancan + rails 4 + model with scoped controller - ruby-on-rails-4

I m using cancan(1.6.10) with rails 4.0.0. I have a model called 'App'(not scoped) and a controller Admin::AppsController(its scoped. ie app/controllers/admin/apps_controller).
the controller code is as
class Admin::AppsController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource class: App
def index
end
#CRUD methods and some other custom methods
...
private
def app_params
params.require(:app).permit(:name, :description, :author, :url_path, :validated, :active, :version)
end
end
I m getting error when i try to create a 'app'.
ActiveModel::ForbiddenAttributesError - ActiveModel::ForbiddenAttributesError:
activemodel (4.0.0) lib/active_model/forbidden_attributes_protection.rb:21:in `sanitize_for_mass_assignment'
I added
before_filter do
resource = controller_path.singularize.gsub('/', '_').to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
as specified in https://github.com/ryanb/cancan/issues/835#issuecomment-18663815 but still getting the above error.

Using name spaces. Please try to change your code to this one below. I had same issue after #JiriKolarik suggested his solution to work with name spaces. I hope it helps.
before_filter do
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end

if you use this workflow
before_filter do
resource = controller_path.singularize.gsub('/', '_').to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
then your params method should look like this
def admin_app_params
params.require(:admin_app).permit(:name, :description, :author, :url_path, :validated, :active, :version)
end
The reason why, it's because form generators (form_form, simple_form) generate params with namespace_resource
So if you have Blog::Post, form generator will create params like this
{ "blog_post"=>{"title"=>"Post"}, "commit"=>"Create", "action"=>"create", "controller"=>"blog/posts", "locale"=>"en"}
And this is how before filter works:
before_filter do
resource = controller_path.singularize.gsub('/', '_').to_sym # => 'blog/posts' => 'blog/post' => 'blog_post' => :blog_post
method = "#{resource}_params" # => 'blog_post_params'
params[resource] &&= send(method) if respond_to?(method, true) # => params[:blog_post]
end
If you need read :blog_post from params, solution above will not work. If you need read :post from params, then this solution will not work, if your controller will be blog/post

cancan just does not work with the strong parameter. While there is a new gem cancancan which works well without any code change.

Related

Ruby on Rails - Cocoon gem, Don't Pre-populate existing/added posts in edit action

My application has models Campaign & Post, I have:
class Campaign < ActiveRecord::Base
has_many :posts, inverse_of: :campaign
accepts_nested_attributes_for :posts, reject_if: :all_blank, allow_destroy: true
class Post < ActiveRecord::Base
belongs_to :campaign
My form:
= simple_form_for(#campaign) do |f|
= f.error_notification
= f.input :title
#posts
= f.simple_fields_for :posts do |post|
= render 'post_fields', f: post
.links
= link_to_add_association 'Add Post', f, :posts, wrap_object: Proc.new {|post| post.user_id = current_user.id; post }
I use Cocoon gem for nested_forms.
When I go to my campaigns#edit view, I can see all posts that were already added to a campaign (natural behavior of the gem), and I can add new posts to my campaign and/or edit existing posts .
I have also a button that has this param: add_to: 'existing_campaign' and what I am trying to achieve is, if my link has ?add_to=existing_campaign, I don't want to show/Pre-populate any of the posts that were already added to campaign, so user can only add new posts to the campaign
My link_to looks like:
= link_to 'Add Post', edit_campaign_path(campaign, add_to: 'existing_campaign'),
short explain: if edit link has param ?add_to=existing_campaign, I don't Pre-populate already added posts, if param doesn't exists, I Pre-populate posts
How can I achieve this?
Set up an attr_accessor in campaign to control whether or not existing posts should be seen...
class Campaign < ActiveRecord::Base
attr_accessor hide_posts
...
end
Set the value in your edit method
class CampaignsController < ApplicationController
def edit
#campaign.hide_posts = params[:add_to] == 'existing_campaign'
...
end
Ensure the temporary variable is in your strong parameters (so that redisplay after failed update remembers to hide posts)
def campaign_params
params.require(:campaign).permit( :hide_posts, ...
Now on your view you can do...
= f.hidden_field :hide_posts
= f.simple_fields_for :posts do |post|
= render('post_fields', f: post) unless #campaign.hide_posts && post.object.persisted?
Updating with more specifics ...
You as the design need to decide if you want to move the logic to a helper. If you aren't aware of skinny controller / fat model or another strategy for organizing your codebase read this ... Link. For now, I'm going to assume you will use a helper.
The theory & some nitpicky things ...
What's going to happen with skinny controller is you need that parameter accessible in the view logic (but that would be insecure and violate the rails way). Since you shouldn't just expose params to the view, you instead are passing the message containing the parameter's value. Object orientation teaches us to use pass messages. Rails says to use instances variables (#something) in the controller and the view has access to those.
We need to setup the conditional logic next. One of the Rails ways is to use helpers to remove extraneous logic from the view or make it readable. This qualifies as something that unless you have another reason I'm not aware of - this should be in a helper. I would be tempted here to just test for boolean and call another partial for the extra view you make.
Which means I have to assume your tests will change too (if not done in a standard way, you have to include to get access to that method/object).
Specific to your question
link_toon the page calling the nested form should be true/false...
= link_to 'Add Post', edit_campaign_path(campaign, show_posts: false)
Your controller will have #show_posts in whatever action of the campaign controller you are using (edit or new usually). You need to set #show_posts = params[:show_posts]
Write the helper ...
helpers/campaign_helper.rb
def showPosts?(show_posts)
testPart == true ? 'only_comment' : 'post_fields'
end
A new partial ... which is basically the same, but drops the simple_fields_for loop which populates the other posts.
Your _form partial will change from what you had above to ...
= f.simple_fields_for :posts do |post|
= render 'posts_fields', f: post
To ...
...
#posts
= render showPosts?(#show_posts), f: post
...
Update, I tested all the parts & got it working with your exact syntax - I ended up using ternary operator in the helper.

Getting tripped up by verify_partial_doubles with rails 4 and rspec 3

I'm using authlogic for my user authentication and in my ApplicationController I have "current_user", "current_user_session", etc. defined and set as helper_methods.
I have an extremely simple view spec for my main index:
RSpec.describe "main/index.html.erb", :type => :view do
context "when not logged in" do
before do
allow(view).to receive(:current_user).and_return(nil)
end
it "has an h1" do
render
expect(rendered).to include('h1')
end
end
end
The problem is that if "mocks.verify_partial_doubles = true" in my config then this causes an impressively massive error as it dumps an entire object and then says at the bottom:
1) main/index.html.erb when not logged in has an h1
Failure/Error: allow(view).to receive(:current_user).and_return(nil)
#<#<Class:0x00000104c249d0>:.........
#rendered_views={}>> does not implement: current_user
Of course, it is recommended that verify_partial_doubles is set to true, but in doing so this breaks. I pulled this straight from the documentation:
https://www.relishapp.com/rspec/rspec-rails/v/3-1/docs/view-specs/view-spec#passing-view-spec-that-stubs-a-helper-method
If the method appears in ApplicationHelper it'll work. But if it's in ApplicationController and defined as a helper_method there's no such luck:
helper_method :current_user, ...
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
I want the protection that verify_partial_doubles provides, how can I work around this?
This is a known issue and the only way to get it working is to extract the methods into a module and include it in your view helpers and the controller.
More information at: https://github.com/rspec/rspec-rails/issues/1076
You can disable double verification for views as follows:
RSpec.configure do |config|
config.before(:each, type: :view) do
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = false
end
end
config.after(:each, type: :view) do
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
end
This way you'll be able to keep stubbing view methods with:
allow(view).to receive(:current_user).and_return(nil)
More information at: https://github.com/rspec/rspec-rails/issues/1076

inherited_resources alternate create method forbiddenattributes

I have two create methods (maybe could be structured better, but new to inherited_resources)
Basically, I want to redirect to a different page after create, I am getting a ForbiddenAttributes error using one method, but not the original Create action, I'm guessing there is some special way to use IH, but I am stumped on this one.
In my second action, I need to manually assign the params - I'm guessing I need to do this the IH way, that line is where it blows up so the question is how is IH achieving this without an error?
def create
if can? :create, LeaveRequest
create! { leave_requests_url }
end
end
def manage_create
#leave_request = LeaveRequest.new(params[:leave_request])
if can? :create, LeaveRequest
create! { manage_leave_requests_url }
end
end
def permitted_params
{:leave_request => params.fetch(:leave_request, {}).permit(:user_id, :controller, :manager_id, :part_day, :comment, :selected_dates, :status, :leave_type_id, leave_dates_attributes:
[:id, :leave_request_id, :hours, :date_requested, :_destroy])}
end
Route is defined as
match 'manage_create', to: 'leave_requests#manage_create', as: :manage_create_leave_request, via: [:post]
I'm using IH 1.4.1
I needed to add the full list of parameters to my manage_create function - I have absolutely no idea why it doesn't use the existing permitted_params method.
#leave_request = LeaveRequest.new(params[:leave_request].permit(:user_id, :controller, :manager_id, :part_day, :comment, :selected_dates, :status, :leave_type_id, leave_dates_attributes:
[:id, :leave_request_id, :hours, :date_requested, :_destroy]))

Rails 4 + Merit + Devise: set a badge for user after registration

I have a trouble at this situation. I followed this manual, but it don't helped me.
Here's my files:
routes.rb:
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', registrations: 'registrations' }
merit.rb:
Merit.setup do |config|
config.checks_on_each_request = true
end
Merit::Badge.create!(
id: 1,
name: 'just-registered',
description: 'Badge for register'
)
badge_rules.rb
module Merit
class BadgeRules
include Merit::BadgeRulesMethods
def initialize
grant_on 'registrations#create', badge: 'just-registered', model_name: 'User'
end
end
end
registrations_controller.rb
def create
#user = build_resource
super
end
And if make sense - when user is registered, to merit_actions table added a new record with target_model = 'registrations', not 'users'
Can someone tell me, what i'm doing wrong ?
That's expected behavior, merit_actions is internal to the gem and saves controller_path as it's target_model attribute. It is confusing, but it shouldn't affect your application. Is the badge being granted?

how do you stub a local variable in an rspec controller test

I've just implemented OmniAuth (using Ryan Bates' Screencast http://asciicasts.com/episodes/235-omniauth-part-1) and am writing Rspec tests for the functionality and have ran into trouble testing the authentifications#create action. I'm at quite a loss as to how to test this one -- in particular how to stub the local variable omniauth. No matter what I try I keep can't get any tests to work.
Taking a cut down version of the action, how would you test that a new is called on User for example
#cut down version of the authentifications controller code I am attempting to test
def create
omniauth = request.env["omniauth.auth"]
authentification = Authentification.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
....
user = User.new
....
end
#example test
it "should create a new user" do
subject.stub_chain(:request,:env) {{"omniauth.auth" => {'provider' =>1, 'uid' => 2}}}
User.should_receive(:new)
post :create
end
I Did that :
class SessionsController < ApplicationController
def create
#user = User.find_by_auth_hash(auth_hash)
end
def auth_hash
request.env['omniauth.auth']
end
end
describe SessionsController do
it 'should allow login' do
controller.stub!(:auth_hash).and_return({'provider' => 'twitter', 'uid' => '1234'})
get :create, :provider => 'twitter'
assigns(:user).should_not be_nil
end
end
Hope that helps.