decent_exposure 3.0.0.beta1 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +12 -0
  3. data/CHANGELOG.md +19 -0
  4. data/Gemfile +2 -0
  5. data/README.md +58 -7
  6. data/decent_exposure.gemspec +16 -16
  7. data/gemfiles/Gemfile.rails-4.2.0 +5 -0
  8. data/gemfiles/Gemfile.rails-4.2.6 +5 -0
  9. data/gemfiles/Gemfile.rails-5.0.0 +5 -0
  10. data/lib/decent_exposure.rb +12 -5
  11. data/lib/decent_exposure/attribute.rb +0 -1
  12. data/lib/decent_exposure/behavior.rb +1 -1
  13. data/lib/decent_exposure/context.rb +2 -2
  14. data/lib/decent_exposure/controller.rb +4 -4
  15. data/lib/decent_exposure/exposure.rb +24 -23
  16. data/lib/decent_exposure/flow.rb +1 -2
  17. data/lib/decent_exposure/mailer.rb +15 -0
  18. data/lib/decent_exposure/version.rb +1 -1
  19. data/lib/generators/decent_exposure/USAGE +13 -0
  20. data/lib/generators/decent_exposure/scaffold_templates_generator.rb +46 -0
  21. data/lib/generators/decent_exposure/templates/_form.html.erb +34 -0
  22. data/lib/generators/decent_exposure/templates/_form.html.haml +15 -0
  23. data/lib/generators/decent_exposure/templates/controller.rb +41 -0
  24. data/lib/generators/decent_exposure/templates/edit.html.erb +6 -0
  25. data/lib/generators/decent_exposure/templates/edit.html.haml +7 -0
  26. data/lib/generators/decent_exposure/templates/index.html.erb +31 -0
  27. data/lib/generators/decent_exposure/templates/index.html.haml +25 -0
  28. data/lib/generators/decent_exposure/templates/new.html.erb +5 -0
  29. data/lib/generators/decent_exposure/templates/new.html.haml +5 -0
  30. data/lib/generators/decent_exposure/templates/show.html.erb +11 -0
  31. data/lib/generators/decent_exposure/templates/show.html.haml +11 -0
  32. data/spec/{controller_spec.rb → decent_exposure/controller_spec.rb} +45 -39
  33. data/spec/features/api_birds_controller_spec.rb +70 -0
  34. data/spec/features/birds_controller_spec.rb +70 -0
  35. data/spec/features/birds_mailer_spec.rb +54 -0
  36. data/spec/generators/decent_exposure/scaffold_templates_generator_spec.rb +45 -0
  37. data/spec/support/rails_app.rb +39 -7
  38. metadata +57 -35
  39. data/hashrocket_logo.png +0 -0
  40. data/spec/integration_spec.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2ec6d62cc52928970902ade2a9998e8f58758f7c
4
- data.tar.gz: 0a3aff2440f88ed56a20197d270402c826c6b5e3
2
+ SHA256:
3
+ metadata.gz: 3f21e1f9876dc8a5fb33726db86f7dd57c37f5c02d1947594e5d35378fa3c9fb
4
+ data.tar.gz: f3df28184866142fdcc357c815e035c4d12181d639cd815e9d2735cc268354d8
5
5
  SHA512:
6
- metadata.gz: c085171f6369dda3d9ae3f459aed654dcdc5a0934f28b61b605ffa1cbff9951cc5b44b214179d3969764064c748c7cddac8dc58655c7972f09dcaa6c5bec0bff
7
- data.tar.gz: 692f0b40e7d1435ebaec5ac4d96422b8cb69592afcb3b5010c597f71f65f7c3f88588ee26dffd17ae38cd83ad269f5fa1581030bbf132ea3c4a63a8b38d1bf81
6
+ metadata.gz: 340c2ad9f56e884653dc235063e43cf874a344cd01e6f8b5fb5301c993dec5a53f4328703a154c4ae1caeeffd39c5eaea5f4a79eb9976bc34f313530d1d6933a
7
+ data.tar.gz: e9d9363c1a34280c7084e1662c9428577f68d0617e31436e013f0f0e782067ecb42177849f0710cd8c49f03f2e94e4e09758771faeec5f1848a9fc02cd23eff5
@@ -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=
@@ -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
@@ -1,3 +1,5 @@
1
1
  source "https://rubygems.org"
2
2
 
3
+ gem "rake"
4
+
3
5
  gemspec
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`. If you are using `rails <= 4.2.x` please look at `decent_exposure < 3.0`.
10
+ DecentExposure `3.0` is a major change from `< 3.0`.
11
11
 
12
- **Be aware... mild API changes ahead.**
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(things_params)`. The result will be memoized in an `@exposed_thing`
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 4 CRUD controller using Decent Exposure might look like:
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
- [![Hashrocket logo](hashrocket_logo.png)](https://hashrocket.com)
387
+ [![Hashrocket logo](https://hashrocket.com/hashrocket_logo.svg)](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-us/hire-us) or [join our team](https://hashrocket.com/contact-us/jobs), don't hesitate to get in touch.
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.
@@ -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 = "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 = %q{
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 = "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\//)
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 = "~> 2.0"
23
+ spec.required_ruby_version = ">= 2.0"
24
24
 
25
- spec.add_dependency "railties", "~> 5.x"
26
- spec.add_dependency "activesupport", "~> 5.x"
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 "rake", "~> 10.3"
29
- spec.add_development_dependency "pry"
29
+ spec.add_development_dependency "standard"
30
30
  end
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rails", "4.2.0"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rails", "4.2.6"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rails", "5.0.0"
@@ -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 :Exposure, "decent_exposure/exposure"
7
- autoload :Attribute, "decent_exposure/attribute"
8
- autoload :Context, "decent_exposure/context"
9
- autoload :Behavior, "decent_exposure/behavior"
10
- autoload :Flow, "decent_exposure/flow"
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
@@ -32,7 +32,6 @@ module DecentExposure
32
32
  "#{name}=".to_sym
33
33
  end
34
34
 
35
-
36
35
  # Public: Expose a getter and setter method for the Attribute
37
36
  # on the passed in Controller class.
38
37
  #
@@ -86,7 +86,7 @@ module DecentExposure
86
86
  protected
87
87
 
88
88
  def params_id_key_candidates
89
- [ "#{model_param_key}_id", "#{name}_id", "id" ].uniq
89
+ ["#{model_param_key}_id", "#{name}_id", "id"].uniq
90
90
  end
91
91
 
92
92
  def model_param_key
@@ -20,7 +20,7 @@ module DecentExposure
20
20
  #
21
21
  # Returns the attribute's value.
22
22
  def get
23
- ivar_defined?? ivar_get : set(fetch_value)
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
- helper_methods = [ attribute.getter_method_name, attribute.setter_method_name ]
71
- controller.helper_method *helper_methods
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] and Proc === previous_value
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