garden_variety 2.0 → 4.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b80f333fab73447d743d99ef4cd170a997011a8e
4
- data.tar.gz: ab23ee38fd1a658f8fa7a82eeac8708110346bb4
2
+ SHA256:
3
+ metadata.gz: dd6dd125edefcf640a8a2aa1b629510eb3e5526551abfea71738b661c9fe1d83
4
+ data.tar.gz: ec5a4e29f8e1937dd74a88d11fdf087394a95cf67c56ceca177e9ce5fb9ae89e
5
5
  SHA512:
6
- metadata.gz: 1a36d05543168c2739e1b2677aa4b870c534ca4a5fc340a80ae80f18e920b238c59017179cd16e5824f2bce62ac36db3ca73a4c80f98a5e8f61381de597ef336
7
- data.tar.gz: a93377a1e7c252f6ad547febe70bdaedc83a62383819e04766bda20b604f5db055571d17fa7d56b5a4680eccb0d124bf54d5171787a5a1858cdb472b80cbc58f
6
+ metadata.gz: bd41cf0735bfabe753a7bb4b5138d76a5e9c572952d5d4fb344f344dfe44926258c1a64ffe67b66875fcae5b09d9640fb8dfb8ce742a812bcbb0cfcad80b0d21
7
+ data.tar.gz: ba7d049d2952075c9a431dbd76b9ced59abc89918dfdbe589a7e8e183654a769669e6396e6e4546984bddf2057e7164a7f921ee849dacdee59c1f2d1ead39177
data/README.md CHANGED
@@ -2,16 +2,15 @@
2
2
 
3
3
  Delightfully boring Rails controllers. One of the superb advantages of
4
4
  Ruby on Rails is convention over configuration. Opinionated default
5
- behavior can decrease development time, and increase application
5
+ behavior can decrease development time and increase application
6
6
  robustness (less custom code == less that can go wrong). In service of
7
7
  this principle, *garden_variety* provides reasonable default controller
8
8
  actions, with care to allow easy override.
9
9
 
