rails_stuff 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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