rails_stuff 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b4d05c4a1131bbc5cf6e653451d50aa7f09a2902
4
- data.tar.gz: 7463af96e7f8c64b3399fb6ce6e9c701b852533a
3
+ metadata.gz: 0a6bd04d2e9f8e71c29e7b70748e3f1529715e0c
4
+ data.tar.gz: fa5c7003161e787b1886de2b3c6e10955c812728
5
5
  SHA512:
6
- metadata.gz: 51871432200910ce3c7e71f4cad3f4112a87c5ddba4bae34a773b179192333e6e1ace524337f8ae23fc9a22d51eaf03c3efca4b202e019f55cb2d294c6c0770e
7
- data.tar.gz: 439fa7ded29c96cf88db521c40c6a60039d8f6db7e74fe961cbebe97c9e907f9e8f875ec5369eb1abba3e0d1f5de6ec04f4298c7943e5b0f709814769a3c6660
6
+ metadata.gz: fa5e2a577b9de17bab80c9caf6e6e323d9637969f135e701042745271c9b60422d627a27c07311355feae66c2d29d4918ae498f5db542db1c0a50713a7c09c90
7
+ data.tar.gz: a70871bf1a0814cd0551a6ad8a7f596b0912751925c04a5353920fdafcd55234344d7e8bb8a0e90c467455fcd3223cf0e3060b0d3c45bd0e21fc57be72d2632d
data/.rubocop.yml CHANGED
@@ -1,23 +1,31 @@
1
1
  AllCops:
2
- RunRailsCops: true
2
+ Exclude:
3
+ - lib/rails_stuff/generators/*/templates/*.rb
4
+ - tmp/**/*
5
+ Rails: {Enabled: true}
3
6
 
7
+ Style/Alias: {Enabled: false}
4
8
  Style/AlignParameters:
5
9
  # Disable, till rubocop supports combination of styles.
6
10
  # Use one of this styles where appropriate, keep it clean, compact and readable.
7
11
  Enabled: false
8
- EnforcedStyle:
9
- - aligned
10
- - with_fixed_indentation
12
+ # EnforcedStyle:
13
+ # - with_first_parameter
14
+ # - with_fixed_indentation
11
15
  Style/ClosingParenthesisIndentation: {Enabled: false}
12
16
  Style/Documentation: {Enabled: false}
13
17
  Style/DotPosition: {EnforcedStyle: trailing}
14
18
  Style/IfUnlessModifier: {Enabled: false}
19
+ Style/Lambda: {Enabled: false}
15
20
  Style/ModuleFunction: {Enabled: false}
21
+ Style/MultilineMethodCallIndentation: {EnforcedStyle: indented}
16
22
  Style/MultilineOperationIndentation: {EnforcedStyle: indented}
23
+ Style/NestedParenthesizedCalls: {Enabled: false}
17
24
  Style/PredicateName: {Enabled: false}
18
25
  Style/SignalException: {EnforcedStyle: only_raise}
19
26
  Style/SpaceInsideHashLiteralBraces: {EnforcedStyle: no_space}
20
- Style/TrailingComma: {Enabled: false}
27
+ Style/TrailingCommaInArguments: {Enabled: false}
28
+ Style/TrailingCommaInLiteral: {EnforcedStyleForMultiline: comma}
21
29
 
22
30
  Metrics/AbcSize: {Max: 21}
23
31
  Metrics/LineLength: {Max: 100}
data/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ ## Unreleased
2
+
3
+ ### Controllers
4
+
5
+ - `belongs_to`.
6
+ - `resource_helper` generates enquirer method.
7
+ - `resources_controller kaminari: false` to skip kaminari for the only controller.
8
+ - `has_sort_scope` can use custom order method.
9
+ - Fix: removed source_for_collection from action_methods.
10
+
11
+ ### Models
12
+
13
+ - Statusable supports mappings (store status as integer) and suffixes.
14
+ - AssociationWriter to override `#field=` & `#field_id=` in single instruction.
15
+ - Limit retries count for RandomUniqAttr (default to 10).
16
+
17
+ ### Helpers
18
+
19
+ - `Helpers::Translation` methods can raise errors on missing translations.
20
+ It respects app's `raise_on_missing_translations`, and can be configured manually.
21
+
22
+ ### Tests
23
+
24
+ - Concurrency helper.
25
+ - RSpec configurator.
26
+
27
+ Misc
28
+
29
+ - RequireNested to require all files in subdirectory.
30
+ - `rails g concern %parent%/%module%` generator for concerns.
31
+
1
32
  ## 0.4.0
2
33
 
3
34
  - TypesTracker defines scopes for every type.
data/Gemfile CHANGED
@@ -18,7 +18,7 @@ group :development do
18
18
  gem 'rspec-its', '~> 1.1.0'
19
19
  gem 'rspec-rails', '~> 3.3.3'
20
20
 
21
- gem 'rubocop', '~> 0.33.0'
21
+ gem 'rubocop', '~> 0.37.2'
22
22
 
23
23
  gem 'coveralls', '~> 0.8.2', require: false
24
24
  end
data/README.md CHANGED
@@ -16,6 +16,8 @@ Collection of useful modules for Rails.
16
16
 
