adequate_exposure 0.2.1 → 0.6.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: 9180aa8ebc21e8d4a03967c573203ca6eac4e0a7
4
- data.tar.gz: 6ef711f9e8eeea01592d5628842e8811ceeef6f2
2
+ SHA256:
3
+ metadata.gz: ce17b7d2fd63e184f9b47eb20d8861407be033749995520b86a435e728c3818b
4
+ data.tar.gz: 9b430ad3d8bba9e410d44716dd512ca918215dc9c605342a040c7bfc31b18fe2
5
5
  SHA512:
6
- metadata.gz: 8715c320612b4a03d877b5f950e364b394ecebd37d581e643c8ac7f4587869132dcdeedfb0eee51a33fb524d5b451d328e2f469a609af4713c387d7aeb0663b2
7
- data.tar.gz: d132e75b4bf4083e64e65b526bce9681bb1ec57db3a7e42d6071a8c9302a5d30ca7d93910a26e357688a968cd7e37b68f2fd247b1001bb1cbdefd10804384bad
6
+ metadata.gz: 1c3c3ec85721257b46b10aa88a2260fc28a5901fb6e8245a0d9a51846bcccdff6dc8a6950813f82e30a97a3a18efcf933ecc791b6feb3d756e44754bca22d548
7
+ data.tar.gz: 5574c4f8578bc4a6ea7ec96d19c5f70e9da6380880f97ff61f5153e29d35e8e2461ab4599e5f4de80fb015b66e64860e47fb9d333befb185302cd89fe907c342
data/.gitignore CHANGED
@@ -14,9 +14,12 @@ rdoc
14
14
  spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
+ vendor/bundle
17
18
  tmp
18
19
  *.bundle
19
20
  *.so
20
21
  *.o
21
22
  *.a
22
23
  mkmf.log
24
+ gemfiles/*.lock
25
+ .tool-versions
@@ -1,4 +1,22 @@
1
+ gemfile:
2
+ - gemfiles/rails_5.0.gemfile
3
+ - gemfiles/rails_5.1.gemfile
4
+ - gemfiles/rails_5.2.gemfile
5
+ - gemfiles/rails_6.0.gemfile
6
+
1
7
  rvm:
2
- - 1.9
3
- - 2.0
4
- - 2.1
8
+ - 2.2
9
+ - 2.3
10
+ - 2.4
11
+ - 2.5
12
+
13
+ matrix:
14
+ exclude:
15
+ - rvm: 2.2
16
+ gemfile: gemfiles/rails_6.0.gemfile
17
+ - rvm: 2.2
18
+ gemfile: gemfiles/rails_5.2.gemfile
19
+ - rvm: 2.3
20
+ gemfile: gemfiles/rails_6.0.gemfile
21
+ - rvm: 2.4
22
+ gemfile: gemfiles/rails_6.0.gemfile
@@ -0,0 +1,19 @@
1
+ appraise "rails-6.0" do
2
+ gem "railties", "~> 6.0.0"
3
+ gem "activesupport", "~> 6.0.0"
4
+ end
5
+
6
+ appraise "rails-5.2" do
7
+ gem "railties", "~> 5.2.0"
8
+ gem "activesupport", "~> 5.2.0"
9
+ end
10
+
11
+ appraise "rails-5.1" do
12
+ gem "railties", "~> 5.1.0"
13
+ gem "activesupport", "~> 5.1.0"
14
+ end
15
+
16
+ appraise "rails-5.0" do
17
+ gem "railties", "~> 5.0.0"
18
+ gem "activesupport", "~> 5.0.0"
19
+ end
data/Gemfile CHANGED
@@ -2,6 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem "rspec-rails", "~> 3.0"
6
- gem "rake", "~> 10.3"
7
- gem "pry"
5
+ gem "appraisal", "~> 2.2"
6
+ gem "pry", "~> 0.12"
7
+ gem "rake", "~> 13.0"
8
+ gem "rspec-rails", "~> 3.9"
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Adequate Exposure
2
2
  [![Gem Version](https://img.shields.io/gem/v/adequate_exposure.svg)](https://rubygems.org/gems/adequate_exposure)
3
3
  [![Build Status](https://img.shields.io/travis/rwz/adequate_exposure.svg)](http://travis-ci.org/rwz/adequate_exposure)
4
- [![Code Climate](https://img.shields.io/codeclimate/github/rwz/adequate_exposure.svg)](https://codeclimate.com/github/rwz/adequate_exposure)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/50f8fdfcb0cf57ea3798/maintainability)](https://codeclimate.com/github/rwz/adequate_exposure/maintainability)
5
5
 
6
6
  Exposing things, adequately.
7
7
 
8
8
  Adequate exposure is a lightweight alternative to [Decent
9
- Exposure](https://github.com/voxdolo/decent_exposure). With its narrowly
9
+ Exposure](https://github.com/hashrocket/decent_exposure). With its narrowly
10
10
  focused api you can get exactly what you need without all the extra dressing.
11
11
 
12
12
  *Note: It is not the intent of the author to imply that Decent Exposure is
@@ -34,6 +34,46 @@ ID and try to perform `Thing.find(id)`. If the ID isn't found, it'll call
34
34
  `Thing.new(things_params)`. The result will be memoized in an `@exposed_thing`
35
35
  instance variable.
36
36
 
37
+ ## Example Controller
38
+
39
+ Here's what a standard Rails 6 CRUD controller using Adequate Exposure might look like:
40
+
41
+ ```ruby
42
+ class ThingsController < ApplicationController
43
+ expose :things, ->{ Thing.all }
44
+ expose :thing
45
+
46
+ def create
47
+ if thing.save
48
+ redirect_to thing_path(thing)
49
+ else
50
+ render :new
51
+ end
52
+ end
53
+
54
+ def update
55
+ if thing.update(thing_params)
56
+ redirect_to thing_path(thing)
57
+ else
58
+ render :edit
59
+ end
60
+ end
61
+
62
+ def destroy
63
+ thing.destroy
64
+ redirect_to things_path
65
+ end
66
+
67
+ private
68
+
69
+ def thing_params
70
+ params.require(:thing).permit(:foo, :bar)
71
+ end
72
+ end
73
+ ```
74
+
75
+ ## Under the Hood
76
+
37
77
  The default resolving workflow is pretty powerful and customizable. It could be
38
78
  expressed with the following pseudocode:
39
79
 
@@ -120,7 +160,7 @@ with less code:
120
160
 
121
161
  ```ruby
122
162
  expose :comments, from: :post
123
- # equivalent to
163
+ # equivalent to
124
164
  expose :comments, ->{ post.comments }
125
165
  ```
126
166
 
@@ -159,7 +199,7 @@ expose :thing, id: ->{ params[:try_this_id] || params[:or_maybe_that_id] }
159
199
  ### `find`
160
200
 
161
201
  If an ID was provided, Adequate Exposure will try to find the model using it.
162
- Default behavior could be expressed with this configuration:
202
+ Default behavior could be expressed with this configuration:
163
203
 
164
204
  ```ruby
165
205
  expose :thing, find: ->(id, scope){ scope.find(id) }
@@ -273,7 +313,6 @@ expose :thing, with: [:cool_find, :cool_build]
273
313
  expose :another_thing, with: :cool_build
274
314
  ```
275
315
 
276
-
277
316
  ## Contributing
278
317
 
279
318
  1. Fork it (https://github.com/rwz/adequate_exposure/fork)
data/Rakefile CHANGED
@@ -1,6 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
+ require "bundler/setup"
2
3
  require "rspec/core/rake_task"
4
+ require "appraisal/task"
3
5
 
6
+ Appraisal::Task.new
4
7
  RSpec::Core::RakeTask.new
5
8
 
6
9
  task default: :spec
@@ -12,8 +12,8 @@ Gem::Specification.new do |spec|
12
12
  spec.test_files = spec.files.grep(/\Aspec\//)
13
13
  spec.require_path = "lib"
14
14
 
15
- spec.required_ruby_version = ">= 1.9.3"
15
+ spec.required_ruby_version = ">= 2.0"
16
16
 
17
- spec.add_dependency "railties", "~> 4.0"
18
- spec.add_dependency "activesupport", "~> 4.0"
17
+ spec.add_dependency "railties", ">= 5.0", "< 7"
18
+ spec.add_dependency "activesupport", ">= 5.0", "< 7"
19
19
  end
@@ -0,0 +1,12 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal", "~> 2.1"
6
+ gem "pry", "~> 0.10"
7
+ gem "rake", "~> 10.3"
8
+ gem "rspec-rails", "~> 3.9"
9
+ gem "railties", "~> 5.0.0"
10
+ gem "activesupport", "~> 5.0.0"
11
+
12
+ gemspec path: "../"
@@ -0,0 +1,12 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal", "~> 2.1"
6
+ gem "pry", "~> 0.10"
7
+ gem "rake", "~> 10.3"
8
+ gem "rspec-rails", "~> 3.9"
9
+ gem "railties", "~> 5.1.0"
10
+ gem "activesupport", "~> 5.1.0"
11
+
12
+ gemspec path: "../"
@@ -0,0 +1,12 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal", "~> 2.1"
6
+ gem "pry", "~> 0.10"
7
+ gem "rake", "~> 10.3"
8
+ gem "rspec-rails", "~> 3.9"
9
+ gem "railties", "~> 5.2.0"
10
+ gem "activesupport", "~> 5.2.0"
11
+
12
+ gemspec path: "../"
@@ -0,0 +1,12 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal", "~> 2.2"
6
+ gem "pry", "~> 0.12"
7
+ gem "rake", "~> 12.0"
8
+ gem "rspec-rails", "~> 3.9"
9
+ gem "railties", "~> 6.0.0"
10
+ gem "activesupport", "~> 6.0.0"
11
+
12
+ gemspec path: "../"
@@ -6,6 +6,7 @@ module AdequateExposure
6
6
  autoload :Exposure, "adequate_exposure/exposure"
7
7
  autoload :Attribute, "adequate_exposure/attribute"
8
8
  autoload :Context, "adequate_exposure/context"
9
+ autoload :Behavior, "adequate_exposure/behavior"
9
10
  autoload :Flow, "adequate_exposure/flow"
10
11
 
11
12
  ActiveSupport.on_load :action_controller do
@@ -2,20 +2,42 @@ module AdequateExposure
2
2
  class Attribute
3
3
  attr_reader :name, :fetch, :ivar_name
4
4
 
5
+ # Public: Initialize an Attribute
6
+ #
7
+ # options - Hash of options for the Attribute
8
+ # :name - The String name of the Attribute instance
9
+ # :fetch - The Proc fetch to calculate
10
+ # the value of the Attribute instance.
11
+ # This is only called if the attribute's
12
+ # instance variable is not defined.
13
+ # :ivar_name - The String instance variable name that
14
+ # is associated with the attribute.
5
15
  def initialize(options)
6
16
  @name = options.fetch(:name)
7
17
  @fetch = options.fetch(:fetch)
8
18
  @ivar_name = options.fetch(:ivar_name)
9
19
  end
10
20
 
21
+ # Public: The getter method for the Attribute.
22
+ #
23
+ # Returns the name of the Attribute as a Symbol.
11
24
  def getter_method_name
12
25
  name.to_sym
13
26
  end
14
27
 
28
+ # Public: The setter method for the Attribute.
29
+ #
30
+ # Returns the name of the attribute as a Symbol with an appended '='.
15
31
  def setter_method_name
16
32
  "#{name}=".to_sym
17
33
  end
18
34
 
35
+
36
+ # Public: Expose a getter and setter method for the Attribute
37
+ # on the passed in Controller class.
38
+ #
39
+ # klass - The Controller class where the Attribute getter and setter
40
+ # methods will be exposed.
19
41
  def expose!(klass)
20
42
  attribute = self
21
43
 
@@ -0,0 +1,100 @@
1
+ module AdequateExposure
2
+ module Behavior
3
+ # Public: Fetches a scope.
4
+ #
5
+ # Finds an object. If it isn't found, the object gets instantiated.
6
+ #
7
+ # Returns the decorated object.
8
+ def fetch
9
+ instance = id ? find(id, computed_scope) : build(build_params, computed_scope)
10
+ decorate(instance)
11
+ end
12
+
13
+ # Public: Checks a params hash for an id attribute.
14
+ #
15
+ # Checks a hash of parameters for keys that represent an object's id.
16
+ #
17
+ # Returns the value of the id parameter, if it exists. Otherwise nil.
18
+ def id
19
+ params_id_key_candidates.each do |key|
20
+ value = params[key]
21
+ return value if value.present?
22
+ end
23
+
24
+ nil
25
+ end
26
+
27
+ # Public: An object query. Essentially, this method is designed to be
28
+ # overridden.
29
+ #
30
+ # model - The Class to be scoped or queried.
31
+ #
32
+ # Returns the object scope.
33
+ def scope(model)
34
+ model
35
+ end
36
+
37
+ # Public: Converts a name into a standard Class name.
38
+ #
39
+ # Examples
40
+ # 'egg_and_hams'.model # => EggAndHam
41
+ #
42
+ # Returns a standard Class name.
43
+ def model
44
+ name.to_s.classify.constantize
45
+ end
46
+
47
+ # Public: Find an object on the supplied scope.
48
+ #
49
+ # id - The Integer id attribute of the desired object
50
+ # scope - The collection that will be searched.
51
+ #
52
+ # Returns the found object.
53
+ def find(id, scope)
54
+ scope.find(id)
55
+ end
56
+
57
+ # Public: Builds a new object on the passed-in scope.
58
+ #
59
+ # params - A Hash of attributes for the object to-be built.
60
+ # scope - The collection that will be searched.
61
+ #
62
+ # Returns the new object.
63
+ def build(params, scope)
64
+ scope.new(params)
65
+ end
66
+
67
+ # Public: Returns a decorated object. This method is designed to be
68
+ # overridden.
69
+ #
70
+ # Returns the decorated object.
71
+ def decorate(instance)
72
+ instance
73
+ end
74
+
75
+ # Public: Get all the parameters of the current request.
76
+ #
77
+ # Returns the controller's parameters for the current request.
78
+ def build_params
79
+ if controller.respond_to?(params_method_name, true) && !get_request?
80
+ controller.send(params_method_name)
81
+ else
82
+ {}
83
+ end
84
+ end
85
+
86
+ protected
87
+
88
+ def params_id_key_candidates
89
+ [ "#{model_param_key}_id", "#{name}_id", "id" ].uniq
90
+ end
91
+
92
+ def model_param_key
93
+ model.name.underscore
94
+ end
95
+
96
+ def computed_scope
97
+ scope(model)
98
+ end
99
+ end
100
+ end
@@ -2,14 +2,33 @@ module AdequateExposure
2
2
  class Context
3
3
  attr_reader :context, :attribute
4
4
 
5
+ # Public: Initialize a context.
6
+ #
7
+ # context - The Class where the attribute is defined.
8
+ # attribute - The attribute that will be accessed by a getter
9
+ # and setter.
5
10
  def initialize(context, attribute)
6
11
  @context, @attribute = context, attribute
7
12
  end
8
13
 
14
+ # Public: Read an attribute on the context Class.
15
+ #
16
+ # Get an attribute's value. If the attribute's instance
17
+ # variable is not defined, it will create one,
18
+ # execute attribute#fetch, and assign the result
19
+ # to the instance variable.
20
+ #
21
+ # Returns the attribute's value.
9
22
  def get
10
23
  ivar_defined?? ivar_get : set(fetch_value)
11
24
  end
12
25
 
26
+ # Public: Write to an attribute on the context Class.
27
+ #
28
+ # value - The value that will be set to the attribute's
29
+ # instance variable.
30
+ #
31
+ # Returns the attribute's value.
13
32
  def set(value)
14
33
  ivar_set(value)
15
34
  end
@@ -2,18 +2,48 @@ module AdequateExposure
2
2
  module Controller
3
3
  extend ActiveSupport::Concern
4
4
 
5
- included{ class_attribute :exposure_configuration }
5
+ included do
6
+ class_attribute :exposure_configuration,
7
+ instance_accessor: false, instance_predicate: false
8
+ end
6
9
 
7
10
  module ClassMethods
8
- def expose(*args, &block)
9
- Exposure.expose! self, *args, &block
11
+ # Public: Exposes an attribute to a controller Class.
12
+ #
13
+ # *args - An Array of attributes for the new exposure. See
14
+ # Exposure#initialize for attribute details.
15
+ # block - If supplied, the exposed attribute method executes
16
+ # the Proc when accessed.
17
+ #
18
+ # Returns the helper methods that are now defined on the class
19
+ # where this method is included.
20
+ def expose(*args, **options, &block)
21
+ Exposure.expose! self, *args, **options, &block
10
22
  end
11
23
 
24
+ # Public: Exposes an attribute to a controller Class.
25
+ # The exposed methods are then set to a before_action
26
+ # callback.
27
+ #
28
+ # name - The String name of the Exposure instance.
29
+ # *args - An Array of attributes for the new exposure. See
30
+ # Exposure#initialize for attribute details.
31
+ # block - If supplied, the exposed attribute method executes
32
+ # the Proc when accessed.
33
+ #
34
+ # Sets the exposed attribute to a before_action callback in the
35
+ # controller.
12
36
  def expose!(name, *args, &block)
13
37
  expose name, *args, &block
14
38
  before_action name
15
39
  end
16
40
 
41
+ # Public: Configures an Exposure instance for a controller Class.
42
+ #
43
+ # name - The String name of the Exposure instance.
44
+ # options - The Hash of options to configure the Exposure instance.
45
+ #
46
+ # Returns the exposure configuration Hash.
17
47
  def exposure_config(name, options)
18
48
  store = self.exposure_configuration ||= {}
19
49
  self.exposure_configuration = store.merge(name => options)
@@ -2,14 +2,41 @@ module AdequateExposure
2
2
  class Exposure
3
3
  attr_reader :controller, :options
4
4
 
5
- def self.expose!(*args, &block)
6
- new(*args, &block).expose!
7
- end
8
-
9
- def initialize(controller, name, *args, &block)
5
+ # Public: Initializes an Exposure and makes it accessible to a controller.
6
+ # For each Exposure, a getter and setter is defined.
7
+ # Those getters and setters are made available to
8
+ # the controller as helper methods.
9
+ #
10
+ # *args - An Array of all parameters for the new Exposure. See
11
+ # #initialize.
12
+ # **args - A Hash of options. See #initialize.
13
+ # block - If supplied, the exposed attribute method executes
14
+ # the Proc when called.
15
+ #
16
+ # Returns a collection of exposed helper methods.
17
+ def self.expose!(*args, **options, &block)
18
+ new(*args, **options, &block).expose!
19
+ end
20
+
21
+ # Public: Initalize an Exposure with a hash of options.
22
+ #
23
+ # If a block is given, the Proc is assigned to value
24
+ # of options[name].
25
+ #
26
+ # The `asserts_*` section raise errors if the controller
27
+ # was initialized with an unacceptable options Hash.
28
+ #
29
+ # controller - The Controller class where methods will be exposed.
30
+ # name - The String name of the Exposure instance.
31
+ # fetch_block - Proc that will be executed if the exposed
32
+ # attribute has no value (default: nil).
33
+ # options - Hash of options for the Behavior of the exposed methods.
34
+ # block - If supplied, the exposed attribute method executes
35
+ # the Proc.
36
+ #
37
+ # Returns a normalized options Hash.
38
+ def initialize(controller, name, fetch_block=nil, **options, &block)
10
39
  @controller = controller
11
- options = args.extract_options!
12
- fetch_block = args.pop
13
40
  @options = options.with_indifferent_access.merge(name: name)
14
41
 
15
42
  merge_lambda_option :fetch, fetch_block if fetch_block
@@ -24,6 +51,11 @@ module AdequateExposure
24
51
  normalize_options
25
52
  end
26
53
 
54
+ # Public: Creates a getter and setter methods for the attribute.
55
+ # Those methods are made avaiable to the controller as
56
+ # helper methods.
57
+ #
58
+ # Returns a collection of exposed helper methods.
27
59
  def expose!
28
60
  expose_attribute!
29
61
  expose_helper_methods!
@@ -76,7 +108,9 @@ module AdequateExposure
76
108
  exposure_name = options.fetch(:name)
77
109
 
78
110
  if from = options.delete(:from)
79
- merge_lambda_option :fetch, ->{ send(from).send(exposure_name) }
111
+ merge_lambda_option :build, ->{ send(from).send(exposure_name) }
112
+ merge_lambda_option :model, ->{ nil }
113
+ merge_lambda_option :id, ->{ nil }
80
114
  end
81
115
  end
82
116
 
@@ -161,7 +195,7 @@ module AdequateExposure
161
195
  end
162
196
 
163
197
  def assert_singleton_option(name)
164
- if options.except(name, :name).any? && options.key?(name)
198
+ if options.except(name, :name, :decorate).any? && options.key?(name)
165
199
  fail ArgumentError, "Using #{name.inspect} option with other options doesn't make sense"
166
200
  end
167
201
  end
@@ -2,63 +2,39 @@ module AdequateExposure
2
2
  class Flow
3
3
  attr_reader :controller, :options, :name
4
4
 
5
+ # Public: Initialize a Flow. This object responds to missing
6
+ # methods errors and attempts to delegate them to other objects.
7
+ #
8
+ # controller - The Controller class where the method was called.
9
+ # options - The options Hash of the Exposure instance being called.
10
+ # name - The String name of the Exposure instance.
5
11
  def initialize(controller, options)
6
12
  @controller = controller
7
13
  @options = options
8
14
  @name = options.fetch(:name)
9
15
  end
10
16
 
11
- %w[fetch find build build_params scope model id decorate].each do |method_name|
12
- define_method method_name do |*args|
13
- ivar_name = "@#{method_name}"
14
- return instance_variable_get(ivar_name) if instance_variable_defined?(ivar_name)
15
- instance_variable_set(ivar_name, handle_action(method_name, *args))
17
+ # Public: Attempts to re-delegate a method missing to the
18
+ # supplied block or the Behavior object.
19
+ #
20
+ # name - The String name of the Exposure instance.
21
+ # *args - The arguments given for the missing method.
22
+ # block - The Proc invoked by the method.
23
+ def method_missing(name, *args, &block)
24
+ if respond_to_missing?(name)
25
+ handle_flow_method(name, *args, &block)
26
+ else
27
+ super
16
28
  end
17
29
  end
18
30
 
19
- protected
20
-
21
- def default_fetch
22
- computed_scope = scope(model)
23
- instance = id ? find(id, computed_scope) : build(build_params, computed_scope)
24
- decorate(instance)
25
- end
26
-
27
- def default_id
28
- params_id_key_candidates.each do |key|
29
- value = params[key]
30
- return value if value.present?
31
- end
32
-
33
- nil
34
- end
35
-
36
- def default_scope(model)
37
- model
38
- end
39
-
40
- def default_model
41
- name.to_s.classify.constantize
42
- end
43
-
44
- def default_find(id, scope)
45
- scope.find(id)
46
- end
47
-
48
- def default_build(params, scope)
49
- scope.new(params)
50
- end
51
-
52
- def default_decorate(instance)
53
- instance
54
- end
55
-
56
- def default_build_params
57
- if controller.respond_to?(params_method_name, true) && !get_request?
58
- controller.send(params_method_name)
59
- else
60
- {}
61
- end
31
+ # Public: Checks if the Behavior class can handle the missing method.
32
+ #
33
+ # method_name - The name of method that has been called.
34
+ # include_private - Prevents this method from catching calls to private
35
+ # method (default: false).
36
+ def respond_to_missing?(method_name, include_private = false)
37
+ Behavior.method_defined?(method_name) || super
62
38
  end
63
39
 
64
40
  private
@@ -73,15 +49,17 @@ module AdequateExposure
73
49
  options.fetch(:build_params_method){ "#{name}_params" }
74
50
  end
75
51
 
76
- def handle_action(name, *args)
77
- if options.key?(name)
78
- handle_custom_action(name, *args)
79
- else
80
- send("default_#{name}", *args)
52
+ def handle_flow_method(name, *args, &block)
53
+ fetch_ivar name do
54
+ if options.key?(name)
55
+ handle_options_override(name, *args, &block)
56
+ else
57
+ handle_default_flow_method(name, *args, &block)
58
+ end
81
59
  end
82
60
  end
83
61
 
84
- def handle_custom_action(name, *args)
62
+ def handle_options_override(name, *args)
85
63
  value = options[name]
86
64
 
87
65
  if Proc === value
@@ -92,12 +70,20 @@ module AdequateExposure
92
70
  end
93
71
  end
94
72
 
95
- def params_id_key_candidates
96
- [ "#{model.name.underscore}_id", "#{name}_id", "id" ].uniq
73
+ def handle_default_flow_method(name, *args, &block)
74
+ method = Behavior.instance_method(name)
75
+ method.bind(self).call(*args, &block)
97
76
  end
98
77
 
99
- def model_param_key
100
- model.name.underscore
78
+
79
+ def fetch_ivar(name)
80
+ ivar_name = "@#{name}"
81
+
82
+ if instance_variable_defined?(ivar_name)
83
+ instance_variable_get(ivar_name)
84
+ else
85
+ instance_variable_set(ivar_name, yield)
86
+ end
101
87
  end
102
88
  end
103
89
  end
@@ -1,3 +1,3 @@
1
1
  module AdequateExposure
2
- VERSION = "0.2.1"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -23,8 +23,8 @@ describe AdequateExposure::Controller do
23
23
  before{ allow(controller).to receive(:request){ request } }
24
24
 
25
25
  %w[expose expose! exposure_config].each do |method_name|
26
- define_method method_name do |*args, &block|
27
- controller_klass.send method_name, *args, &block
26
+ define_method method_name do |*args, **keyword_args, &block|
27
+ controller_klass.public_send method_name, *args, **keyword_args, &block
28
28
  end
29
29
  end
30
30
 
@@ -348,10 +348,9 @@ describe AdequateExposure::Controller do
348
348
 
349
349
  context "from option" do
350
350
  it "allows scope to be called from method" do
351
- post = double("Post")
352
351
  comments = double("Comments")
352
+ post = double("Post", comments: comments)
353
353
  allow(controller).to receive(:post).and_return(post)
354
- expect(post).to receive(:comments).and_return(comments)
355
354
  expose :comments, from: :post
356
355
 
357
356
  expect(controller.comments).to eq(comments)
@@ -361,5 +360,15 @@ describe AdequateExposure::Controller do
361
360
  action = ->{ expose :thing, from: :foo, parent: :bar }
362
361
  expect(&action).to raise_error(ArgumentError, "Using :from option with other options doesn't make sense")
363
362
  end
363
+
364
+ it "should still work with decorate option" do
365
+ decorated_thing = double("DecoratedThing")
366
+ thing = double("Thing")
367
+ foo = double("Foo", thing: thing)
368
+ expect(controller).to receive(:foo).and_return(foo)
369
+ expect(controller).to receive(:decorate).with(thing).and_return(decorated_thing)
370
+ expose :thing, from: :foo, decorate: ->(thing){ decorate(thing) }
371
+ expect(controller.thing).to eq(decorated_thing)
372
+ end
364
373
  end
365
374
  end
@@ -9,11 +9,11 @@ describe BirdsController, type: :controller do
9
9
  after{ expect(controller.bird).to eq(mockingbird) }
10
10
 
11
11
  it "finds model by id" do
12
- get :show, id: "mockingbird"
12
+ get :show, params: { id: "mockingbird" }
13
13
  end
14
14
 
15
15
  it "finds model by bird_id" do
16
- get :show, bird_id: "mockingbird"
16
+ get :show, params: { bird_id: "mockingbird" }
17
17
  end
18
18
  end
19
19
 
@@ -32,7 +32,7 @@ class BirdsController < ApplicationController
32
32
  expose :bird
33
33
 
34
34
  def show
35
- render nothing: true
35
+ head :ok
36
36
  end
37
37
  end
38
38
 
metadata CHANGED
@@ -1,43 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adequate_exposure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pavel Pravosud
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-01 00:00:00.000000000 Z
11
+ date: 2020-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.0'
19
+ version: '5.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '5.0'
30
+ - - "<"
25
31
  - !ruby/object:Gem::Version
26
- version: '4.0'
32
+ version: '7'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: activesupport
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - "~>"
37
+ - - ">="
32
38
  - !ruby/object:Gem::Version
33
- version: '4.0'
39
+ version: '5.0'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '7'
34
43
  type: :runtime
35
44
  prerelease: false
36
45
  version_requirements: !ruby/object:Gem::Requirement
37
46
  requirements:
38
- - - "~>"
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '5.0'
50
+ - - "<"
39
51
  - !ruby/object:Gem::Version
40
- version: '4.0'
52
+ version: '7'
41
53
  description:
42
54
  email:
43
55
  - pavel@pravosud.com
@@ -47,13 +59,19 @@ extra_rdoc_files: []
47
59
  files:
48
60
  - ".gitignore"
49
61
  - ".travis.yml"
62
+ - Appraisals
50
63
  - Gemfile
51
64
  - LICENSE.txt
52
65
  - README.md
53
66
  - Rakefile
54
67
  - adequate_exposure.gemspec
68
+ - gemfiles/rails_5.0.gemfile
69
+ - gemfiles/rails_5.1.gemfile
70
+ - gemfiles/rails_5.2.gemfile
71
+ - gemfiles/rails_6.0.gemfile
55
72
  - lib/adequate_exposure.rb
56
73
  - lib/adequate_exposure/attribute.rb
74
+ - lib/adequate_exposure/behavior.rb
57
75
  - lib/adequate_exposure/context.rb
58
76
  - lib/adequate_exposure/controller.rb
59
77
  - lib/adequate_exposure/exposure.rb
@@ -75,15 +93,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
93
  requirements:
76
94
  - - ">="
77
95
  - !ruby/object:Gem::Version
78
- version: 1.9.3
96
+ version: '2.0'
79
97
  required_rubygems_version: !ruby/object:Gem::Requirement
80
98
  requirements:
81
99
  - - ">="
82
100
  - !ruby/object:Gem::Version
83
101
  version: '0'
84
102
  requirements: []
85
- rubyforge_project:
86
- rubygems_version: 2.2.2
103
+ rubygems_version: 3.1.2
87
104
  signing_key:
88
105
  specification_version: 4
89
106
  summary: Exposing things, adequately