├── .coveralls.yml ├── lib ├── action_parameter │ ├── version.rb │ ├── railtie.rb │ ├── helpers.rb │ └── base.rb ├── generators │ └── parameters │ │ ├── templates │ │ └── parameter_class.rb │ │ ├── USAGE │ │ └── parameters_generator.rb └── action_parameter.rb ├── .gitignore ├── Gemfile ├── .travis.yml ├── CHANGELOG.md ├── Rakefile ├── test ├── test_helper.rb └── action_parameter_test.rb ├── action_parameter.gemspec ├── MIT-LICENSE └── README.md /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | repo_token: UyOEGDlk0CRMGA13fGDYwg96fLKh1hOXs -------------------------------------------------------------------------------- /lib/action_parameter/version.rb: -------------------------------------------------------------------------------- 1 | module ActionParameter 2 | VERSION = "0.0.3".freeze 3 | end -------------------------------------------------------------------------------- /lib/generators/parameters/templates/parameter_class.rb: -------------------------------------------------------------------------------- 1 | class <%= name.camelcase %>Parameters < ActionParameter::Base 2 | 3 | end -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle 2 | *.gem 3 | .bundle 4 | .idea 5 | Gemfile.lock 6 | log/* 7 | pkg/* 8 | .ruby-gemset 9 | .ruby-version 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | gem 'mocha', '~> 0.13.2', require: false 6 | gem 'rake' 7 | gem 'coveralls', require: false 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | script: "bundle exec rake test" 3 | rvm: 4 | - 1.9.3 5 | - 2.0.0 6 | gemfile: 7 | - Gemfile 8 | notifications: 9 | email: false -------------------------------------------------------------------------------- /lib/generators/parameters/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Explain the generator 3 | 4 | Example: 5 | rails generate parameters Thing 6 | 7 | This will create: 8 | what/will/it/create 9 | -------------------------------------------------------------------------------- /lib/action_parameter.rb: -------------------------------------------------------------------------------- 1 | require 'action_pack' 2 | require 'active_support' 3 | require 'action_parameter/railtie' if defined?(Rails) 4 | 5 | module ActionParameter 6 | extend ActiveSupport::Autoload 7 | 8 | autoload :Base, 'action_parameter/base.rb' 9 | autoload :Helpers, 'action_parameter/helpers.rb' 10 | 11 | end 12 | -------------------------------------------------------------------------------- /lib/action_parameter/railtie.rb: -------------------------------------------------------------------------------- 1 | require 'rails/railtie' 2 | 3 | module ActionParameter 4 | class Railtie < Rails::Railtie 5 | 6 | initializer "action_parameter.helpers" do |app| 7 | ActiveSupport.on_load :action_controller do 8 | include ActionParameter::Helpers 9 | end 10 | end 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/generators/parameters/parameters_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators' 2 | require 'rails/generators/named_base' 3 | 4 | class ParametersGenerator < Rails::Generators::NamedBase 5 | source_root File.expand_path('../templates', __FILE__) 6 | 7 | def parameters 8 | template "parameter_class.rb", "app/parameters/#{name.underscore}_parameters.rb" 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.0.3 2 | 3 | * bug fixing 4 | * Adds class namespacing support when guessing parameter class. Thanks to @poporul. 5 | 6 | ### 0.0.2 7 | 8 | * enhancements 9 | * Adds `locals` method to permitted_params to create helpers methods on ActionParameter 10 | 11 | * deprecations 12 | * `permitted_params(locals: {})` in favor of `permitted_params.locals({})` 13 | * `rails g parameter` rails' generator in favor of `rails g parameters` -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | begin 3 | require 'bundler/setup' 4 | require 'bundler/gem_tasks' 5 | rescue LoadError 6 | raise 'You must `gem install bundler` and `bundle install` to run rake tasks' 7 | end 8 | 9 | require 'rdoc/task' 10 | 11 | RDoc::Task.new(:rdoc) do |rdoc| 12 | rdoc.rdoc_dir = 'rdoc' 13 | rdoc.title = 'ActionParameter' 14 | rdoc.options << '--line-numbers' 15 | rdoc_main = 'README.md' 16 | rdoc.rdoc_files.include('README.md') 17 | rdoc.rdoc_files.include('lib/**/*.rb') 18 | end 19 | 20 | require 'rake/testtask' 21 | 22 | Rake::TestTask.new(:test) do |t| 23 | t.libs << 'lib' 24 | t.libs << 'test' 25 | t.pattern = 'test/**/*_test.rb' 26 | t.verbose = false 27 | end 28 | 29 | task :default => :test -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'coveralls' 2 | Coveralls.wear! 3 | 4 | require 'bundler' 5 | 6 | Bundler.setup 7 | require 'test/unit' 8 | require 'mocha/setup' 9 | 10 | # Configure Rails 11 | ENV["RAILS_ENV"] = "test" 12 | 13 | require 'active_support' 14 | require 'action_controller' 15 | require 'action_dispatch/middleware/flash' 16 | 17 | $:.unshift File.expand_path('../../lib', __FILE__) 18 | require 'action_parameter' 19 | 20 | ActionParameter::Routes = ActionDispatch::Routing::RouteSet.new 21 | ActionParameter::Routes.draw do 22 | get '/:controller(/:action(/:id))' 23 | end 24 | 25 | class ApplicationController < ActionController::Base 26 | include ActionParameter::Routes.url_helpers 27 | end 28 | 29 | class ActiveSupport::TestCase 30 | setup do 31 | @routes = ActionParameter::Routes 32 | end 33 | end -------------------------------------------------------------------------------- /action_parameter.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "action_parameter/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "action_parameter" 7 | s.version = ActionParameter::VERSION.dup 8 | s.platform = Gem::Platform::RUBY 9 | s.summary = "Single Responsability Principle for Rails Controller's Parameters." 10 | s.email = "edelpero@gmail.com" 11 | s.homepage = "https://github.com/edelpero/action_parameter" 12 | s.description = "ActionParameter helps you move all your parameter's logic into it's own class. This way you'll keep your controllers dry and they would be easier to test." 13 | s.authors = ['Ezequiel Delpero'] 14 | s.license = "MIT" 15 | 16 | s.files = `git ls-files`.split("\n") 17 | s.test_files = `git ls-files -- test/*`.split("\n") 18 | s.require_paths = ["lib"] 19 | 20 | s.add_development_dependency('minitest', '~> 4.2') 21 | 22 | s.add_dependency('activesupport', '>= 3.0.0') 23 | s.add_dependency('actionpack', '>= 3.0.0') 24 | end -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2009-2013 Ezequiel Delpero. http://github.com/edelpero 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lib/action_parameter/helpers.rb: -------------------------------------------------------------------------------- 1 | module ActionParameter 2 | module Helpers 3 | 4 | protected 5 | 6 | # permitted_params: Returns an ActionParameter instance. 7 | # 8 | # == Options 9 | # 10 | # * options - Hash with one valid key: class. 11 | # * options[:class] - Symbol value with the name of the Parameters class you want to use. 12 | # 13 | # == Examples 14 | # 15 | # permitted_params(class: customer) # called from UsersController 16 | # 17 | # This will create an instance of CustomerParameters and also will make 18 | # 'params', 'controller_name' and 'action_name' helper methods 19 | # available on the CustomerParameters instace. 20 | def permitted_params(options = {}) 21 | parameter_class = permitted_params_class(options[:class]) 22 | @permitted_params ||= parameter_class.new(params) 23 | end 24 | 25 | # permitted_params_class: Returns a Parameters class. 26 | # 27 | # == Options 28 | # 29 | # * class_name - Symbol value with the name of the Parameters class you want to use. 30 | # 31 | # == Examples 32 | # 33 | # permitted_params_class(:customer) # called from PeopleController 34 | # # => CustomerParameters 35 | # 36 | # permitted_params_class(:customers) # called from PeopleController 37 | # # => CustomersParameters 38 | # 39 | # permitted_params_class() # called from PeopleController 40 | # # => PersonParameters 41 | def permitted_params_class(class_name = nil) 42 | class_name = class_name || self.class.name.sub(/Controller$/, '').singularize 43 | @permitted_params_class ||= "#{class_name.to_s.camelcase}Parameters".constantize 44 | end 45 | 46 | end 47 | end -------------------------------------------------------------------------------- /lib/action_parameter/base.rb: -------------------------------------------------------------------------------- 1 | module ActionParameter 2 | class Base 3 | 4 | attr_accessor :params 5 | 6 | # initialize: Initialize parameter class and creates controller_name and action_name helpers. 7 | # 8 | # == Options 9 | # 10 | # * params - The ActionController::Parameters instance from the controller who initialize this. 11 | def initialize(params) 12 | @params = params 13 | create_base_helpers 14 | end 15 | 16 | # locals: Creates helper methods for the ActionParameter instace. 17 | # 18 | # == Options 19 | # 20 | # * locals - Hash used to create helper methods available for the ActionParameter instance. 21 | # 22 | # == Examples 23 | # 24 | # * locals(new_method: @value, another_method: @other_value) 25 | # # => 'ActionParameter instace' 26 | # 27 | # Returns the ActionParameter instace. 28 | def locals(locals = {}) 29 | create_methods(locals) 30 | self 31 | end 32 | 33 | protected 34 | 35 | # create_base_helpers: Creates controller_name and action_name helper methods, every time an ActionParameter instace is created. 36 | def create_base_helpers 37 | locals = { controller_name: params[:controller], 38 | action_name: params[:action] } 39 | create_methods(locals) 40 | end 41 | 42 | # create_methods: Creates instance methods using locals hash's keys and values. 43 | # 44 | # == Options 45 | # 46 | # * locals - Hash used to create helper methods available for the ActionParameter instance. Methods will be named using the hash keys and they'll return the hash values. 47 | # 48 | # == Examples 49 | # 50 | # create_methods(current_user: @user, another_key: @another_variable) 51 | # 52 | # Will create 'current_user' and 'another_key' instance methods. 53 | # This methods will be aviable only in the current parameter class where create_method was called. 54 | # 'current_user' will return @user. 55 | # 'another_key' will return @another_variable 56 | def create_methods(locals = {}) 57 | locals = {} unless locals 58 | 59 | klass = class << self; self; end 60 | 61 | locals.each do |method_name, value| 62 | klass.send(:define_method, method_name) do 63 | value 64 | end 65 | end 66 | end 67 | 68 | end 69 | end -------------------------------------------------------------------------------- /test/action_parameter_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | UserParameters = Class.new(ActionParameter::Base) 4 | CustomerParameters = Class.new(ActionParameter::Base) 5 | 6 | Admin = Module.new 7 | Admin::UserParameters = Class.new(ActionParameter::Base) 8 | Admin::CustomerParameters = Class.new(ActionParameter::Base) 9 | 10 | module SharedActionMethods 11 | def permitted_params_without_arguments 12 | permitted_params 13 | render nothing: true 14 | end 15 | 16 | def permitted_params_with_class_attribute 17 | permitted_params(class: :customer) 18 | render nothing: true 19 | end 20 | 21 | def permitted_params_with_namespaced_class_attribute 22 | permitted_params(class: 'admin/customer') 23 | render nothing: true 24 | end 25 | 26 | def permitted_params_with_locals 27 | permitted_params.locals(current_user: 'user') 28 | render nothing: true 29 | end 30 | end 31 | 32 | class Admin::UsersController < ApplicationController 33 | include ActionParameter::Helpers 34 | include SharedActionMethods 35 | end 36 | 37 | class UsersController < ApplicationController 38 | include ActionParameter::Helpers 39 | include SharedActionMethods 40 | end 41 | 42 | class ActionParameterTest < ActionController::TestCase 43 | tests UsersController 44 | 45 | def test_permitted_params_without_arguments_is_an_instance_of_user_parameters 46 | get :permitted_params_without_arguments 47 | assert_equal('UserParameters', assigns(:permitted_params).class.to_s) 48 | end 49 | 50 | def test_permitted_params_with_class_attribute_returns_an_instance_of_customer_parameters 51 | get :permitted_params_with_class_attribute 52 | assert_equal('CustomerParameters', assigns(:permitted_params).class.to_s) 53 | end 54 | 55 | def test_permitted_params_instance_params_helper_method 56 | get :permitted_params_without_arguments 57 | assert_equal(@request.params, assigns(:permitted_params).params) 58 | end 59 | 60 | def test_permitted_params_instance_controller_name_helper_method 61 | get :permitted_params_without_arguments 62 | assert_equal(@request.params['controller'], assigns(:permitted_params).controller_name) 63 | end 64 | 65 | def test_permitted_params_instance_action_name_helper_method 66 | get :permitted_params_without_arguments 67 | assert_equal(@request.params['action'], assigns(:permitted_params).action_name) 68 | end 69 | 70 | def test_permitted_params_with_locals_should_respond_to_current_user_method 71 | get :permitted_params_with_locals 72 | assert_equal(true, assigns(:permitted_params).respond_to?(:current_user)) 73 | end 74 | end 75 | 76 | class ActionParameterNamespaceTest < ActionController::TestCase 77 | tests Admin::UsersController 78 | 79 | def test_permitted_params_without_arguments_is_an_instance_of_user_parameters 80 | get :permitted_params_without_arguments 81 | assert_equal('Admin::UserParameters', assigns(:permitted_params).class.to_s) 82 | end 83 | 84 | def test_permitted_params_with_class_attribute_returns_an_instance_of_customer_parameters 85 | get :permitted_params_with_class_attribute 86 | assert_equal('CustomerParameters', assigns(:permitted_params).class.to_s) 87 | end 88 | 89 | def test_permitted_params_with_namespaced_class_attribute_returns_an_instance_of_customer_parameters 90 | get :permitted_params_with_namespaced_class_attribute 91 | assert_equal('Admin::CustomerParameters', assigns(:permitted_params).class.to_s) 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/action_parameter.png)](http://badge.fury.io/rb/action_parameter) 2 | [![Build Status](https://travis-ci.org/edelpero/action_parameter.png?branch=master)](https://travis-ci.org/edelpero/action_parameter) 3 | [![Coverage Status](https://coveralls.io/repos/edelpero/action_parameter/badge.png)](https://coveralls.io/r/edelpero/action_parameter) 4 | [![Code Climate](https://codeclimate.com/github/edelpero/action_parameter.png)](https://codeclimate.com/github/edelpero/action_parameter) 5 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/edelpero/action_parameter/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 6 | 7 | ActionParameter 8 | =============== 9 | 10 | ActionParameter helps you move all your parameter's logic into it's own class. This way you'll keep your controllers dry and they would be easier to test. 11 | 12 | Before 13 | ------ 14 | 15 | ```ruby 16 | # app/controllers/users_controllers.rb 17 | class UsersController < ActionController::Base 18 | def create 19 | User.create(user_params) 20 | end 21 | 22 | private 23 | def user_params 24 | params.require(:user).permit(:name, :age) 25 | end 26 | end 27 | ``` 28 | 29 | After 30 | ----- 31 | 32 | ```ruby 33 | # app/controllers/users_controllers.rb 34 | class UsersController < ActionController::Base 35 | def create 36 | # It automatically deduces which Parameters class from Controller's name 37 | User.create(permitted_params.permit) 38 | end 39 | end 40 | ``` 41 | 42 | ```ruby 43 | # app/parameters/user_parameters.rb 44 | class UserParameters < ActionParameter::Base 45 | def permit 46 | params.require(:user).permit(:name, :age) 47 | end 48 | end 49 | ``` 50 | 51 | Install 52 | ------- 53 | 54 | ActionParameter works with Rails 3.0 onwards and Ruby 1.9.3 onwards. You can add it to your Gemfile with: 55 | 56 | ```ruby 57 | gem 'action_parameter' 58 | ``` 59 | 60 | Run the bundle command to install it. 61 | 62 | Usage 63 | ----- 64 | 65 | ####Generator 66 | 67 | ```ruby 68 | rails generate parameters [MODEL_NAME] 69 | ``` 70 | Will create **app/parameters/[model_name]_parameters.rb**. 71 | 72 | ####Controller Helpers 73 | 74 | - **permitted_params:** Returns an ActionParameter instance. 75 | 76 | ```ruby 77 | permitted_params(options={}) 78 | ``` 79 | 80 | #####Options Hash 81 | 82 | * **options** - Hash with one valid key: **:class**. 83 | * **options[:class]** - Symbol value with the name of the Parameters class you want to use. 84 | 85 | #####Example 1 86 | 87 | ```ruby 88 | # app/controllers/people_controllers.rb 89 | class PeopleController < ActionController::Base 90 | def create 91 | Person.create(permitted_params.permit) # This will call to PersonParameters' permit method 92 | end 93 | end 94 | ``` 95 | 96 | ```ruby 97 | # app/parameters/person_parameters.rb 98 | class PersonParameters < ActionParameter::Base 99 | def permit 100 | params.require(:person).permit(:name, :age) 101 | end 102 | end 103 | ``` 104 | 105 | #####Example 2 106 | 107 | ```ruby 108 | # app/controllers/people_controllers.rb 109 | class PeopleController < ActionController::Base 110 | def create 111 | Person.create(permitted_params(class: :user).sign_up) # This will call to UserParameters' sign_up method 112 | end 113 | end 114 | ``` 115 | 116 | ```ruby 117 | # app/parameters/user_parameters.rb 118 | class UserParameters < ActionParameter::Base 119 | def sign_up 120 | params.require(:person).permit(:name, :age) 121 | end 122 | end 123 | ``` 124 | 125 | ####Parameter Class Helpers 126 | 127 | #####Default Helpers 128 | 129 | - **params:** Returns params from the current controller request which instantiated the Parameter class. 130 | - **controller_name:** Returns the controller's name from which the Parameter class was instantiated. 131 | - **action_name:** Returns the action's name from the controller from which the Parameter class was instantiated. 132 | 133 | #####Creating New Helpers 134 | 135 | If you want to create new helper methods for your parameters class, just call **locals** method over **permitted_params**. Let say you want to make **@current_user** available for the UserParameter's class under the **user** method, then you'll need to use the **locals** method to tell the UserParameters class to create a new helper that returns **@current_user**. 136 | 137 | ```ruby 138 | permitted_params(class: :user).locals( user: @current_user, 139 | another_helper: @value ) 140 | ``` 141 | This will create **user** and **another_helper** methods and they will be available in UserParameters class. 142 | 143 | #####Example 144 | 145 | ```ruby 146 | # app/controllers/users_controllers.rb 147 | class UsersController < ActionController::Base 148 | def create 149 | User.create(permitted_params.locals(user: @current_user).permit) # This will call to UserParameters' permit method 150 | end 151 | end 152 | ``` 153 | 154 | ```ruby 155 | # app/parameters/user_parameters.rb 156 | class UserParameters < ActionParameter::Base 157 | def permit 158 | if user.admin? 159 | params.require(:person).permit(:name, :age, :admin) 160 | else 161 | params.require(:person).permit(:name, :age) 162 | end 163 | end 164 | end 165 | ``` 166 | 167 | RSpec 168 | ----- 169 | 170 | This example shows how to test using RSpec. 171 | 172 | Theses tests require your **test.rb** configured to **config.action_controller.action_on_unpermitted_parameters = :raise**. 173 | 174 | ```ruby 175 | # spec/parameters/user_parameters_spec.rb 176 | require "spec_helper" 177 | 178 | describe UserParameters do 179 | describe ".permit" do 180 | describe "when permitted parameters" do 181 | it "returns the cleaned parameters" do 182 | user_params = { first_name: "John", last_name: "Doe" } 183 | params = ActionController::Parameters.new(user: user_params) 184 | 185 | permitted_params = UserParameters.new(params).permit 186 | 187 | expect(permitted_params).to eq user_params.with_indifferent_access 188 | end 189 | end 190 | 191 | describe "when unpermitted parameters" do 192 | it "raises error" do 193 | user_params = { foo: "bar" } 194 | params = ActionController::Parameters.new(user: user_params) 195 | 196 | expect{ UserParameters.new(params).permit }. 197 | to raise_error(ActionController::UnpermittedParameters) 198 | end 199 | end 200 | end 201 | end 202 | 203 | ``` 204 | 205 | --------------------------------------------------------------------------------