17
17
  - __[NullifyBlankAttrs](#nullifyblankattrs)__
18
18
  Proxies writers to replace empty values with `nil`.
19
+ - __[AssociationWriter](#associationwriter)__
20
+ Override both writers with single instruction.
19
21
  - __[RandomUniqAttr](#randomuniqattr)__
20
22
  You generate random values for attributes, it'll ensure they are uniq.
21
23
  - __[Statusable](#statusable)__
@@ -34,6 +36,9 @@ Collection of useful modules for Rails.
34
36
  `require_permitted` helper.
35
37
  - __UrlFor__
36
38
  `#url_for_keeping_params` merges passed options with request's query params.
39
+ - __[RequireNested](#requirenested)__
40
+ helper to load files in subdirectory.
41
+ - `rails g concern %parent%/%module%` generator for concerns.
37
42
 
38
43
  #### Helpers:
39
44
 
@@ -53,6 +58,10 @@ __[Helpers usage](#helpers-usage)__
53
58
 
54
59
  - __Response__
55
60
  `#json_body` to test json responses.
61
+ - __Configurator__
62
+ Provides useful basic configuration for RSpec.
63
+ - __Concurrency__
64
+ Helpers for testing with concurrent requests.
56
65
 
57
66
  __[Test helpers usage](#test-helpers-usage)__
58
67
 
@@ -104,7 +113,7 @@ check docs & code (press `t` on github) if you miss something.
104
113
 
105
114
  ### ResourcesController
106
115
 
107
- Similar to [InheriteResource](https://github.com/josevalim/inherited_resources)
116
+ Similar to [InheritedResource](https://github.com/josevalim/inherited_resources)
108
117
  but much simpler. It adds implementations for basic actions and
109
118
  accessors for collection and resource. There is no options for almost everything,
110
119
  but it's easy to extend.
@@ -127,14 +136,20 @@ class ProjectsController < ApplicationController
127
136
  after_save_action: :index,
128
137
  source_relation: -> { user.projects }
129
138
  resource_helper :user
139
+
140
+ # or just
141
+ resources_controller sti: true,
142
+ after_save_action: :index,
143
+ belongs_to: :user
144
+ # `belongs_to: [:user, optional: true]` for shallow routes
145
+
130
146
  permit_attrs :name
131
147
  permit_attrs_for Project::External, :company
132
148
  permit_attrs_for Project::Internal, :department
133
149
  end
134
150
  ```
135
151
 
136
- There is built-in support for pagination with Kaminari.
137
- It's enabled automatically if `kaminari` gem is loaded.
152
+ There is built-in support for `has_scope` gem and pagination with Kaminari.
138
153
 
139
154
  Currently depends on `gem 'responders', '> 2.0'`.
140
155
 
@@ -144,7 +159,7 @@ Currently depends on `gem 'responders', '> 2.0'`.
144
159
  # in controller
145
160
  extend RailsStuff::SortScope # when using without railtie
146
161
 
147
- sort_scope by: [:name, :created_at, :balance], default: [:name]
162
+ has_sort_scope by: [:name, :created_at, :balance], default: [:name]
148
163
 
149
164
  # this scope will accept
150
165
  # - `sort=name`
@@ -166,6 +181,17 @@ extend RailsStuff::NullifyBlankAttrs # when using without railtie
166
181
  nullify_blank_attrs :email, :title
167
182
  ```
168
183
 
184
+ ### AssociationWriter
185
+
186
+ ActiveRecord's association can be updated with object and by object id.
187
+ Owerwrite this both writers with single instruction:
188
+
189
+ ```ruby
190
+ association_writer :product do |val|
191
+ super(val).tap { update_price if product }
192
+ end
193
+ ```
194
+
169
195
  ### RandomUniqAttr
170
196
 
171
197
  Uses database's UNIQUE constraints and transactions to generate uniq random values.
@@ -194,8 +220,9 @@ end
194
220
 
195
221
  ```ruby
196
222
  class User < ActiveRecord::Base
197
- extend RailsStuff::RandomUniqAttr # when using without railtie
223
+ extend RailsStuff::Statusable # when using without railtie
198
224
 
225
+ # Setup with array and it'll store status values as strings.
199
226
  STATUSES = %i(confirmed banned)
200
227
  has_status_field # uses #status field and STATUSES as values
201
228
 
@@ -204,6 +231,12 @@ class User < ActiveRecord::Base
204
231
  # :prefix is used for methods that are build
205
232
  end
206
233
 
234
+ class Order < ActiveRecord::Base
235
+ # Provide hash, and it'll store mapped values in database.
236
+ STATUSES_MAPPING = {submitted: 1, confirmed: 2, delivered: 3}
237
+ has_status_field
238
+ end
239
+
207
240
  user = User.first
208
241
 
209
242
  # And you get:
@@ -212,6 +245,8 @@ User.confirmed.subs_active
212
245
  User.not_banned.not_subs_expired
213
246
  # Useful with has_scope
214
247
  User.with_status(param[:status]).with_subscription_status(params[:subs_status])
248
+ # When using mapped values, scopes will accept status names:
249
+ Order.with_status(:confirmed)
215
250
 
216
251
  # Translation & select helpers (requires activemodel_translation gem)
217
252
  User.status_name(:active)
@@ -240,9 +275,7 @@ class Project
240
275
 
241
276
  # If you want to show all available descendants in development
242
277
  # (ex. in dropdown/select), you definitely want this:
243
- eager_load_types! # will load all files in app/models/project
244
- # or pass folder explicitly:
245
- eager_load_types!('lib/path/to/projects')
278
+ require_nested # will load all .rb files in app/models/project
246
279
  end
247
280
 
248
281
  class Project::Big < Project
@@ -324,6 +357,21 @@ params.require_permitted(:access_token, :refresh_token)
324
357
  params.permit(:access_token, :refresh_token).require(:access_token, :refresh_token)
325
358
  ```
326
359
 
360
+ ### RequireNested
361
+
362
+ ```ruby
363
+ class Project < ApplicationRecord
364
+ # To load all files in app/models/project with require_dependency:
365
+ require_nested
366
+ # or pass folder explicitly:
367
+ require_nested('lib/path/to/projects')
368
+
369
+ # For non-rails apps use
370
+ RailsStuff::RequireNested.require_nested
371
+ # or call RailsStuff::RequireNested.setup to add #require_nested to Module
372
+ end
373
+ ```
374
+
327
375
  ### Helpers usage
328
376
 
329
377
  Include helper module into `ApplicationHelper`.
@@ -380,6 +428,21 @@ Note that `hashie` conflicts with `Hash` methods, so `.json_body.key` or
380
428
  `.json_body.hash` will not work as expected (or at all).
381
429
  Use `json_body['key']` instead.
382
430
 
431
+ There is `Configurator` with useful RSpec configs:
432
+
433
+ ```ruby
434
+ RSpec.configure do |config|
435
+ RailsStuff::TestHelpers::Configurator.tap do |configurator|
436
+ # Setup DatabaseCleaner with basic settings:
437
+ configurator.database_cleaner(config)
438
+ # Flush redis after suite and exampes with `flush_redis: true`:
439
+ configurator.redis(config)
440
+ # Run debugger after failed tests:
441
+ configurator.debug(config)
442
+ end
443
+ end
444
+ ```
445
+
383
446
  ### PluginManager
384
447
 
385
448
  Provides simple way to create jQuery plugins. Create class and PluginManager
@@ -0,0 +1,15 @@
1
+ module RailsStuff
2
+ # ActiveRecord's association can be updated with object and by object id.
3
+ # Owerwrite this both writers with single instruction:
4
+ #
5
+ # association_writer :product do |val|
6
+ # super(val).tap { update_price if product }
7
+ # end
8
+ #
9
+ module AssociationWriter
10
+ def association_writer(name, &block)
11
+ define_method("#{name}=", &block)
12
+ define_method("#{name}_id=", &block)
13
+ end
14
+ end
15
+ end
@@ -1,13 +1,15 @@
1
1
  require 'rails/railtie'
2
2
 
3
3
  module RailsStuff
4
- MODULES = {
4
+ MODULES = { # rubocop:disable MutableConstant
5
+ require_nested: [:require, -> { RequireNested.setup }],
6
+ association_writer: :model,
5
7
  nullify_blank_attrs: :model,
6
8
  random_uniq_attr: :model,
7
9
  statusable: :model,
8
10
  resources_controller: [
9
11
  :controller,
10
- -> { ResourcesController.kaminari! if defined?(::Kaminari) },
12
+ -> { ResourcesController.use_kaminari! if defined?(::Kaminari) },
11
13
  ],
12
14
  sort_scope: -> { defined?(::HasScope) && :controller },
13
15
  strong_parameters: -> { defined?(ActionController::Parameters) && :require },
@@ -53,5 +55,9 @@ module RailsStuff
53
55
  initializer :rails_stuff_setup_modules, after: :load_config_initializers do
54
56
  RailsStuff.setup_modules!
55
57
  end
58
+
59
+ generators do
60
+ require 'rails_stuff/generators/concern/concern_generator'
61
+ end
56
62
  end
57
63
  end
@@ -0,0 +1,11 @@
1
+ Description:
2
+ Generates concern for the class.
3
+
4
+ Example:
5
+ `rails generate concern User::Authentication`
6
+
7
+ create app/models/user/authentication.rb
8
+
9
+ `rails generate concern admin_controller/localized`
10
+
11
+ create app/controllers/admin_controller/localized.rb
@@ -0,0 +1,26 @@
1
+ require 'rails/generators'
2
+
3
+ module RailsStuff
4
+ module Generators
5
+ class ConcernGenerator < Rails::Generators::NamedBase # :nodoc:
6
+ argument :base,
7
+ type: :string,
8
+ required: false,
9
+ banner: 'Base dir with the class',
10
+ description: 'Use it when class is not inside app/models or app/controllers.'
11
+
12
+ namespace 'rails:concern'
13
+ source_root File.expand_path('../templates', __FILE__)
14
+
15
+ def create_concern_files
16
+ template 'concern.rb', File.join(base_path, "#{file_path}.rb")
17
+ end
18
+
19
+ private
20
+
21
+ def base_path
22
+ base || file_path.include?('_controller/') ? 'app/controllers' : 'app/models'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ module <%= class_name %>
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ end
6
+
7
+ module ClassMethods
8
+ def class_method
9
+ end
10
+ end
11
+
12
+ def instance_method
13
+ end
14
+ end
@@ -2,7 +2,7 @@ module RailsStuff
2
2
  module Helpers
3
3
  # Link helpers for basic actions.
4
4
  module Links
5
- ICONS = {
5
+ ICONS = { # rubocop:disable MutableConstant
6
6
  destroy: -> { translate_action(:destroy) },
7
7
  edit: -> { translate_action(:edit) },
8
8
  new: -> { translate_action(:new) },
@@ -1,10 +1,18 @@
1
1
  module RailsStuff
2
2
  module Helpers
3
3
  module Translation
4
+ class << self
5
+ def i18n_raise
6
+ @i18n_raise ||= defined?(Rails) && ActionView::Base.raise_on_missing_translations
7
+ end
8
+
9
+ attr_writer :i18n_raise
10
+ end
11
+
4
12
  # Translates & caches actions within `helpers.actions` scope.
5
13
  def translate_action(action)
6
14
  @translate_action ||= Hash.new do |h, key|
7
- h[key] = I18n.t("helpers.actions.#{key}")
15
+ h[key] = I18n.t("helpers.actions.#{key}", raise: Translation.i18n_raise)
8
16
  end
9
17
  @translate_action[action]
10
18
  end
@@ -12,7 +20,10 @@ module RailsStuff
12
20
  # Translates & caches confirmations within `helpers.confirmations` scope.
13
21
  def translate_confirmation(action)
14
22
  @translate_confirmation ||= Hash.new do |h, key|
15
- h[key] = I18n.t("helpers.confirmations.#{key}", default: [:'helpers.confirm'])
23
+ h[key] = I18n.t("helpers.confirmations.#{key}",
24
+ default: [:'helpers.confirm'],
25
+ raise: Translation.i18n_raise,
26
+ )
16
27
  end
17
28
  @translate_confirmation[action]
18
29
  end
@@ -20,7 +31,7 @@ module RailsStuff
20
31
  # Translates boolean values.
21
32
  def yes_no(val)
22
33
  @translate_yes_no ||= Hash.new do |h, key|
23
- h[key] = I18n.t("helpers.yes_no.#{key}")
34
+ h[key] = I18n.t("helpers.yes_no.#{key}", raise: Translation.i18n_raise)
24
35
  end
25
36
  @translate_yes_no[val.to_s]
26
37
  end
@@ -10,6 +10,7 @@ module RailsStuff
10
10
  #
11
11
  module RandomUniqAttr
12
12
  DEFAULT_GENERATOR = ->(*) { SecureRandom.hex(32) }
13
+ MAX_ATTEMPTS = 10
13
14
 
14
15
  class << self
15
16
  # Made from `Devise.firendly_token` with increased length.
@@ -23,9 +24,10 @@ module RailsStuff
23
24
  #
24
25
  # random_uniq_attr(:code) { |instance| my_random(instance) }
25
26
  #
26
- def random_uniq_attr(field, &block)
27
+ def random_uniq_attr(field, **options, &block)
27
28
  set_method = :"set_#{field}"
28
29
  generate_method = :"generate_#{field}"
30
+ max_attempts = options.fetch(:max_attempts) { MAX_ATTEMPTS }
29
31
 
30
32
  after_create set_method, unless: :"#{field}?"
31
33
 
@@ -34,12 +36,15 @@ module RailsStuff
34
36
 
35
37
  # def set_key
36
38
  define_method(set_method) do
39
+ attempt = 0
37
40
  begin
38
41
  raise 'Available only for persisted record' unless persisted?
39
42
  transaction(requires_new: true) do
40
43
  update_column field, self.class.send(generate_method, self)
41
44
  end
42
45
  rescue ActiveRecord::RecordNotUnique
46
+ attempt += 1
47
+ raise if attempt > max_attempts
43
48
  retry
44
49
  end
45
50
  end
@@ -0,0 +1,22 @@
1
+ require 'active_support/dependencies'
2
+
3
+ module RailsStuff
4
+ module RequireNested
5
+ class << self
6
+ # Make #require_nested available in module.
7
+ def setup
8
+ Module.include(self)
9
+ end
10
+ end
11
+
12
+ module_function
13
+
14
+ # Requires nested modules with `require_dependency`.
15
+ # Pass custom directory to require its content.
16
+ # By default uses caller's filename with stripped `.rb` extension from.
17
+ def require_nested(dir = 0)
18
+ dir = caller_locations(dir + 1, 1)[0].path.sub(/\.rb$/, '') if dir.is_a?(Integer)
19
+ Dir["#{dir}/*.rb"].each { |file| require_dependency file }
20
+ end
21
+ end
22
+ end
@@ -3,16 +3,6 @@ module RailsStuff
3
3
  module BasicHelpers
4
4
  extend ActiveSupport::Concern
5
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
6
  included do
17
7
  helper_method :resource, :collection
18
8
  self.after_save_action = :show
@@ -51,17 +41,6 @@ module RailsStuff
51
41
  permitted_attrs.concat attrs
52
42
  end
53
43
 
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
44
  # Prevent CanCan's implementation.
66
45
  def authorize_resource
67
46
  raise 'use `before_action :authorize_resource!` instead'
@@ -117,15 +96,21 @@ module RailsStuff
117
96
  def after_save_url
118
97
  action = self.class.after_save_action
119
98
  if action == :index
120
- url_for action: :index
99
+ index_url
121
100
  else
122
101
  url_for action: action, id: resource
123
102
  end
124
103
  end
125
104
 
126
105
  # URL to be used in `Location` header & to redirect after
127
- # resource was destroyed. Default to `:index` action.
106
+ # resource was destroyed. Default to #index_url.
128
107
  def after_destroy_url
108
+ index_url
109
+ end
110
+
111
+ # Extracted from #after_save_url and #after_destroy_url because they use
112
+ # same value. It's easier to override this urls in one place.
113
+ def index_url
129
114
  url_for action: :index
130
115
  end
131
116
 
@@ -134,7 +119,7 @@ module RailsStuff
134
119
  def resource_params
135
120
  @_resource_params ||= begin
136
121
  key = self.class.resource_param_name
137
- params.permit(key => permitted_attrs)[key] || {}
122
+ params.permit(key => permitted_attrs)[key] || params.class.new
138
123
  end
139
124
  end
140
125
 
@@ -0,0 +1,35 @@
1
+ module RailsStuff
2
+ module ResourcesController
3
+ module BelongsTo
4
+ class << self
5
+ # Builds lambda to use as `#source_relation`.
6
+ def source_relation(subject, collection, optional: false, **)
7
+ check_subject = :"#{subject}?"
8
+ lambda do
9
+ if optional && !send(check_subject)
10
+ super()
11
+ else
12
+ send(subject).public_send(collection)
13
+ end
14
+ end
15
+ end
16
+
17
+ # Builds lambda to use as `#index_url`
18
+ def index_url(subject, *, field: nil, param: nil)
19
+ field ||= :"#{subject}_id"
20
+ param ||= field
21
+ -> { url_for action: :index, param => resource.public_send(field) }
22
+ end
23
+ end
24
+
25
+ # Defines resource helper and source relation
26
+ def resource_belongs_to(subject, resource_helper: true, urls: true, **options)
27
+ resource_helper(subject) if resource_helper
28
+ collection = options[:collection] || resource_class.model_name.plural
29
+ source_relation_proc = BelongsTo.source_relation(subject, collection, options)
30
+ protected define_method(:source_relation, &source_relation_proc)
31
+ protected define_method(:index_url, &BelongsTo.index_url(subject, urls)) if urls
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ module RailsStuff
2
+ module ResourcesController
3
+ module HasScopeHelpers
4
+ # This method overrides default `has_scope` so it include helpers from
5
+ # InstanceMethods only when this method is called.
6
+ def has_scope(*)
7
+ super.tap { include InstanceMethods }
8
+ end
9
+
10
+ module InstanceMethods
11
+ protected
12
+
13
+ # Applies `has_scope` scopes to original source.
14
+ def source_for_collection
15
+ apply_scopes(super)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ module RailsStuff
2
+ module ResourcesController
3
+ module KaminariHelpers
4
+ protected
5
+
6
+ # Make source_for_collection use Kaminari-style scopes to paginate relation.
7
+ def source_for_collection
8
+ super.page(params[:page]).per(params[:per])
9
+ end
10
+
11
+ module ConfigMethods
12
+ def use_kaminari!(val = true)
13
+ @use_kaminari = val
14
+ end
15
+
16
+ def use_kaminari?
17
+ @use_kaminari
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -15,15 +15,21 @@ module RailsStuff
15
15
  # - `class` - class name, default to `resource_name.classify`
16
16
  # - `param` - param name, default to `resource_name.foreign_key`
17
17
  def resource_helper(resource_name, **options)
18
- helper_method resource_name
18
+ helper_method resource_name, :"#{resource_name}?"
19
19
  resource_name = resource_name.to_s
20
+ class_name = options[:class] || resource_name.classify
21
+ param_key = options[:param] || resource_name.foreign_key
20
22
 
21
23
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
24
+ protected
25
+
22
26
  def #{resource_name}
23
- @#{resource_name} ||= #{options[:class] || resource_name.classify}.
24
- find params[:#{options[:param] || resource_name.foreign_key}]
27
+ @#{resource_name} ||= #{class_name}.find(params[:#{param_key}])
28
+ end
29
+
30
+ def #{resource_name}?
31
+ params.key?(:#{param_key})
25
32
  end
26
- protected :#{resource_name}
27
33
  RUBY
28
34
  end
29
35
  end
@@ -7,34 +7,46 @@ module RailsStuff
7
7
  module ResourcesController
8
8
  extend ActiveSupport::Autoload
9
9
 
10
- class << self
11
- delegate :kaminari!, to: 'RailsStuff::ResourcesController::BasicHelpers'
12
- end
13
-
14
10
  autoload :Actions
15
11
  autoload :BasicHelpers
12
+ autoload :BelongsTo
13
+ autoload :HasScopeHelpers
14
+ autoload :KaminariHelpers
15
+ autoload :ResourceHelper
16
16
  autoload :Responder
17
17
  autoload :StiHelpers
18
- autoload :ResourceHelper
18
+
19
+ extend KaminariHelpers::ConfigMethods
20
+
21
+ class << self
22
+ def inject(base, **options)
23
+ base.include BasicHelpers
24
+ base.include KaminariHelpers if options.fetch(:kaminari) { use_kaminari? }
25
+ base.include StiHelpers if options[:sti]
26
+ base.include Actions
27
+ base.extend HasScopeHelpers
28
+ base.extend ResourceHelper
29
+ base.extend BelongsTo
30
+ end
31
+ end
19
32
 
20
33
  # Setups basic actions and helpers in resources controller.
21
34
  #
22
35
  # #### Options
23
36
  #
24
37
  # - `sti` - include STI helpers
38
+ # - `kaminari` - include Kaminari helpers
25
39
  # - `after_save_action` - action to use for `after_save_url`
26
40
  # - `source_relation` - override `source_relation`
27
41
  def resources_controller(**options)
28
- include BasicHelpers
29
- include StiHelpers if options[:sti]
30
- include Actions
31
- extend ResourceHelper
42
+ ResourcesController.inject(self, **options)
32
43
 
33
44
  respond_to :html
34
45
  self.responder = Responder
35
46
  self.after_save_action = options[:after_save_action] || after_save_action
36
47
 
37
- if options[:source_relation] # rubocop:disable GuardClause
48
+ resource_belongs_to(*options[:belongs_to]) if options[:belongs_to]
49
+ if options[:source_relation]
38
50
  protected define_method(:source_relation, &options[:source_relation])
39
51
  end
40
52
  end
@@ -23,7 +23,8 @@ module RailsStuff
23
23
  #
24
24
  # - `by` - array of available fields to sort by,
25
25
  # - `default` - default sort expression,
26
- # - `only` - bypassed to `has_scope` to limit actions (default to `:index`).
26
+ # - `only` - bypassed to `has_scope` to limit actions (default to `:index`),
27
+ # - `order_method` - use custom method to sort instead of `.order`.
27
28
  #
28
29
  # rubocop:disable ClassVars
29
30
  def has_sort_scope(config = {})
@@ -31,6 +32,7 @@ module RailsStuff
31
32
  default = config[:default] || :id
32
33
  allowed = Array.wrap(config[:by]).map(&:to_s)
33
34
  only_actions = config.fetch(:only, :index)
35
+ order_method = config.fetch(:order_method, :order)
34
36
  # Counter added into scope name to allow to define multiple scopes in same controller.
35
37
  has_scope("sort_#{@@_sort_scope_id += 1}",
36
38
  as: :sort,
@@ -39,7 +41,8 @@ module RailsStuff
39
41
  only: only_actions,
40
42
  type: :any,
41
43
  ) do |c, scope, val|
42
- scope.order(SortScope.filter_param(val, c.params, allowed, default))
44
+ sort_args = SortScope.filter_param(val, c.params, allowed, default)
45
+ scope.public_send(order_method, sort_args)
43
46
  end
44
47
  end
45
48
  # rubocop:enable ClassVars
@@ -15,10 +15,13 @@ module RailsStuff
15
15
  # - `#status_sym`
16
16
  # - `status_select_options` helper.
17
17
  #
18
+ # It supports mapped statuses, just provide a hash with
19
+ # `{status_name => interna_value}` instead of array of statuses.
18
20
  module Statusable
19
21
  # Defines all helpers working with `field` (default to `status`).
20
22
  # List of values can be given as second argument, otherwise it'll
21
- # be read from const with pluralized name of `field` (eg. default to STATUSES).
23
+ # be read from consts using pluralized name of `field`
24
+ # (eg. default to `STATUSES_MAPPING`, `STATUSES`).
22
25
  #
23
26
  # #### Options
24
27
  #
@@ -28,29 +31,19 @@ module RailsStuff
28
31
  # has_status_field :delivery_status, %i(shipped delivered)
29
32
  #
30
33
  # # this defines #delivery_shipped?, #delivery_shipped! methods
31
- # has_status_field :delivery_status, %i(shipped delivered), prefix: :delivery
34
+ # has_status_field :delivery_status, %i(shipped delivered), prefix: :delivery_
35
+ #
36
+ # - `suffix` - similar to `prefix`.
32
37
  #
33
38
  # - `validate` - additional options for validatior. `false` to disable it.
34
- def has_status_field(field = :status, statuses = nil, **options) # rubocop:disable AbcSize
35
- statuses ||= const_get(field.to_s.pluralize.upcase)
36
- prefix = options[:prefix]
37
-
38
- if options[:validate] != false
39
- validates_inclusion_of field,
40
- {in: statuses.map(&:to_s)}.merge!(options.fetch(:validate, {}))
41
- end
42
-
43
- statusable_methods.generate_field_methods field, statuses
44
-
45
- # Scope with given status. Useful for has_scope.
46
- scope "with_#{field}", ->(status) { where(field => status) }
47
-
48
- statuses.map(&:to_s).each do |status_name|
49
- # Scopes for every status.
50
- scope "#{prefix}#{status_name}", -> { where(field => status_name) }
51
- scope "not_#{prefix}#{status_name}", -> { where.not(field => status_name) }
52
- statusable_methods.status_accessor field, status_name, prefix
39
+ def has_status_field(field = :status, statuses = nil, **options)
40
+ unless statuses
41
+ const_name = "#{field.to_s.pluralize.upcase}_MAPPING"
42
+ const_name = field.to_s.pluralize.upcase unless const_defined?(const_name)
43
+ statuses = const_get(const_name)
53
44
  end
45
+ generator = statuses.is_a?(Hash) ? MappedBuilder : Builder
46
+ generator.new(self, field, statuses, options).generate
54
47
  end
55
48
 
56
49
  # Module to hold generated methods.
@@ -58,49 +51,94 @@ module RailsStuff
58
51
  # Include generated methods with a module, not right in class.
59
52
  @statusable_methods ||= Module.new.tap do |m|
60
53
  m.const_set :ClassMethods, Module.new
61
- m.extend MethodsGenerator
62
54
  include m
63
55
  extend m::ClassMethods
64
56
  end
65
57
  end
66
58
 
67
- # Generates methods.
68
- module MethodsGenerator
69
- # Generates all methods for the `field`.
70
- def generate_field_methods(field, statuses)
71
- field_accessor field
72
- translation_helpers field
73
- select_options_helper field, statuses
59
+ # Generates methods and scopes.
60
+ class Builder
61
+ attr_reader :model, :field, :statuses, :options, :prefix, :suffix
62
+ alias_method :statuses_list, :statuses
63
+
64
+ def initialize(model, field, statuses, **options)
65
+ @model = model
66
+ @field = field
67
+ @statuses = statuses
68
+ @options = options
69
+ @prefix = options[:prefix]
70
+ @suffix = options[:suffix]
71
+ end
72
+
73
+ def generate
74
+ validations unless options[:validate] == false
75
+ field_reader
76
+ field_writer
77
+ select_options_helper
78
+ translation_helpers
79
+ field_scope
80
+ value_methods
81
+ end
82
+
83
+ def validations
84
+ model.validates_inclusion_of field,
85
+ {in: statuses.map(&:to_s)}.merge!(options.fetch(:validate, {}))
86
+ end
87
+
88
+ # Scope with given status. Useful for has_scope.
89
+ def field_scope
90
+ field = self.field
91
+ define_scope "with_#{field}", ->(status) { where(field => status) }
92
+ end
93
+
94
+ def value_methods
95
+ field = self.field
96
+ statuses.map(&:to_s).each do |status_name|
97
+ # Scopes for every status.
98
+ define_scope "#{prefix}#{status_name}#{suffix}",
99
+ -> { where(field => status_name) }
100
+ define_scope "not_#{prefix}#{status_name}#{suffix}",
101
+ -> { where.not(field => status_name) }
102
+ status_accessor status_name, status_name
103
+ end
74
104
  end
75
105
 
76
106
  # Generates methods for specific value.
77
- def status_accessor(field, status_name, prefix = nil)
107
+ def status_accessor(status_name, value)
108
+ field = self.field
109
+
78
110
  # Shortcut to check status.
79
- define_method "#{prefix}#{status_name}?" do
80
- self[field] == status_name
111
+ define_method "#{prefix}#{status_name}#{suffix}?" do
112
+ self[field] == value
81
113
  end
82
114
 
83
115
  # Shortcut to update status.
84
- define_method "#{prefix}#{status_name}!" do
85
- update_attributes!(field => status_name)
116
+ define_method "#{prefix}#{status_name}#{suffix}!" do
117
+ update_attributes!(field => value)
86
118
  end
87
119
  end
88
120
 
89
- def field_accessor(field)
90
- # Make field accept sympbols.
121
+ # Make field accept sympbols.
122
+ def field_writer
91
123
  define_method "#{field}=" do |val|
92
124
  val = val.to_s if val.is_a?(Symbol)
93
125
  super(val)
94
126
  end
127
+ end
95
128
 
96
- # Status as symbol.
129
+ # Status as symbol.
130
+ def field_reader
131
+ field = self.field
97
132
  define_method "#{field}_sym" do
98
- val = self[field]
133
+ val = send(field)
99
134
  val && val.to_sym
100
135
  end
101
136
  end
102
137
 
103
- def translation_helpers(field)
138
+ def translation_helpers
139
+ field = self.field
140
+ sym_method = "#{field}_sym"
141
+
104
142
  # Class-level translation helper.
105
143
  generate_class_method "#{field}_name" do |status|
106
144
  t(".#{field}_name.#{status}") if status
@@ -108,22 +146,99 @@ module RailsStuff
108
146
 
109
147
  # Translation helper.
110
148
  define_method "#{field}_name" do
111
- val = send field
149
+ val = send(sym_method)
112
150
  self.class.t(".#{field}_name.#{val}") if val
113
151
  end
114
152
  end
115
153
 
116
- def select_options_helper(field, statuses)
154
+ def select_options_helper
155
+ statuses_list = self.statuses_list
117
156
  translation_method = :"#{field}_name"
118
157
  # Returns array compatible with select_options helper.
119
158
  generate_class_method "#{field}_select_options" do |args = {}|
120
- filtered_statuses = statuses - Array.wrap(args[:except])
159
+ filtered_statuses = statuses_list - Array.wrap(args[:except])
121
160
  filtered_statuses.map { |x| [send(translation_method, x), x] }
122
161
  end
123
162
  end
124
163
 
164
+ # Rails 4 doesn't use `instance_exec` for scopes, so we do it manually.
165
+ def define_scope(name, body)
166
+ model.singleton_class.send(:define_method, name) do |*args|
167
+ all.scoping { instance_exec(*args, &body) } || all
168
+ end
169
+ end
170
+
171
+ def define_method(method, &block)
172
+ model.statusable_methods.send(:define_method, method, &block)
173
+ end
174
+
125
175
  def generate_class_method(method, &block)
126
- const_get(:ClassMethods).send(:define_method, method, &block)
176
+ model.statusable_methods::ClassMethods.send(:define_method, method, &block)
177
+ end
178
+ end
179
+
180
+ # Generates methods and scopes when status names are mapped to internal values.
181
+ class MappedBuilder < Builder
182
+ attr_reader :mapping, :statuses_list
183
+
184
+ def initialize(*)
185
+ super
186
+ @mapping = statuses.with_indifferent_access
187
+ @statuses_list = statuses.keys
188
+ end
189
+
190
+ def validations
191
+ model.validates_inclusion_of field,
192
+ {in: statuses.values}.merge!(options.fetch(:validate, {}))
193
+ end
194
+
195
+ # Scope with given status. Useful for has_scope.
196
+ def field_scope
197
+ field = self.field
198
+ mapping = self.mapping
199
+ define_scope "with_#{field}", ->(status) do
200
+ values = Array.wrap(status).map { |x| mapping.fetch(x, x) }
201
+ where(field => values)
202
+ end
203
+ end
204
+
205
+ def value_methods
206
+ field = self.field
207
+ statuses.each do |status_name, value|
208
+ # Scopes for every status.
209
+ define_scope "#{prefix}#{status_name}#{suffix}", -> { where(field => value) }
210
+ define_scope "not_#{prefix}#{status_name}#{suffix}", -> { where.not(field => value) }
211
+ status_accessor status_name, value
212
+ end
213
+ end
214
+
215
+ def field_reader
216
+ field = self.field
217
+ inverse_mapping = statuses.stringify_keys.invert
218
+
219
+ # Returns status name.
220
+ define_method field do |mapped = false|
221
+ val = super()
222
+ return val unless mapped && val
223
+ mapped = inverse_mapping[val]
224
+ raise "Missing mapping for value #{val.inspect}" unless mapped
225
+ mapped
226
+ end
227
+
228
+ # Status as symbol.
229
+ define_method "#{field}_sym" do
230
+ val = public_send(field, true)
231
+ val && val.to_sym
232
+ end
233
+ end
234
+
235
+ def field_writer
236
+ mapping = self.mapping
237
+ # Make field accept sympbols.
238
+ define_method "#{field}=" do |val|
239
+ val = val.to_s if val.is_a?(Symbol)
240
+ super(mapping.fetch(val, val))
241
+ end
127
242
  end
128
243
  end
129
244
  end
@@ -0,0 +1,71 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/array/wrap'
3
+
4
+ module RailsStuff
5
+ module TestHelpers
6
+ module Concurrency
7
+ extend ActiveSupport::Concern
8
+
9
+ class << self
10
+ # Default threads count
11
+ attr_accessor :threads_count
12
+ end
13
+ @threads_count = 3
14
+
15
+ module ClassMethods
16
+ # Defines subject which runs parent's value in multiple threads concurrently.
17
+ # Define `thread_args` or `threads_count` with `let` to configure it.
18
+ def concurrent_subject!
19
+ metadata[:concurrent] = true
20
+ subject do
21
+ super_proc = super()
22
+ args = defined?(thread_args) && thread_args
23
+ args ||= defined?(threads_count) && threads_count
24
+ -> { concurrently(args, &super_proc) }
25
+ end
26
+ end
27
+
28
+ # Runs given block in current context and nested context with concurrent subject.
29
+ #
30
+ # subject { -> { increment_value_once } }
31
+ # # This will create 2 examples. One for current contex, and one
32
+ # # for current context where subject will run multiple times concurrently.
33
+ # check_concurrent do
34
+ # it { should change { value }.by(1) }
35
+ # end
36
+ def check_concurrent(&block)
37
+ instance_eval(&block)
38
+ context 'running multiple times concurrently' do
39
+ concurrent_subject!
40
+ instance_eval(&block)
41
+ end
42
+ end
43
+ end
44
+
45
+ # Runs block concurrently in separate threads.
46
+ # Pass array of args arrays to run each thread with its own arguments.
47
+ # Or pass Integer to run specified threads count with same arguments.
48
+ # Default is to run Concurrency.threads_count threads.
49
+ #
50
+ # concurrently { do_something }
51
+ # concurrently(5) { do_something }
52
+ # concurrently([[1, opt: true], [2, opt: false]]) do |arg, **options|
53
+ # do_something(arg, options)
54
+ # end
55
+ # # It'll automatically wrap single args into Array:
56
+ # concurrently(1, 2, {opt: true}, {opt: false}, [1, opt: false]) { ... }
57
+ #
58
+ def concurrently(thread_args = nil)
59
+ thread_args ||= Concurrency.threads_count
60
+ threads =
61
+ case thread_args
62
+ when Integer
63
+ Array.new(thread_args) { Thread.new { yield } }
64
+ else
65
+ thread_args.map { |args| Thread.new { yield(*Array.wrap(args)) } }
66
+ end
67
+ threads.each(&:join)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,60 @@
1
+ module RailsStuff
2
+ module TestHelpers
3
+ # Collection of useful RSpec configurations.
4
+ #
5
+ # RailsStuff::TestHelpers::Configurator.tap do |configurator|
6
+ # configurator.database_cleaner(config)
7
+ # # ...
8
+ # end
9
+ #
10
+ module Configurator
11
+ module_function
12
+
13
+ # Setups database cleaner to use strategy depending on metadata.
14
+ # By default it uses `:transaction` for all examples and `:truncation`
15
+ # for features and examples with `concurrent: true`.
16
+ #
17
+ # Other types can be tuned with `config.cleaner_strategy` hash &
18
+ # `config.cleaner_strategy.default`.
19
+ def database_cleaner(config)
20
+ config.use_transactional_fixtures = false
21
+ config.add_setting :cleaner_strategy
22
+ config.cleaner_strategy = {feature: :truncation}
23
+ config.cleaner_strategy.default = :transaction
24
+ config.around do |ex|
25
+ strategy = ex.metadata[:concurrent] && :truncation
26
+ strategy ||= config.cleaner_strategy[ex.metadata[:type]]
27
+ options = strategy == :truncation ? {except: %w(spatial_ref_sys)} : {}
28
+ DatabaseCleaner.strategy = strategy, options
29
+ DatabaseCleaner.cleaning { ex.run }
30
+ end
31
+ end
32
+
33
+ # Setups redis to flush db after suite and before each example with
34
+ # `flush_redis: :true`. `Rails.redis` client is used by default.
35
+ # Can be tuned with `config.redis`.
36
+ def redis(config)
37
+ config.add_setting :redis
38
+ config.redis = Rails.redis if defined?(Rails.redis)
39
+ config.before { |ex| config.redis.flushdb if ex.metadata[:flush_redis] }
40
+ config.after(:suite) { config.redis.flushdb }
41
+ end
42
+
43
+ # Runs debugger after each failed example. Uses `pry` by default and
44
+ # runs only for examples with `:debug` tag. This can be configured
45
+ # with `:debugger` and `:filter` options respectively:
46
+ #
47
+ # configurator.debug(config, filter: {my_tag: :val}, debugger: true)
48
+ # # to disable filter:
49
+ # configurator.debug(config, filter: nil)
50
+ def debug(config, filter: {debug: true}, debugger: :pry)
51
+ config.after(filter) do |ex|
52
+ if ex.exception
53
+ debugger == :pry ? binding.pry : self.debugger # rubocop:disable Debugger
54
+ ex.exception # noop
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -4,3 +4,6 @@ require 'rails_stuff/test_helpers/response'
4
4
  ActionDispatch::TestResponse.class_eval do
5
5
  include RailsStuff::TestHelpers::Response
6
6
  end
7
+
8
+ require 'rails_stuff/test_helpers/concurrency'
9
+ require 'rails_stuff/test_helpers/configurator'
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/class/attribute'
2
+ require 'active_support/deprecation'
2
3
 
3
4
  module RailsStuff
4
5
  # Adds `types_list` method which tracks all descendants.
@@ -29,7 +30,7 @@ module RailsStuff
29
30
  else
30
31
  types_list << self
31
32
  end
32
- if types_tracker_base.respond_to?(:scope) && # rubocop:disable GuardClause
33
+ if types_tracker_base.respond_to?(:scope) &&
33
34
  !types_tracker_base.respond_to?(model_name.element)
34
35
  type_name = name
35
36
  types_tracker_base.scope model_name.element, -> { where(type: type_name) }
@@ -44,10 +45,13 @@ module RailsStuff
44
45
 
45
46
  # Shortcut to eager load all descendants.
46
47
  def eager_load_types!(dir = nil)
47
- dir ||= "#{Rails.root}/app/models/#{to_s.underscore}"
48
- Dir["#{dir}/*.rb"].each { |file| require_dependency file }
48
+ RequireNested.require_nested(dir || 2) # skip 1 more because of deprecation
49
49
  end
50
50
 
51
+ ActiveSupport::Deprecation.deprecate_methods self,
52
+ deprecator: ActiveSupport::Deprecation.new('0.6', 'RailsStuff'),
53
+ eager_load_types!: 'Use RequireNested.require_nested instead'
54
+
51
55
  # Tracks all descendants automatically.
52
56
  def inherited(base)
53
57
  super
@@ -5,7 +5,7 @@ module RailsStuff
5
5
 
6
6
  module VERSION #:nodoc:
7
7
  MAJOR = 0
8
- MINOR = 4
8
+ MINOR = 5
9
9
  TINY = 0
10
10
  PRE = nil
11
11
 
data/lib/rails_stuff.rb CHANGED
@@ -6,13 +6,15 @@ module RailsStuff
6
6
  extend ActiveSupport::Autoload
7
7
 
8
8
  autoload :Helpers
9
+ autoload :AssociationWriter
9
10
  autoload :NullifyBlankAttrs
10
11
  autoload :ParamsParser
11
12
  autoload :RandomUniqAttr
12
13
  autoload :RedisStorage
14
+ autoload :RequireNested
13
15
  autoload :ResourcesController
14
- autoload :Statusable
15
16
  autoload :SortScope
17
+ autoload :Statusable
16
18
  autoload :TypesTracker
17
19
  end
18
20
 
data/rails_stuff.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.name = 'rails_stuff'
8
8
  spec.version = RailsStuff::VERSION::STRING
9
9
  spec.authors = ['Max Melentiev']
10
- spec.email = ['m.melentiev@corp.mail.ru']
10
+ spec.email = ['melentievm@gmail.com']
11
11
 
12
12
  spec.summary = 'Collection of useful modules for Rails'
13
13
  spec.homepage = 'https://github.com/printercu/rails_stuff'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_stuff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max Melentiev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-10-01 00:00:00.000000000 Z
11
+ date: 2016-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '10.0'
55
55
  description:
56
56
  email:
57
- - m.melentiev@corp.mail.ru
57
+ - melentievm@gmail.com
58
58
  executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
@@ -76,7 +76,11 @@ files:
76
76
  - lib/assets/stylesheets/rails_stuff/_media_queries.scss
77
77
  - lib/net/http/debug.rb
78
78
  - lib/rails_stuff.rb
79
+ - lib/rails_stuff/association_writer.rb
79
80
  - lib/rails_stuff/engine.rb
81
+ - lib/rails_stuff/generators/concern/USAGE
82
+ - lib/rails_stuff/generators/concern/concern_generator.rb
83
+ - lib/rails_stuff/generators/concern/templates/concern.rb
80
84
  - lib/rails_stuff/helpers.rb
81
85
  - lib/rails_stuff/helpers/all.rb
82
86
  - lib/rails_stuff/helpers/bootstrap.rb
@@ -89,15 +93,21 @@ files:
89
93
  - lib/rails_stuff/params_parser.rb
90
94
  - lib/rails_stuff/random_uniq_attr.rb
91
95
  - lib/rails_stuff/redis_storage.rb
96
+ - lib/rails_stuff/require_nested.rb
92
97
  - lib/rails_stuff/resources_controller.rb
93
98
  - lib/rails_stuff/resources_controller/actions.rb
94
99
  - lib/rails_stuff/resources_controller/basic_helpers.rb
100
+ - lib/rails_stuff/resources_controller/belongs_to.rb
101
+ - lib/rails_stuff/resources_controller/has_scope_helpers.rb
102
+ - lib/rails_stuff/resources_controller/kaminari_helpers.rb
95
103
  - lib/rails_stuff/resources_controller/resource_helper.rb
96
104
  - lib/rails_stuff/resources_controller/responder.rb
97
105
  - lib/rails_stuff/resources_controller/sti_helpers.rb
98
106
  - lib/rails_stuff/sort_scope.rb
99
107
  - lib/rails_stuff/statusable.rb
100
108
  - lib/rails_stuff/strong_parameters.rb
109
+ - lib/rails_stuff/test_helpers/concurrency.rb
110
+ - lib/rails_stuff/test_helpers/configurator.rb
101
111
  - lib/rails_stuff/test_helpers/rails.rb
102
112
  - lib/rails_stuff/test_helpers/response.rb
103
113
  - lib/rails_stuff/types_tracker.rb
@@ -124,9 +134,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
134
  version: '0'
125
135
  requirements: []
126
136
  rubyforge_project:
127
- rubygems_version: 2.4.6
137
+ rubygems_version: 2.5.1
128
138
  signing_key:
129
139
  specification_version: 4
130
140
  summary: Collection of useful modules for Rails
131
141
  test_files: []
132
- has_rdoc: