rails_stuff 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +24 -0
  5. data/.travis.yml +8 -0
  6. data/Gemfile +21 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +350 -0
  9. data/Rakefile +22 -0
  10. data/bin/console +7 -0
  11. data/bin/git-hooks/pre-commit +14 -0
  12. data/bin/install_git_hooks +8 -0
  13. data/bin/setup +8 -0
  14. data/lib/net/http/debug.rb +26 -0
  15. data/lib/rails_stuff/helpers/all.rb +12 -0
  16. data/lib/rails_stuff/helpers/bootstrap.rb +34 -0
  17. data/lib/rails_stuff/helpers/forms.rb +21 -0
  18. data/lib/rails_stuff/helpers/links.rb +38 -0
  19. data/lib/rails_stuff/helpers/resource_form.rb +49 -0
  20. data/lib/rails_stuff/helpers/text.rb +28 -0
  21. data/lib/rails_stuff/helpers/translation.rb +29 -0
  22. data/lib/rails_stuff/helpers.rb +14 -0
  23. data/lib/rails_stuff/nullify_blank_attrs.rb +23 -0
  24. data/lib/rails_stuff/params_parser.rb +121 -0
  25. data/lib/rails_stuff/railtie.rb +54 -0
  26. data/lib/rails_stuff/random_uniq_attr.rb +48 -0
  27. data/lib/rails_stuff/redis_storage.rb +119 -0
  28. data/lib/rails_stuff/resources_controller/actions.rb +31 -0
  29. data/lib/rails_stuff/resources_controller/basic_helpers.rb +161 -0
  30. data/lib/rails_stuff/resources_controller/resource_helper.rb +31 -0
  31. data/lib/rails_stuff/resources_controller/responder.rb +21 -0
  32. data/lib/rails_stuff/resources_controller/sti_helpers.rb +62 -0
  33. data/lib/rails_stuff/resources_controller.rb +42 -0
  34. data/lib/rails_stuff/sort_scope.rb +71 -0
  35. data/lib/rails_stuff/statusable.rb +130 -0
  36. data/lib/rails_stuff/test_helpers/rails.rb +6 -0
  37. data/lib/rails_stuff/test_helpers/response.rb +34 -0
  38. data/lib/rails_stuff/types_tracker.rb +50 -0
  39. data/lib/rails_stuff/version.rb +14 -0
  40. data/lib/rails_stuff.rb +19 -0
  41. data/rails_stuff.gemspec +25 -0
  42. metadata +126 -0
