adequate_exposure 0.2.1 → 0.6.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
- 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