decent_exposure 3.0.0.beta1 → 3.0.3
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 +5 -5
- data/.travis.yml +12 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +2 -0
- data/README.md +58 -7
- data/decent_exposure.gemspec +16 -16
- data/gemfiles/Gemfile.rails-4.2.0 +5 -0
- data/gemfiles/Gemfile.rails-4.2.6 +5 -0
- data/gemfiles/Gemfile.rails-5.0.0 +5 -0
- data/lib/decent_exposure.rb +12 -5
- data/lib/decent_exposure/attribute.rb +0 -1
- data/lib/decent_exposure/behavior.rb +1 -1
- data/lib/decent_exposure/context.rb +2 -2
- data/lib/decent_exposure/controller.rb +4 -4
- data/lib/decent_exposure/exposure.rb +24 -23
- data/lib/decent_exposure/flow.rb +1 -2
- data/lib/decent_exposure/mailer.rb +15 -0
- data/lib/decent_exposure/version.rb +1 -1
- data/lib/generators/decent_exposure/USAGE +13 -0
- data/lib/generators/decent_exposure/scaffold_templates_generator.rb +46 -0
- data/lib/generators/decent_exposure/templates/_form.html.erb +34 -0
- data/lib/generators/decent_exposure/templates/_form.html.haml +15 -0
- data/lib/generators/decent_exposure/templates/controller.rb +41 -0
- data/lib/generators/decent_exposure/templates/edit.html.erb +6 -0
- data/lib/generators/decent_exposure/templates/edit.html.haml +7 -0
- data/lib/generators/decent_exposure/templates/index.html.erb +31 -0
- data/lib/generators/decent_exposure/templates/index.html.haml +25 -0
- data/lib/generators/decent_exposure/templates/new.html.erb +5 -0
- data/lib/generators/decent_exposure/templates/new.html.haml +5 -0
- data/lib/generators/decent_exposure/templates/show.html.erb +11 -0
- data/lib/generators/decent_exposure/templates/show.html.haml +11 -0
- data/spec/{controller_spec.rb → decent_exposure/controller_spec.rb} +45 -39
- data/spec/features/api_birds_controller_spec.rb +70 -0
- data/spec/features/birds_controller_spec.rb +70 -0
- data/spec/features/birds_mailer_spec.rb +54 -0
- data/spec/generators/decent_exposure/scaffold_templates_generator_spec.rb +45 -0
- data/spec/support/rails_app.rb +39 -7
- metadata +57 -35
- data/hashrocket_logo.png +0 -0
- data/spec/integration_spec.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3f21e1f9876dc8a5fb33726db86f7dd57c37f5c02d1947594e5d35378fa3c9fb
|
4
|
+
data.tar.gz: f3df28184866142fdcc357c815e035c4d12181d639cd815e9d2735cc268354d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 340c2ad9f56e884653dc235063e43cf874a344cd01e6f8b5fb5301c993dec5a53f4328703a154c4ae1caeeffd39c5eaea5f4a79eb9976bc34f313530d1d6933a
|
7
|
+
data.tar.gz: e9d9363c1a34280c7084e1662c9428577f68d0617e31436e013f0f0e782067ecb42177849f0710cd8c49f03f2e94e4e09758771faeec5f1848a9fc02cd23eff5
|
data/.travis.yml
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
language: ruby
|
1
2
|
rvm:
|
2
3
|
- 2.2.2
|
3
4
|
- 2.3.0
|
5
|
+
gemfile:
|
6
|
+
- Gemfile
|
7
|
+
- gemfiles/Gemfile.rails-5.0.0
|
8
|
+
- gemfiles/Gemfile.rails-4.2.6
|
9
|
+
- gemfiles/Gemfile.rails-4.2.0
|
10
|
+
notifications:
|
11
|
+
slack:
|
12
|
+
on_success: change
|
13
|
+
on_failure: always
|
14
|
+
rooms:
|
15
|
+
secure: Ch/Euv0+g1BQawHLVXzT9R/eoFxQWBf95SQzufpA19tlwz+7OsGlaOH4YfxVbnanH33XL06AEB1arsDUwlv0lx/I2Cs1LRh4WHAJ7T5hahusF2W3/+RgCDf2eifk1MRswkDpvhxH/sAS6INc4gOKc52q4gAdzU2uytVsdaaCjCY=
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# DecentExposure History/Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
This project adheres to [Semantic Versioning](http://semver.org/).
|
5
|
+
|
6
|
+
## 3.0.2
|
7
|
+
|
8
|
+
* Fix mailers when arguments are not a hash.
|
9
|
+
|
10
|
+
## 3.0.1
|
11
|
+
|
12
|
+
* Allow exposures with a question mark (?).
|
13
|
+
* Remove setter from view helper.
|
14
|
+
* Fix undefined `helper_method` error
|
15
|
+
|
16
|
+
## 3.0.0
|
17
|
+
|
18
|
+
* New codebase, see [api changes document](https://github.com/hashrocket/decent_exposure/wiki/Api-changes-in-version-3).
|
19
|
+
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -7,16 +7,18 @@
|
|
7
7
|
|
8
8
|
### Notice
|
9
9
|
|
10
|
-
DecentExposure `3.0` is a major change from `< 3.0`.
|
10
|
+
DecentExposure `3.0` is a major change from `< 3.0`.
|
11
11
|
|
12
|
-
|
12
|
+
Version `3.0` will support Rails 4 and 5.
|
13
|
+
|
14
|
+
**Be aware... mild API changes ahead.** Check this [API changes in version 3](https://github.com/hashrocket/decent_exposure/wiki/Api-changes-in-version-3)
|
13
15
|
|
14
16
|
### Installation
|
15
17
|
|
16
18
|
Add this line to your application's Gemfile:
|
17
19
|
|
18
20
|
```ruby
|
19
|
-
gem 'decent_exposure'
|
21
|
+
gem 'decent_exposure', '3.0.0'
|
20
22
|
```
|
21
23
|
|
22
24
|
And then execute:
|
@@ -43,12 +45,12 @@ end
|
|
43
45
|
|
44
46
|
Now every time you call `thing` in your controller or view, it will look for an
|
45
47
|
ID and try to perform `Thing.find(id)`. If the ID isn't found, it will call
|
46
|
-
`Thing.new(
|
48
|
+
`Thing.new(thing_params)`. The result will be memoized in an `@exposed_thing`
|
47
49
|
instance variable.
|
48
50
|
|
49
51
|
#### Example Controller
|
50
52
|
|
51
|
-
Here's what a standard Rails
|
53
|
+
Here's what a standard Rails 5 CRUD controller using Decent Exposure might look like:
|
52
54
|
|
53
55
|
```ruby
|
54
56
|
class ThingsController < ApplicationController
|
@@ -307,6 +309,7 @@ decoration process. Initially, this does nothing, but you can obviously change
|
|
307
309
|
that:
|
308
310
|
|
309
311
|
```ruby
|
312
|
+
expose :things, ->{ Thing.all.map{ |thing| ThingDecorator.new(thing) } }
|
310
313
|
expose :thing, decorate: ->(thing){ ThingDecorator.new(thing) }
|
311
314
|
```
|
312
315
|
|
@@ -323,6 +326,54 @@ expose :thing, with: [:cool_find, :cool_build]
|
|
323
326
|
expose :another_thing, with: :cool_build
|
324
327
|
```
|
325
328
|
|
329
|
+
## Rails Mailers
|
330
|
+
|
331
|
+
Mailers and Controllers use the same decent_exposure dsl.
|
332
|
+
|
333
|
+
### Example Mailer
|
334
|
+
|
335
|
+
```ruby
|
336
|
+
class PostMailer < ApplicationMailer
|
337
|
+
expose(:posts, -> { Post.last(10) })
|
338
|
+
expose(:post)
|
339
|
+
|
340
|
+
def top_posts
|
341
|
+
@greeting = "Top Posts"
|
342
|
+
mail to: "to@example.org"
|
343
|
+
end
|
344
|
+
|
345
|
+
def featured_post(id:)
|
346
|
+
@greeting = "Featured Post"
|
347
|
+
mail to: "to@example.org"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
```
|
351
|
+
|
352
|
+
## Rails Scaffold Templates
|
353
|
+
|
354
|
+
If you want to generate rails scaffold templates prepared for `decent_exposure` run:
|
355
|
+
|
356
|
+
```bash
|
357
|
+
rails generate decent_exposure:scaffold_templates [--template_engine erb|haml]
|
358
|
+
```
|
359
|
+
|
360
|
+
This will create the templates in your `lib/templates` folder.
|
361
|
+
|
362
|
+
Make sure you have configured your templates engine for generators in `config/application.rb`:
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
# config/application.rb
|
366
|
+
config.generators do |g|
|
367
|
+
g.template_engine :erb
|
368
|
+
end
|
369
|
+
```
|
370
|
+
|
371
|
+
Now you can run scaffold like:
|
372
|
+
|
373
|
+
```bash
|
374
|
+
rails generate scaffold post title description:text
|
375
|
+
```
|
376
|
+
|
326
377
|
## Contributing
|
327
378
|
|
328
379
|
1. Fork it (https://github.com/hashrocket/decent_exposure/fork)
|
@@ -333,6 +384,6 @@ expose :another_thing, with: :cool_build
|
|
333
384
|
|
334
385
|
## About
|
335
386
|
|
336
|
-
[](https://hashrocket.com)
|
337
388
|
|
338
|
-
Decent Exposure is supported by the team at [Hashrocket](https://hashrocket.com), a multidisciplinary design & development consultancy. If you'd like to [work with us](https://hashrocket.com/contact
|
389
|
+
Decent Exposure is supported by the team at [Hashrocket](https://hashrocket.com), a multidisciplinary design & development consultancy. If you'd like to [work with us](https://hashrocket.com/contact) or [join our team](https://hashrocket.com/careers), don't hesitate to get in touch.
|
data/decent_exposure.gemspec
CHANGED
@@ -1,30 +1,30 @@
|
|
1
1
|
require File.expand_path("../lib/decent_exposure/version", __FILE__)
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name
|
5
|
-
spec.version
|
6
|
-
spec.authors
|
7
|
-
spec.email
|
8
|
-
spec.summary
|
9
|
-
spec.description =
|
4
|
+
spec.name = "decent_exposure"
|
5
|
+
spec.version = DecentExposure::VERSION
|
6
|
+
spec.authors = ["Pavel Pravosud", "Stephen Caudill"]
|
7
|
+
spec.email = ["info@hashrocket.com"]
|
8
|
+
spec.summary = "A helper for creating declarative interfaces in controllers"
|
9
|
+
spec.description = '
|
10
10
|
DecentExposure helps you program to an interface, rather than an
|
11
11
|
implementation in your Rails controllers. The fact of the matter is that
|
12
12
|
sharing state via instance variables in controllers promotes close coupling
|
13
13
|
with views. DecentExposure gives you a declarative manner of exposing an
|
14
14
|
interface to the state that controllers contain and thereby decreasing
|
15
15
|
coupling and improving your testability and overall design.
|
16
|
-
|
17
|
-
spec.homepage
|
18
|
-
spec.license
|
19
|
-
spec.files
|
20
|
-
spec.test_files
|
16
|
+
'
|
17
|
+
spec.homepage = "https://github.com/hashrocket/decent_exposure"
|
18
|
+
spec.license = "MIT"
|
19
|
+
spec.files = `git ls-files -z`.split("\x0")
|
20
|
+
spec.test_files = spec.files.grep(/\Aspec\//)
|
21
21
|
spec.require_path = "lib"
|
22
22
|
|
23
|
-
spec.required_ruby_version = "
|
23
|
+
spec.required_ruby_version = ">= 2.0"
|
24
24
|
|
25
|
-
spec.add_dependency "
|
26
|
-
spec.
|
25
|
+
spec.add_dependency "activesupport", ">= 4.0"
|
26
|
+
spec.add_development_dependency "railties", ">= 4.0"
|
27
|
+
spec.add_development_dependency "actionmailer"
|
27
28
|
spec.add_development_dependency "rspec-rails", "~> 3.0"
|
28
|
-
spec.add_development_dependency "
|
29
|
-
spec.add_development_dependency "pry"
|
29
|
+
spec.add_development_dependency "standard"
|
30
30
|
end
|
data/lib/decent_exposure.rb
CHANGED
@@ -1,15 +1,22 @@
|
|
1
1
|
require "decent_exposure/version"
|
2
2
|
require "active_support/all"
|
3
|
+
require "generators/decent_exposure/scaffold_templates_generator"
|
3
4
|
|
4
5
|
module DecentExposure
|
5
6
|
autoload :Controller, "decent_exposure/controller"
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :
|
7
|
+
autoload :Mailer, "decent_exposure/mailer"
|
8
|
+
autoload :Exposure, "decent_exposure/exposure"
|
9
|
+
autoload :Attribute, "decent_exposure/attribute"
|
10
|
+
autoload :Context, "decent_exposure/context"
|
11
|
+
autoload :Behavior, "decent_exposure/behavior"
|
12
|
+
autoload :Flow, "decent_exposure/flow"
|
11
13
|
|
12
14
|
ActiveSupport.on_load :action_controller do
|
13
15
|
include Controller
|
14
16
|
end
|
17
|
+
|
18
|
+
ActiveSupport.on_load :action_mailer do
|
19
|
+
include Controller
|
20
|
+
include Mailer
|
21
|
+
end
|
15
22
|
end
|
@@ -20,7 +20,7 @@ module DecentExposure
|
|
20
20
|
#
|
21
21
|
# Returns the attribute's value.
|
22
22
|
def get
|
23
|
-
ivar_defined
|
23
|
+
ivar_defined? ? ivar_get : set(fetch_value)
|
24
24
|
end
|
25
25
|
|
26
26
|
# Public: Write to an attribute on the context Class.
|
@@ -51,7 +51,7 @@ module DecentExposure
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def ivar_name
|
54
|
-
"@#{attribute.ivar_name}"
|
54
|
+
"@#{attribute.ivar_name.gsub("?", "_question_mark_")}"
|
55
55
|
end
|
56
56
|
|
57
57
|
def fetch_value
|
@@ -17,8 +17,8 @@ module DecentExposure
|
|
17
17
|
#
|
18
18
|
# Returns the helper methods that are now defined on the class
|
19
19
|
# where this method is included.
|
20
|
-
def expose(*args, &block)
|
21
|
-
Exposure.expose! self, *args, &block
|
20
|
+
def expose(*args, **options, &block)
|
21
|
+
Exposure.expose! self, *args, **options, &block
|
22
22
|
end
|
23
23
|
|
24
24
|
# Public: Exposes an attribute to a controller Class.
|
@@ -33,8 +33,8 @@ module DecentExposure
|
|
33
33
|
#
|
34
34
|
# Sets the exposed attribute to a before_action callback in the
|
35
35
|
# controller.
|
36
|
-
def expose!(name, *args, &block)
|
37
|
-
expose name, *args, &block
|
36
|
+
def expose!(name, *args, **options, &block)
|
37
|
+
expose name, *args, **options, &block
|
38
38
|
before_action name
|
39
39
|
end
|
40
40
|
|
@@ -13,8 +13,8 @@ module DecentExposure
|
|
13
13
|
# the Proc when called.
|
14
14
|
#
|
15
15
|
# Returns a collection of exposed helper methods.
|
16
|
-
def self.expose!(*args, &block)
|
17
|
-
new(*args, &block).expose!
|
16
|
+
def self.expose!(*args, **options, &block)
|
17
|
+
new(*args, **options, &block).expose!
|
18
18
|
end
|
19
19
|
|
20
20
|
# Public: Initalize an Exposure with a hash of options.
|
@@ -34,7 +34,7 @@ module DecentExposure
|
|
34
34
|
# the Proc.
|
35
35
|
#
|
36
36
|
# Returns a normalized options Hash.
|
37
|
-
def initialize(controller, name, fetch_block=nil, **options, &block)
|
37
|
+
def initialize(controller, name, fetch_block = nil, **options, &block)
|
38
38
|
@controller = controller
|
39
39
|
@options = options.with_indifferent_access.merge(name: name)
|
40
40
|
|
@@ -67,8 +67,9 @@ module DecentExposure
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def expose_helper_methods!
|
70
|
-
|
71
|
-
|
70
|
+
return unless controller.respond_to?(:helper_method)
|
71
|
+
|
72
|
+
controller.helper_method attribute.getter_method_name
|
72
73
|
end
|
73
74
|
|
74
75
|
def normalize_options
|
@@ -85,43 +86,43 @@ module DecentExposure
|
|
85
86
|
|
86
87
|
def normalize_fetch_option
|
87
88
|
normalize_non_proc_option :fetch do |method_name|
|
88
|
-
->{ send(method_name) }
|
89
|
+
-> { send(method_name) }
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|
92
93
|
def normalize_find_by_option
|
93
|
-
if find_by = options.delete(:find_by)
|
94
|
-
merge_lambda_option :find, ->(id, scope){ scope.find_by!(find_by => id) }
|
94
|
+
if (find_by = options.delete(:find_by))
|
95
|
+
merge_lambda_option :find, ->(id, scope) { scope.find_by!(find_by => id) }
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
98
99
|
def normalize_parent_option
|
99
100
|
exposure_name = options.fetch(:name)
|
100
101
|
|
101
|
-
if parent = options.delete(:parent)
|
102
|
-
merge_lambda_option :scope, ->{ send(parent).send(exposure_name.to_s.pluralize) }
|
102
|
+
if (parent = options.delete(:parent))
|
103
|
+
merge_lambda_option :scope, -> { send(parent).send(exposure_name.to_s.pluralize) }
|
103
104
|
end
|
104
105
|
end
|
105
106
|
|
106
107
|
def normalize_from_option
|
107
108
|
exposure_name = options.fetch(:name)
|
108
109
|
|
109
|
-
if from = options.delete(:from)
|
110
|
-
merge_lambda_option :build, ->{ send(from).send(exposure_name) }
|
111
|
-
merge_lambda_option :model, ->{ nil }
|
112
|
-
merge_lambda_option :id, ->{ nil }
|
110
|
+
if (from = options.delete(:from))
|
111
|
+
merge_lambda_option :build, -> { send(from).send(exposure_name) }
|
112
|
+
merge_lambda_option :model, -> { nil }
|
113
|
+
merge_lambda_option :id, -> { nil }
|
113
114
|
end
|
114
115
|
end
|
115
116
|
|
116
117
|
def normalize_with_option
|
117
|
-
if configs = options.delete(:with)
|
118
|
-
Array.wrap(configs).each{ |config| reverse_merge_config! config }
|
118
|
+
if (configs = options.delete(:with))
|
119
|
+
Array.wrap(configs).each { |config| reverse_merge_config! config }
|
119
120
|
end
|
120
121
|
end
|
121
122
|
|
122
123
|
def normalize_id_option
|
123
124
|
normalize_non_proc_option :id do |ids|
|
124
|
-
->{ Array.wrap(ids).map{ |id| params[id] }.find(&:present?) }
|
125
|
+
-> { Array.wrap(ids).map { |id| params[id] }.find(&:present?) }
|
125
126
|
end
|
126
127
|
end
|
127
128
|
|
@@ -133,7 +134,7 @@ module DecentExposure
|
|
133
134
|
value
|
134
135
|
end
|
135
136
|
|
136
|
-
->{ model }
|
137
|
+
-> { model }
|
137
138
|
end
|
138
139
|
end
|
139
140
|
|
@@ -146,7 +147,7 @@ module DecentExposure
|
|
146
147
|
|
147
148
|
def normalize_scope_options
|
148
149
|
normalize_non_proc_option :scope do |custom_scope|
|
149
|
-
->(model){ model.send(custom_scope) }
|
150
|
+
->(model) { model.send(custom_scope) }
|
150
151
|
end
|
151
152
|
end
|
152
153
|
|
@@ -164,7 +165,7 @@ module DecentExposure
|
|
164
165
|
end
|
165
166
|
|
166
167
|
def merge_lambda_option(name, body)
|
167
|
-
if previous_value = options[name]
|
168
|
+
if (previous_value = options[name]) && (Proc === previous_value)
|
168
169
|
fail ArgumentError, "#{name.to_s.titleize} block is already defined"
|
169
170
|
end
|
170
171
|
|
@@ -177,7 +178,7 @@ module DecentExposure
|
|
177
178
|
|
178
179
|
name = options.fetch(:name)
|
179
180
|
ivar_name = "exposed_#{name}"
|
180
|
-
fetch = ->{ Flow.new(self, local_options).fetch }
|
181
|
+
fetch = -> { Flow.new(self, local_options).fetch }
|
181
182
|
|
182
183
|
Attribute.new(
|
183
184
|
name: name,
|
@@ -188,13 +189,13 @@ module DecentExposure
|
|
188
189
|
end
|
189
190
|
|
190
191
|
def assert_incompatible_options_pair(key1, key2)
|
191
|
-
if options.key?(key1) && options.key?(key2)
|
192
|
+
if options.symbolize_keys.key?(key1) && options.symbolize_keys.key?(key2)
|
192
193
|
fail ArgumentError, "Using #{key1.inspect} option with #{key2.inspect} doesn't make sense"
|
193
194
|
end
|
194
195
|
end
|
195
196
|
|
196
197
|
def assert_singleton_option(name)
|
197
|
-
if options.except(name, :name, :decorate).any? && options.key?(name)
|
198
|
+
if options.symbolize_keys.except(name, :name, :decorate).any? && options.symbolize_keys.key?(name)
|
198
199
|
fail ArgumentError, "Using #{name.inspect} option with other options doesn't make sense"
|
199
200
|
end
|
200
201
|
end
|