@@ -0,0 +1,49 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ module RailsStuff
3
+ module Helpers
4
+ # Provides helper for SimpleForm.
5
+ module ResourceForm
6
+ # Generates `resource_form` helper to display form with basic arguments,
7
+ # elements, errors and options. Generated method can work without arguments
8
+ # in most of cases: it takes object from `resource` method.
9
+ #
10
+ # Use `namespace` to add additional path parts to form action:
11
+ #
12
+ # # this one will use [:site, resource]
13
+ # resource_form_for :site
14
+ #
15
+ # #### Options
16
+ #
17
+ # - `back_url` - default back url. Can be string with code, or hash for `url_for`.
18
+ # - `resource_method` - method to take resource from.
19
+ # - `method` - name of generated method.
20
+ #
21
+ def resource_form_for(namespace = nil, **options)
22
+ default_back_url =
23
+ case options[:back_url]
24
+ when Hash then "url_for(#{options[:back_url]})"
25
+ when String then options[:back_url]
26
+ else 'url_for(object)'
27
+ end
28
+ resource_method = options.fetch(:resource_method, :resource)
29
+ method_name = options.fetch(:method, :resource_form)
30
+ object_arg = (Array.wrap(namespace).map(&:inspect) + [resource_method]).join(', ')
31
+
32
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
33
+ def #{method_name}(object = [#{object_arg}], **options)
34
+ back_url = options.delete(:back_url) || #{default_back_url}
35
+ simple_form_for object, options do |f|
36
+ html = ActiveSupport::SafeBuffer.new
37
+ msg = f.object.errors[:base].first
38
+ html << content_tag(:div, msg, class: 'alert alert-danger') if msg
39
+ html << capture { yield(f) }
40
+ html << f.button(:submit, class: 'btn-primary')
41
+ html << ' '
42
+ html << link_to(translate_action(:cancel), back_url, class: :btn)
43
+ end
44
+ end
45
+ RUBY
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,28 @@
1
+ module RailsStuff
2
+ module Helpers
3
+ module Text
4
+ # Replaces blank values with cached placeholder from translations.
5
+ # When called with block, it'll check value for blankness, but returns
6
+ # block's result if value is present.
7
+ #
8
+ # replace_blank(description)
9
+ # replace_blank(tags) { tags.join(', ') }
10
+ # replace_blank(order.paid_at) { |x| l x, format: :long }
11
+ #
12
+ def replace_blank(value, &block)
13
+ if value.blank?
14
+ blank_placeholder
15
+ else
16
+ block_given? ? capture(value, &block) : value
17
+ end
18
+ end
19
+
20
+ # Default placeholder value.
21
+ def blank_placeholder
22
+ @_blank_placeholder ||= content_tag :small,
23
+ "(#{I18n.t(:'helpers.placeholder.blank', default: '-')})",
24
+ class: :'text-muted'
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ module RailsStuff
2
+ module Helpers
3
+ module Translation
4
+ # Translates & caches actions within `helpers.actions` scope.
5
+ def translate_action(action)
6
+ @translate_action ||= Hash.new do |h, key|
7
+ h[key] = I18n.t("helpers.actions.#{key}")
8
+ end
9
+ @translate_action[action]
10
+ end
11
+
12
+ # Translates & caches confirmations within `helpers.confirmations` scope.
13
+ def translate_confirmation(action)
14
+ @translate_confirmation ||= Hash.new do |h, key|
15
+ h[key] = I18n.t("helpers.confirmations.#{key}", default: [:'helpers.confirm'])
16
+ end
17
+ @translate_confirmation[action]
18
+ end
19
+
20
+ # Translates boolean values.
21
+ def yes_no(val)
22
+ @translate_yes_no ||= Hash.new do |h, key|
23
+ h[key] = I18n.t("helpers.yes_no.#{key}")
24
+ end
25
+ @translate_yes_no[val.to_s]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ module RailsStuff
2
+ module Helpers
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Bootstrap
6
+ autoload :Forms
7
+ autoload :Links
8
+ autoload :ResourceForm
9
+ autoload :Text
10
+ autoload :Translation
11
+
12
+ autoload :All
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
3
+ module RailsStuff
4
+ # Changes to `nil` assigned blank attributes.
5
+ #
6
+ # class App
7
+ # nullify_blank_attrs :site_url
8
+ # # ...
9
+ module NullifyBlankAttrs
10
+ def nullify_blank_attrs(*attrs)
11
+ nullify_blank_attrs_methods.class_eval do
12
+ attrs.each do |attr|
13
+ define_method("#{attr}=") { |val| super(val.presence) }
14
+ end
15
+ end
16
+ end
17
+
18
+ # Module to store generated methods, so they can be overriden in model.
19
+ def nullify_blank_attrs_methods
20
+ @nullify_blank_attrs_methods ||= Module.new.tap { |x| prepend x }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,121 @@
1
+ module RailsStuff
2
+ # Provides parsing and type-casting functions.
3
+ # Reraises all ocured errors with Error class, so you can handle it together:
4
+ #
5
+ # rescue_from RailsStuff::ParamsParser::Error, with: :render_bad_request
6
+ #
7
+ # You can define more parsing methods by extending with this module
8
+ # and using .parse:
9
+ #
10
+ # # models/params_parser
11
+ # module ParamsParser
12
+ # extend RailsStuff::ParamsParser
13
+ # extend self
14
+ #
15
+ # def parse_money(val)
16
+ # parse(val) { your_stuff(val) }
17
+ # end
18
+ # end
19
+ #
20
+ module ParamsParser
21
+ # This exceptions is wrapper for any exception occured in parser.
22
+ # Original exception message can be retrieved with `original_message` method.
23
+ class Error < ::StandardError
24
+ attr_reader :original_message, :value
25
+
26
+ def initialize(original_message = nil, value = nil)
27
+ message = "Error while parsing: #{value.inspect}"
28
+ @original_message = original_message || message
29
+ @value = value
30
+ super(message)
31
+ end
32
+
33
+ # Keeps message when passing instance to `raise`.
34
+ def exception(*)
35
+ self
36
+ end
37
+
38
+ # Show original messages in tests.
39
+ def to_s
40
+ "#{super} (#{original_message})"
41
+ end
42
+ end
43
+
44
+ extend self
45
+
46
+ # Parses value with specified block. Reraises occured error with Error.
47
+ def parse(val)
48
+ yield(val) unless val.nil?
49
+ rescue => e
50
+ raise Error.new(e.message, val), nil, e.backtrace
51
+ end
52
+
53
+ # Parses each value in array with specified block.
54
+ # Returns `nil` if `val` is not an array.
55
+ def parse_array(val)
56
+ parse(val) { val.map { |x| yield x unless x.nil? } } if val.is_a?(Array)
57
+ end
58
+
59
+ # :method: parse_int
60
+ # :call-seq: parse_int(val)
61
+ #
62
+ # Parse int value.
63
+
64
+ # :method: parse_int_array
65
+ # :call-seq: parse_int_array(val)
66
+ #
67
+ # Parses array of ints. Returns `nil` if `val` is not an array.
68
+
69
+ # :method: parse_float
70
+ # :call-seq: parse_float(val)
71
+ #
72
+ # Parse float value.
73
+
74
+ # :method: parse_float_array
75
+ # :call-seq: parse_float_array(val)
76
+ #
77
+ # Parses array of floats. Returns `nil` if `val` is not an array.
78
+
79
+ # :method: parse_string
80
+ # :call-seq: parse_string(val)
81
+ #
82
+ # Parse string value.
83
+
84
+ # :method: parse_string_array
85
+ # :call-seq: parse_string_array(val)
86
+ #
87
+ # Parses array of strings. Returns `nil` if `val` is not an array.
88
+
89
+ # Parsers for generic types, which are implemented with #to_i, #to_f & #to_s
90
+ # methods.
91
+ %w(string int float).each do |type|
92
+ block = :"to_#{type[0]}".to_proc
93
+
94
+ define_method "parse_#{type}" do |val|
95
+ parse(val, &block)
96
+ end
97
+
98
+ define_method "parse_#{type}_array" do |val|
99
+ parse_array(val, &block)
100
+ end
101
+ end
102
+
103
+ # Parse boolean using ActiveResord's parser.
104
+ def parse_boolean(val)
105
+ parse(val) do
106
+ @boolean_parser ||= ActiveRecord::Type::Boolean.new
107
+ @boolean_parser.type_cast_from_user(val)
108
+ end
109
+ end
110
+
111
+ # Parse time in current TZ using `Time.parse`.
112
+ def parse_datetime(val)
113
+ parse(val) { Time.zone.parse(val) || raise('Invalid datetime') }
114
+ end
115
+
116
+ # Parse JSON string.
117
+ def parse_json(val)
118
+ parse(val) { JSON.parse(val) if val.present? }
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,54 @@
1
+ require 'rails/railtie'
2
+
3
+ module RailsStuff
4
+ MODULES = {
5
+ nullify_blank_attrs: :model,
6
+ random_uniq_attr: :model,
7
+ statusable: :model,
8
+ resources_controller: [
9
+ :controller,
10
+ -> { ResourcesController.kaminari! if defined?(::Kaminari) },
11
+ ],
12
+ sort_scope: -> { defined?(::HasScope) && :controller },
13
+ }
14
+
15
+ class << self
16
+ # Set it to array of modules to load.
17
+ #
18
+ # # config/initializers/rails_stuff.rb
19
+ # RailsStuff.load_modules = [:statusable, :sort_scope]
20
+ attr_accessor :load_modules
21
+ # Override default base classes for models & controllers.
22
+ attr_writer :base_controller, :base_model
23
+
24
+ def base_controller
25
+ @base_controller || ActionController::Base
26
+ end
27
+
28
+ def base_model
29
+ @base_model || ActiveRecord::Base
30
+ end
31
+
32
+ # Extends base controller and model classes with modules.
33
+ # By default uses all modules. Use load_modules= to override this list.
34
+ def setup_modules!
35
+ modules_to_load = load_modules || MODULES.keys
36
+ MODULES.slice(*modules_to_load).each do |m, (type, init)|
37
+ m = const_get m.to_s.camelize
38
+ case type.respond_to?(:call) ? type.call : type
39
+ when :controller
40
+ RailsStuff.base_controller.extend m
41
+ when :model
42
+ RailsStuff.base_model.extend m
43
+ end
44
+ init.try!(:call)
45
+ end
46
+ end
47
+ end
48
+
49
+ class Railtie < Rails::Railtie
50
+ initializer :rails_stuff_setup_modules, after: :load_config_initializers do
51
+ RailsStuff.setup_modules!
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,48 @@
1
+ module RailsStuff
2
+ # Provides save way to generate uniq random values for ActiveRecord models.
3
+ # You need to make field nullable and add unique index on it.
4
+ # The way it works:
5
+ #
6
+ # - Instance is saved as usual
7
+ # - If random fields are not empty, it does nothing
8
+ # - Generates random value and tries to update instance
9
+ # - If `RecordNotUnique` is occured, it keeps trying to generate new values.
10
+ #
11
+ module RandomUniqAttr
12
+ DEFAULT_GENERATOR = ->(*) { SecureRandom.hex(32) }
13
+
14
+ class << self
15
+ # Made from `Devise.firendly_token` with increased length.
16
+ def friendly_token(length = 32)
17
+ SecureRandom.urlsafe_base64(length).tr('lIO0', 'sxyz')
18
+ end
19
+ end
20
+
21
+ # Generates necessary methods and setups on-create callback for the `field`.
22
+ # You can optionally pass custom generator function:
23
+ #
24
+ # random_uniq_attr(:code) { |instance| my_random(instance) }
25
+ #
26
+ def random_uniq_attr(field, &block)
27
+ set_method = :"set_#{field}"
28
+ generate_method = :"generate_#{field}"
29
+
30
+ after_create set_method, unless: :"#{field}?"
31
+
32
+ # def self.generate_key
33
+ define_singleton_method generate_method, &(block || DEFAULT_GENERATOR)
34
+
35
+ # def set_key
36
+ define_method(set_method) do
37
+ begin
38
+ raise 'Available only for persisted record' unless persisted?
39
+ transaction(requires_new: true) do
40
+ update_column field, self.class.send(generate_method, self)
41
+ end
42
+ rescue ActiveRecord::RecordNotUnique
43
+ retry
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,119 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+ require 'active_support/core_ext/module/remove_method'
3
+ require 'active_support/core_ext/object/blank'
4
+
5
+ module RailsStuff
6
+ # Provides methods to store data in redis. Can be easily integrated into
7
+ # ActiveRecor or other class.
8
+ #
9
+ # Redis is accessed via with_redis method which uses redis_pool
10
+ # (default to `Rails.redis_pool`, see `pooled_redis` gem) to checkout connection.
11
+ # Basic methods are #get, #delete and #set.
12
+ #
13
+ # Redis keys are generated from requested key and redis_prefix
14
+ # (default to underscored class name). You can pass an array as a key and all the
15
+ # parts will be concatenated with `:`. #set automalically generates
16
+ # sequential keys, if given key is `nil` (last element of array is `nil`).
17
+ #
18
+ # It uses `dump` and `load` methods to encode values
19
+ # (by default delegated to `Marshal`).
20
+ module RedisStorage
21
+ # Serializers
22
+ delegate :dump, :load, to: Marshal
23
+
24
+ # Redis connections pool. Default to `Rails.redis_pool`.
25
+ # Override this method to change it.
26
+ def redis_pool
27
+ Rails.redis_pool
28
+ end
29
+
30
+ # Options to use in SET command. Use to set EX, or smth.
31
+ def redis_set_options
32
+ {}
33
+ end
34
+
35
+ # :method: redis_pool=
36
+ # :call-seq: redis_pool=
37
+ #
38
+ # Set redis_pool.
39
+
40
+ # :method: redis_set_options=
41
+ # :call-seq: redis_set_options=
42
+ #
43
+ # Set redis_set_options.
44
+
45
+ # Setters that overrides methods, so new values are inherited without recursive `super`.
46
+ [:redis_pool, :redis_set_options].each do |name|
47
+ define_method "#{name}=" do |val|
48
+ singleton_class.class_eval do
49
+ remove_possible_method(name)
50
+ define_method(name) { val }
51
+ end
52
+ end
53
+ end
54
+
55
+ # Checkout connection & run block with it.
56
+ def with_redis(&block)
57
+ redis_pool.with(&block)
58
+ end
59
+
60
+ # Prefix that used in every key for a model. Default to pluralized model name.
61
+ def redis_prefix
62
+ @redis_prefix ||= name.underscore
63
+ end
64
+
65
+ # Override default redis_prefix.
66
+ attr_writer :redis_prefix
67
+
68
+ # Generates key for given `id`(s) prefixed with #redis_prefix.
69
+ # Multiple ids are joined with `:`.
70
+ def redis_key_for(id)
71
+ "#{redis_prefix}:#{Array(id).join(':')}"
72
+ end
73
+
74
+ # Generates key to store current maximum id. Examples:
75
+ #
76
+ # users_id_seq
77
+ # user_id_seq:eu
78
+ def redis_id_seq_key(id = [])
79
+ postfix = Array(id).join(':')
80
+ "#{redis_prefix}_id_seq#{":#{postfix}" if postfix.present?}"
81
+ end
82
+
83
+ # Generate next ID. It stores counter separately and uses
84
+ # it to retrieve next id.
85
+ def next_id(*args)
86
+ with_redis { |redis| redis.incr(redis_id_seq_key(*args)) }
87
+ end
88
+
89
+ # Reset ID counter.
90
+ def reset_id_seq(*args)
91
+ with_redis { |redis| redis.del(redis_id_seq_key(*args)) }
92
+ end
93
+
94
+ # Saves value to redis. If `id` is `nil`, it's generated with #next_id.
95
+ # Returns last part of id / generated id.
96
+ def set(id, value, options = {})
97
+ id = Array(id)
98
+ id.push(nil) if id.empty?
99
+ id[id.size - 1] ||= next_id(id[0..-2])
100
+ with_redis do |redis|
101
+ redis.set(redis_key_for(id), dump(value), redis_set_options.merge(options))
102
+ end
103
+ id.last
104
+ end
105
+
106
+ # Reads value from redis.
107
+ def get(id)
108
+ return unless id
109
+ with_redis { |redis| redis.get(redis_key_for(id)).try { |data| load(data) } }
110
+ end
111
+
112
+ # Remove record from redis.
113
+ def delete(id)
114
+ return true unless id
115
+ with_redis { |redis| redis.del(redis_key_for(id)) }
116
+ true
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,31 @@
1
+ module RailsStuff
2
+ module ResourcesController
3
+ # Basic actions for resources controller.
4
+ module Actions
5
+ def new
6
+ build_resource
7
+ end
8
+
9
+ def create(options = {})
10
+ if create_resource
11
+ options[:location] = after_save_url
12
+ end
13
+ respond_with(resource, options)
14
+ end
15
+
16
+ def update(options = {})
17
+ if update_resource
18
+ options[:location] = after_save_url
19
+ end
20
+ respond_with(resource, options)
21
+ end
22
+
23
+ def destroy(options = {})
24
+ resource.destroy
25
+ options[:location] = after_destroy_url
26
+ flash_errors!
27
+ respond_with(resource, options)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,161 @@
1
+ module RailsStuff
2
+ module ResourcesController
3
+ module BasicHelpers
4
+ extend ActiveSupport::Concern
5
+
6
+ class << self
7
+ # Make source_for_collection use Kaminari-style scopes
8
+ # to paginate relation.
9
+ def kaminari!
10
+ define_method(:source_for_collection) do
11
+ source_relation.page(params[:page]).per(params[:per])
12
+ end
13
+ end
14
+ end
15
+
16
+ included do
17
+ helper_method :resource, :collection
18
+ self.after_save_action = :show
19
+ end
20
+
21
+ module ClassMethods
22
+ attr_writer :resource_class,
23
+ :resource_param_name,
24
+ :permitted_attrs
25
+
26
+ # Defines action to redirect after resource was saved. Default to `:show`.
27
+ attr_accessor :after_save_action
28
+
29
+ # Resource class for controller. Default to class, based on
30
+ # demodulized controller name.
31
+ def resource_class
32
+ @resource_class ||=
33
+ Object.const_get(name.to_s.demodulize.sub(/Controller$/, '').singularize)
34
+ end
35
+
36
+ # Key to lookup for resource attributes in `params`.
37
+ # Default to class'es `param_key`.
38
+ def resource_param_name
39
+ @resource_param_name ||= resource_class.model_name.param_key
40
+ end
41
+
42
+ # Class-level permitted attributes.
43
+ #
44
+ # `attr_reader`, default to `[]`.
45
+ def permitted_attrs
46
+ @permitted_attrs ||= []
47
+ end
48
+
49
+ # Concats `@permitted_attrs` variable with given attrs.
50
+ def permit_attrs(*attrs)
51
+ permitted_attrs.concat attrs
52
+ end
53
+
54
+ # This method overrides default `has_scope`. It calls default implementation
55
+ # and overrides `collection` to use `apply_scope`.
56
+ def has_scope(*)
57
+ super.tap do
58
+ define_method :collection do
59
+ @_collection ||= apply_scopes(source_for_collection)
60
+ end
61
+ protected :collection
62
+ end
63
+ end
64
+
65
+ # Prevent CanCan's implementation.
66
+ def authorize_resource
67
+ raise 'use `before_action :authorize_resource!` instead'
68
+ end
69
+ end
70
+
71
+ protected
72
+
73
+ # Accesss resources collection.
74
+ def collection
75
+ @_collection ||= source_for_collection
76
+ end
77
+
78
+ # End-point relation to be used as source for `collection`.
79
+ def source_for_collection
80
+ source_relation
81
+ end
82
+
83
+ # Relation which is used to find and build resources.
84
+ def source_relation
85
+ self.class.resource_class
86
+ end
87
+
88
+ # Resource found by `params[:id]`
89
+ def resource
90
+ @_resource ||= source_relation.find params[:id]
91
+ end
92
+
93
+ # Instantiate resource with attrs from `resource_params`.
94
+ def build_resource(attrs = resource_params)
95
+ @_resource = source_relation.new(attrs)
96
+ end
97
+
98
+ # Builds and saves resource.
99
+ def create_resource
100
+ build_resource
101
+ resource.save
102
+ end
103
+
104
+ # Updates resource with `resource_params`.
105
+ def update_resource(attrs = resource_params)
106
+ resource.update_attributes(attrs)
107
+ end
108
+
109
+ # Flashes errors in a safe way. Joins `full_messages` and truncates
110
+ # result to avoid cookies overflow.
111
+ def flash_errors!(errors = resource.errors, max_length = 100)
112
+ flash[:error] = errors.full_messages.join("\n").truncate(max_length) if errors.any?
113
+ end
114
+
115
+ # URL to be used in `Location` header & to redirect to after
116
+ # resource was created/updated. Default uses `self.class.after_save_action`.
117
+ def after_save_url
118
+ action = self.class.after_save_action
119
+ if action == :index
120
+ url_for action: :index
121
+ else
122
+ url_for action: action, id: resource
123
+ end
124
+ end
125
+
126
+ # URL to be used in `Location` header & to redirect after
127
+ # resource was destroyed. Default to `:index` action.
128
+ def after_destroy_url
129
+ url_for action: :index
130
+ end
131
+
132
+ # Override it to return permited params. By default it returns params
133
+ # using `self.class.resource_param_name` and `permitted_attrs` methods.
134
+ def resource_params
135
+ @_resource_params ||= begin
136
+ key = self.class.resource_param_name
137
+ params.permit(key => permitted_attrs)[key] || {}
138
+ end
139
+ end
140
+
141
+ # Default permitted attributes are taken from class method. Override it
142
+ # to implement request-based permitted attrs.
143
+ def permitted_attrs
144
+ self.class.permitted_attrs
145
+ end
146
+
147
+ # Default authorization implementation.
148
+ # Uses `#authorize!` method which is not implemented here
149
+ # (use CanCan or other implementation).
150
+ def authorize_resource!
151
+ action = action_name.to_sym
152
+ target =
153
+ case action
154
+ when :index, :create, :new then self.class.resource_class.new
155
+ else resource
156
+ end
157
+ authorize!(action, target)
158
+ end
159
+ end
160
+ end
161
+ end