10
- *garden_variety* also uses the excellent
11
- [Pundit](https://rubygems.org/gems/pundit) gem to isolate authorization
12
- concerns. If you're unfamiliar with Pundit, see its documentation for
13
- an explanation of policy objects and how they help controller actions
14
- stay DRY and boring.
10
+ *garden_variety* also relies on the [Pundit](https://rubygems.org/gems/pundit)
11
+ gem to isolate authorization concerns. If you're unfamiliar with
12
+ Pundit, see its documentation for an explanation of policy objects and
13
+ how they help controller actions stay DRY and boring.
15
14
 
16
15
  As an example, this controller using `garden_variety`...
17
16
 
@@ -27,29 +26,26 @@ end
27
26
  class PostsController < ApplicationController
28
27
 
29
28
  def index
30
- authorize(resource_class)
31
- self.resources = policy_scope(list_resources)
29
+ authorize(self.class.model_class)
30
+ self.collection = policy_scope(find_collection)
32
31
  end
33
32
 
34
33
  def show
35
- self.resource = find_resource
36
- authorize(resource)
34
+ self.model = authorize(find_model)
37
35
  end
38
36
 
39
37
  def new
40
- if params.key?(resource_class.model_name.param_key)
41
- self.resource = vest(new_resource)
42
- else
43
- self.resource = new_resource
44
- authorize(resource)
38
+ self.model = authorize(new_model)
39
+ if params.key?(self.class.model_class.model_name.param_key)
40
+ assign_attributes(model)
45
41
  end
46
42
  end
47
43
 
48
44
  def create
49
- self.resource = vest(new_resource)
50
- if resource.save
45
+ self.model = assign_attributes(authorize(new_model))
46
+ if model.save
51
47
  flash[:success] = flash_message(:success)
52
- redirect_to resource
48
+ redirect_to model
53
49
  else
54
50
  flash.now[:error] = flash_message(:error)
55
51
  render :new
@@ -57,15 +53,14 @@ class PostsController < ApplicationController
57
53
  end
58
54
 
59
55
  def edit
60
- self.resource = find_resource
61
- authorize(resource)
56
+ self.model = authorize(find_model)
62
57
  end
63
58
 
64
59
  def update
65
- self.resource = vest(find_resource)
66
- if resource.save
60
+ self.model = assign_attributes(authorize(find_model))
61
+ if model.save
67
62
  flash[:success] = flash_message(:success)
68
- redirect_to resource
63
+ redirect_to model
69
64
  else
70
65
  flash.now[:error] = flash_message(:error)
71
66
  render :edit
@@ -73,9 +68,8 @@ class PostsController < ApplicationController
73
68
  end
74
69
 
75
70
  def destroy
76
- self.resource = find_resource
77
- authorize(resource)
78
- if resource.destroy
71
+ self.model = authorize(find_model)
72
+ if model.destroy
79
73
  flash[:success] = flash_message(:success)
80
74
  redirect_to action: :index
81
75
  else
@@ -86,80 +80,82 @@ class PostsController < ApplicationController
86
80
 
87
81
  private
88
82
 
89
- def resource_class
83
+ def self.model_class
90
84
  Post
91
85
  end
92
86
 
93
- def resources
87
+ def collection
94
88
  @posts
95
89
  end
96
90
 
97
- def resources=(models)
91
+ def collection=(models)
98
92
  @posts = models
99
93
  end
100
94
 
101
- def resource
95
+ def model
102
96
  @post
103
97
  end
104
98
 
105
- def resource=(model)
99
+ def model=(model)
106
100
  @post = model
107
101
  end
108
102
 
109
- def list_resources
110
- resource_class.all
103
+ def find_collection
104
+ self.class.model_class.all
111
105
  end
112
106
 
113
- def find_resource
114
- resource_class.find(params[:id])
107
+ def find_model
108
+ self.class.model_class.find(params[:id])
115
109
  end
116
110
 
117
- def new_resource
118
- resource_class.new
111
+ def new_model
112
+ self.class.model_class.new
119
113
  end
120
114
 
121
- def vest(model)
122
- authorize(model)
115
+ def assign_attributes(model)
123
116
  model.assign_attributes(permitted_attributes(model))
124
117
  model
125
118
  end
126
119
 
127
120
  def flash_options
128
- { resource_name: "post", resource_capitalized: "Post" }
121
+ { model_name: "Post" }
129
122
  end
130
123
 
131
124
  def flash_message(status)
132
125
  keys = [
133
- :"posts.#{action_name}.#{status}",
134
- :"posts.#{action_name}.#{status}_html",
135
- :"#{action_name}.#{status}",
136
- :"#{action_name}.#{status}_html",
137
- :"#{status}",
138
- :"#{status}_html",
126
+ :"flash.posts.#{action_name}.#{status}",
127
+ :"flash.posts.#{action_name}.#{status}_html",
128
+ :"flash.#{action_name}.#{status}",
129
+ :"flash.#{action_name}.#{status}_html",
130
+ :"flash.#{status}",
131
+ :"flash.#{status}_html",
139
132
  ]
140
- helpers.translate(keys.first, default: keys.drop(1), **flash_options)
133
+ helpers.translate(keys.first, { default: keys.drop(1), **flash_options })
141
134
  end
142
135
 
143
136
  end
144
137
  ```
145
138
 
146
- The implementations of the `resource_class` and `resource` / `resources`
147
- accessor methods are generated based on the controller name. They can
148
- be altered with an optional argument to the `garden_variety` macro. The
149
- rest of the methods can be overridden as normal, a la carte. For a
150
- detailed description of method behavior, see the
151
- [full documentation](http://www.rubydoc.info/gems/garden_variety/).
152
- (Note that the `authorize`, `policy_scope`, and `permitted_attributes`
153
- methods are provided by Pundit.)
139
+ The `::model_class` method returns a class corresponding to the
140
+ controller name, by default. That value can be overridden using the
141
+ matching `::model_class=` setter. The `model` / `collection` accessor
142
+ method definitions are dictated by `::model_class`. The rest of the
143
+ methods can be overridden as normal, a la carte. For a detailed
144
+ description of method behavior, see the [API documentation](
145
+ https://www.rubydoc.info/gems/garden_variety/). (Note that the
146
+ `authorize`, `policy_scope`, and `permitted_attributes` methods are
147
+ provided by Pundit.)
154
148
 
155
149
 
156
150
  ## Scaffold generator
157
151
 
158
- *garden_variety* includes a scaffold generator similar to the Rails
159
- scaffold generator:
152
+ *garden_variety* includes a `garden:scaffold` generator similar to the
153
+ Rails `scaffold` generator:
160
154
 
161
155
  ```
162
156
  $ rails generate garden:scaffold post title:string body:text published:boolean
157
+ exist config/locales
158
+ create config/locales/flash.en.yml
163
159
  generate resource
164
160
  invoke active_record
165
161
  create db/migrate/19991231235959_create_posts.rb
@@ -176,11 +172,6 @@ $ rails generate garden:scaffold post title:string body:text published:boolean
176
172
  invoke helper
177
173
  create app/helpers/posts_helper.rb
178
174
  invoke test_unit
179
- invoke assets
180
- invoke coffee
181
- create app/assets/javascripts/posts.coffee
182
- invoke scss
183
- create app/assets/stylesheets/posts.scss
184
175
  invoke resource_route
185
176
  route resources :posts
186
177
  generate erb:scaffold
@@ -190,6 +181,7 @@ $ rails generate garden:scaffold post title:string body:text published:boolean
190
181
  create app/views/posts/show.html.erb
191
182
  create app/views/posts/new.html.erb
192
183
  create app/views/posts/_form.html.erb
184
+ create app/views/posts/_post.html.erb
193
185
  insert app/controllers/posts_controller.rb
194
186
  generate pundit:policy
195
187
  create app/policies/post_policy.rb
@@ -206,14 +198,23 @@ generator in a few small ways:
206
198
  * No jbuilder templates. Only HTML templates are generated.
207
199
  * `rails generate pundit:policy` is invoked for the specified model.
208
200
 
201
+ Additionally, if you are using the [talent_scout] gem, the scaffold
202
+ generator will invoke `rails generate talent_scout:search` for the
203
+ specified model. This behavior can be disabled with the
204
+ `--skip-talent-scout` option. For more information about integrating
205
+ with *talent_scout*, see the [Searching with talent_scout](
206
+ #searching-with-talent_scout) section below.
207
+
208
+ [talent_scout]: https://rubygems.org/gems/talent_scout
209
+
209
210
 
210
211
  ## Flash messages
211
212
 
212
213
  Flash messages are defined using I18n. The *garden_variety* installer
213
214
  (`rails generate garden:install`) will create a
214
- "config/locales/garden_variety.en.yml" file containing default "success"
215
- and "error" messages. You can edit this file to customize those
216
- messages, or add your own translation files to support other languages.
215
+ "config/locales/flash.en.yml" file containing default "success" and
216
+ "error" messages. You can edit this file to customize those messages,
217
+ or add your own translation files to support other languages.
217
218
 
218
219
  As seen in the `PostsController#flash_message` method in the example
219
220
  above, a prioritized list of keys are tried when retrieving a flash
@@ -226,12 +227,11 @@ Internationalization guide for more information.)
226
227
 
227
228
  Interpolation in flash messages is also supported (as described by
228
229
  [Passing Variables to Translations]), with interpolation values provided
229
- by the `flash_options` method. By default, `flash_options` provides
230
- `resource_name` and `resource_capitalized` values, but you can override
231
- it to provide your own values.
230
+ by the `flash_options` method. By default, `flash_options` provides a
231
+ `model_name` value, but you can override it to provide your own values.
232
232
 
233
- [Safe HTML Translations]: http://guides.rubyonrails.org/i18n.html#using-safe-html-translations
234
- [Passing Variables to Translations]: http://guides.rubyonrails.org/i18n.html#passing-variables-to-translations
233
+ [Safe HTML Translations]: https://guides.rubyonrails.org/i18n.html#using-safe-html-translations
234
+ [Passing Variables to Translations]: https://guides.rubyonrails.org/i18n.html#passing-variables-to-translations
235
235
 
236
236
 
237
237
  ## Beyond garden variety behavior
@@ -240,46 +240,105 @@ it to provide your own values.
240
240
  written, including in situations where custom code is unavoidable.
241
241
 
242
242
 
243
- ### Integrating with pagination
243
+ ### Eliminating N+1 queries
244
+
245
+ With proper Russian doll caching, [N+1 queries can be a feature](
246
+ https://youtu.be/ktZLpjCanvg?t=4m27s). But, if you need to eliminate
247
+ an N+1 query by using eager loading, you can override the
248
+ `find_collection` method:
249
+
250
+ ```ruby
251
+ class PostsController < ApplicationController
252
+ garden_variety
253
+
254
+ def find_collection
255
+ super.includes(:author)
256
+ end
257
+ end
258
+ ```
259
+
244
260
 
245
- You can integrate your your favorite pagination gem (*may I suggest
246
- [foliate](https://rubygems.org/gems/foliate)?*) by overriding the
247
- `list_resources` method:
261
+ ### Pagination
262
+
263
+ You can integrate your favorite pagination gem (*may I suggest
264
+ [moar](https://rubygems.org/gems/moar)?*) by overriding the
265
+ `find_collection` method:
248
266
 
249
267
  ```ruby
250
268
  class PostsController < ApplicationController
251
269
  garden_variety
252
270
 
253
- def list_resources
254
- paginate(super)
271
+ def find_collection
272
+ moar(super.order(:created_at))
255
273
  end
256
274
  end
257
275
  ```
258
276
 
259
277
 
260
- ### Integrating with search
278
+ ### Searching
261
279
 
262
- You can also integrate search functionality by overriding the
263
- `list_resources` method:
280
+ You can provide search functionality by overriding the `find_collection`
281
+ method:
264
282
 
265
283
  ```ruby
266
284
  class PostsController < ApplicationController
267
285
  garden_variety
268
286
 
269
- def list_resources
270
- params[:author] ? super.where(author: params[:author]) : super
287
+ def find_collection
288
+ params[:title] ? super.where("title LIKE ?", "%#{params[:title]}%") : super
271
289
  end
272
290
  end
273
291
  ```
274
292
 
293
+ #### Searching with talent_scout
294
+
295
+ If you are using the [talent_scout] gem, the default implementation of
296
+ `find_collection` will automatically instantiate your model search class
297
+ -- no custom code required. For example, if a `PostSearch` class is
298
+ defined, `PostsController#find_collection` will be equivalent to:
299
+
300
+ ```ruby
301
+ def find_collection
302
+ @search = PostSearch.new(params[:q])
303
+ @search.results
304
+ end
305
+ ```
306
+
307
+ Notice, as a side effect, the `@search` instance variable is set for
308
+ later use in the view.
275
309
 
276
- ### Integrating with SJR
310
+ The model search class will be chosen based on the controller's
311
+ `::model_class`. For example:
277
312
 
278
- Server-generated JavaScript Response (SJR) controller actions are
279
- generally the same as conventional controller actions, with one
280
- difference: non-GET SJR actions render instead of redirect on success.
281
- *garden_variety* provides a concise syntax for overriding only
282
- on-success behavior of non-GET actions:
313
+ ```ruby
314
+ class MyPostsController < ApplicationController
315
+ garden_variety
316
+
317
+ self.model_class = Post # find_collection will use PostSearch instead of MyPostSearch
318
+ end
319
+ ```
320
+
321
+ If a corresponding model search class is not defined, `find_collection`
322
+ will fall back to its original non-search behavior.
323
+
324
+ Alternatively, you can override the model search class directly:
325
+
326
+ ```ruby
327
+ class MyPostsController < ApplicationController
328
+ garden_variety
329
+
330
+ self.model_class = Post
331
+ self.model_search_class = MyPostSearch # find_collection will use MyPostSearch
332
+ end
333
+ ```
334
+
335
+
336
+ ### Server-generated JavaScript Responses (SJR)
337
+
338
+ SJR controller actions are generally the same as conventional controller
339
+ actions, with one difference: non-GET SJR actions render instead of
340
+ redirect on success. *garden_variety* provides a concise syntax for
341
+ overriding only the on-success behavior of non-GET actions:
283
342
 
284
343
  ```ruby
285
344
  class PostsController < ApplicationController
@@ -323,7 +382,7 @@ messages. If a redirect is replaced with a render, the flash message
323
382
  will be set such that it does not affect the next request.
324
383
 
325
384
 
326
- ### Integrating with authentication
385
+ ### Authentication
327
386
 
328
387
  The details of integrating authentication will depend on your chosen
329
388
  authentication library. [Devise](https://rubygems.org/gems/devise) is
@@ -359,7 +418,7 @@ this conflict, add the following line to your Clearance initializer:
359
418
  ```
360
419
 
361
420
 
362
- ### Integrating with Form Objects
421
+ ### Form Objects
363
422
 
364
423
  The Form Object pattern is used to mitigate the complexity of handling
365
424
  forms which need special processing logic, such as context-dependent
@@ -399,10 +458,10 @@ end
399
458
  In this example, only the `new` and `create` actions are generated by
400
459
  the `garden_variety` macro. The on-success behavior of `create` is
401
460
  overridden to redirect to `root_path`, using the concise syntax
402
- discussed in the [Integrating with SJR](#integrating-with-sjr) example.
403
- The *garden_variety* controller helper methods all work as expected
404
- because `RegistrationForm` responds to `assign_attributes` and `save`,
405
- and has a default (nullary) constructor.
461
+ discussed in the [Server-generated JavaScript Responses (SJR)](#server-generated-javascript-responses-sjr)
462
+ example. The *garden_variety* controller helper methods all work as
463
+ expected because `RegistrationForm` responds to `assign_attributes` and
464
+ `save`, and has a default (nullary) constructor.
406
465
 
407
466
 
408
467
  ### Non-REST actions
@@ -430,20 +489,21 @@ end
430
489
 
431
490
  ```ruby
432
491
  class PublishedPostsController < ApplicationController
433
- garden_variety :index, resources: :posts
492
+ self.model_class = Post
493
+ garden_variety :index
434
494
 
435
- def list_resources
495
+ def find_collection
436
496
  super.where(published: true)
437
497
  end
438
498
  end
439
499
  ```
440
500
 
441
- Note the `resources:` argument to the `garden_variety` macro. The
442
- resource class for `PublishedPostsController` will be overridden as
443
- `Post` instead of derived as `PublishedPost`. Likewise, the `@posts`
444
- instance variable will be used instead of `@published_posts`.
501
+ Notice the call to `::model_class=`. The model class for
502
+ `PublishedPostsController` is overridden as `Post` instead of derived as
503
+ `PublishedPost`. And because of this override, the `@posts` instance
504
+ variable will be used instead of `@published_posts`.
445
505
 
446
- This example may be somewhat contrived, but there is an excellent talk
506
+ This example may be somewhat contrived, but here is an excellent talk
447
507
  from RailsConf which delves deeper into the principle:
448
508
  [In Relentless Pursuit of REST](https://www.youtube.com/watch?v=HctYHe-YjnE)
449
509
  ([slides](https://speakerdeck.com/derekprior/in-relentless-pursuit-of-rest)).
@@ -451,19 +511,13 @@ from RailsConf which delves deeper into the principle:
451
511
 
452
512
  ## Installation
453
513
 
454
- Add this line to your application's Gemfile:
455
-
456
- ```ruby
457
- gem "garden_variety"
458
- ```
459
-
460
- Then execute:
514
+ Add the gem to your Gemfile:
461
515
 
462
516
  ```bash
463
- $ bundle install
517
+ $ bundle add garden_variety
464
518
  ```
465
519
 
466
- And finally, run the *garden_variety* install generator:
520
+ And run the install generator:
467
521
 
468
522
  ```bash
469
523
  $ rails generate garden:install
@@ -474,9 +528,9 @@ This will also run the Pundit install generator, if necessary.
474
528
 
475
529
  ## Contributing
476
530
 
477
- Run `rake test` to run the tests.
531
+ Run `bin/test` to run the tests.
478
532
 
479
533
 
480
534
  ## License
481
535
 
482
- [MIT License](https://opensource.org/licenses/MIT)
536
+ [MIT License](MIT-LICENSE)
data/Rakefile CHANGED
@@ -1,29 +1,3 @@
1
- begin
2
- require 'bundler/setup'
3
- rescue LoadError
4
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
- end
6
-
7
- require 'yard'
8
-
9
- YARD::Rake::YardocTask.new(:doc) do |t|
10
- end
11
-
12
-
13
-
14
-
15
-
16
-
1
+ require 'bundler/setup'
17
2
 
18
3
  require 'bundler/gem_tasks'
19
-
20
- require 'rake/testtask'
21
-
22
- Rake::TestTask.new(:test) do |t|
23
- t.libs << 'test'
24
- t.pattern = 'test/**/*_test.rb'
25
- t.verbose = false
26
- end
27
-
28
-
29
- task default: :test
@@ -1,13 +1,13 @@
1
1
  module GardenVariety
2
2
 
3
- REDIRECT_CODES = [301, 302, 303, 307, 308]
3
+ REDIRECT_CODES = [301, 302, 303, 307, 308].to_set
4
4
 
5
5
  module IndexAction
6
6
  # Garden variety controller +index+ action.
7
7
  # @return [void]
8
8
  def index
9
- authorize(resource_class)
10
- self.resources = policy_scope(list_resources)
9
+ authorize(self.class.model_class)
10
+ self.collection = policy_scope(find_collection)
11
11
  end
12
12
  end
13
13
 
@@ -15,8 +15,7 @@ module GardenVariety
15
15
  # Garden variety controller +show+ action.
16
16
  # @return [void]
17
17
  def show
18
- self.resource = find_resource
19
- authorize(resource)
18
+ self.model = authorize(find_model)
20
19
  end
21
20
  end
22
21
 
@@ -24,12 +23,8 @@ module GardenVariety
24
23
  # Garden variety controller +new+ action.
25
24
  # @return [void]
26
25
  def new
27
- if params.key?(resource_class.model_name.param_key)
28
- self.resource = vest(new_resource)
29
- else
30
- self.resource = new_resource
31
- authorize(resource)
32
- end
26
+ self.model = (model = authorize(new_model))
27
+ assign_attributes(model) if params.key?(self.class.model_name.param_key)
33
28
  end
34
29
  end
35
30
 
@@ -40,10 +35,10 @@ module GardenVariety
40
35
  # @yield on-success callback, replaces default redirect
41
36
  # @return [void]
42
37
  def create
43
- self.resource = vest(new_resource)
44
- if resource.save
38
+ self.model = (model = assign_attributes(authorize(new_model)))
39
+ if model.save
45
40
  flash[:success] = flash_message(:success)
46
- block_given? ? yield : redirect_to(resource)
41
+ block_given? ? yield : redirect_to(model)
47
42
  flash.discard(:success) if REDIRECT_CODES.exclude?(response.status)
48
43
  else
49
44
  flash.now[:error] = flash_message(:error)
@@ -56,8 +51,7 @@ module GardenVariety
56
51
  # Garden variety controller +edit+ action.
57
52
  # @return [void]
58
53
  def edit
59
- self.resource = find_resource
60
- authorize(resource)
54
+ self.model = authorize(find_model)
61
55
  end
62
56
  end
63
57
 
@@ -68,10 +62,10 @@ module GardenVariety
68
62
  # @yield on-success callback, replaces default redirect
69
63
  # @return [void]
70
64
  def update
71
- self.resource = vest(find_resource)
72
- if resource.save
65
+ self.model = (model = assign_attributes(authorize(find_model)))
66
+ if model.save
73
67
  flash[:success] = flash_message(:success)
74
- block_given? ? yield : redirect_to(resource)
68
+ block_given? ? yield : redirect_to(model)
75
69
  flash.discard(:success) if REDIRECT_CODES.exclude?(response.status)
76
70
  else
77
71
  flash.now[:error] = flash_message(:error)
@@ -87,9 +81,8 @@ module GardenVariety
87
81
  # @yield on-success callback, replaces default redirect
88
82
  # @return [void]
89
83
  def destroy
90
- self.resource = find_resource
91
- authorize(resource)
92
- if resource.destroy
84
+ self.model = (model = authorize(find_model))
85
+ if model.destroy
93
86
  flash[:success] = flash_message(:success)
94
87
  block_given? ? yield : redirect_to(action: :index)
95
88
  flash.discard(:success) if REDIRECT_CODES.exclude?(response.status)
@@ -101,8 +94,8 @@ module GardenVariety
101
94
  end
102
95
 
103
96
  # Map of controller action name to action module. Used by the
104
- # {GardenVariety::Controller::ClassMethods#garden_variety} macro to
105
- # include desired controller actions.
97
+ # {GardenVariety::Controller::ClassMethods#garden_variety
98
+ # garden_variety} macro to include desired controller actions.
106
99
  ACTION_MODULES = {
107
100
  index: IndexAction,
108
101
  show: ShowAction,