rubanok 0.1.3 → 0.2.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
2
  SHA256:
3
- metadata.gz: 76b1edc5c67f7a783dc3db022fee6aba015af2745a877d0b05089ce2c2ca2df7
4
- data.tar.gz: d3e055c5453c793e17d0c6efea3b0bd6bcb3c8b16ada1461544b28431058a3a1
3
+ metadata.gz: 657a457fbefb6bf0f596ec5d0933dce51cbf74ca2a6cfed72f9c439d02e3d727
4
+ data.tar.gz: a820673bee22722ad9291332e2cbcef9aa0088a07c0077cc24d99777eca73f3b
5
5
  SHA512:
6
- metadata.gz: ab19e7909a899d1831f246164888e76c29c1dfd6f9d3864a801b69fd3caba8cc754402a3e3e11ae6a0185bb87cec76711dadcfd8762e32eb4923cf24d3e16bae
7
- data.tar.gz: 91c17c4695b0ccc7ff6ab0b75cace311c61b9ca3edf29a43bbd2c747dbb5995bc6f2af45ca6966117dc45304c8cf1ca2f58401bd7329d966a180d8c2942b24dc
6
+ metadata.gz: af7d742f04ba7399d40e92e66a5e26dda0dfa487254f347ee450020fe308e9f1aab906a69c1ba04468825619f3e49acb3ecca61ce22615ce1aab07c150dbe4ee
7
+ data.tar.gz: 75fb744a42d0248cc6b4b83da6758fca149fc1212fa4fc434a02ece81ce4bd046653192de7209ef0660f4c3302fb24301b07c418c82f1b6e6d0457000e820428
@@ -23,6 +23,9 @@ Style/TrailingCommaInArrayLiteral:
23
23
  Style/TrailingCommaInHashLiteral:
24
24
  EnforcedStyleForMultiline: no_comma
25
25
 
26
+ Style/SignalException:
27
+ Enabled: true
28
+
26
29
  Style/FrozenStringLiteralComment:
27
30
  Enabled: true
28
31
 
@@ -8,8 +8,8 @@ notifications:
8
8
  email: false
9
9
 
10
10
  before_install:
11
- - gem update --system
12
- - gem install bundler
11
+ - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
12
+ - gem install bundler -v '< 2'
13
13
 
14
14
  matrix:
15
15
  fast_finish: true
@@ -2,6 +2,37 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.2.0 (2019-08-23)
6
+
7
+ - Add `Process.project` and `rubanok_scope` methods to get the Hash of recognized params. ([@palkan][])
8
+
9
+ ```ruby
10
+ class PostsProcessor < Rubanok::Processor
11
+ map :q { ... }
12
+ match :page, :per_page, activate_on: :page { ... }
13
+ end
14
+
15
+ PostsProcessor.project(q: "search_me", filter: "smth", page: 2)
16
+ # => { q: "search_me", page: 2 }
17
+
18
+ class PostsController < ApplicationController
19
+ def index
20
+ @filter_params = rubanok_scope
21
+ # or
22
+ @filter_params = rubanok_scope params.require(:filter), with: PostsProcessor
23
+ # ...
24
+ end
25
+ end
26
+ ```
27
+
28
+ - Improve naming by using "processor" instead of "plane". ([@palkan][])
29
+
30
+ See [the discussion](https://github.com/palkan/rubanok/issues/3).
31
+
32
+ **NOTE**: Older API is still available without deprecation.
33
+
34
+ - Add `fail_when_no_matches` parameter to `match` method. ([@Earendil95][])
35
+
5
36
  ## 0.1.3 (2019-03-05)
6
37
 
7
38
  - Fix using `activate_always: true` with `default` matching clause. ([@palkan][])
@@ -19,3 +50,4 @@ Initial implementation.
19
50
  Proposal added.
20
51
 
21
52
  [@palkan]: https://github.com/palkan
53
+ [@Earendil95]: https://github.com/Earendil95
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Rubanok provides a DSL to build parameters-based data transformers.
6
6
 
7
- 📖 Read the introduction post: ["Carve your controllers like Papa Carlo"](https://dev.to/evilmartians/carve-your-controllers-like-papa-carlo-32m6)
7
+ 📖 Read the introduction post: ["Carve your controllers like Papa Carlo"](https://evilmartians.com/chronicles/rubanok-carve-your-rails-controllers-like-papa-carlo)
8
8
 
9
9
  The typical usage is to describe all the possible collection manipulation for REST `index` action, e.g. filtering, sorting, searching, pagination, etc..
10
10
 
@@ -28,13 +28,13 @@ You have:
28
28
  ```ruby
29
29
  class CourseSessionController < ApplicationController
30
30
  def index
31
- @sessions = planish(
31
+ @sessions = rubanok_process(
32
32
  # pass input
33
33
  CourseSession.all,
34
34
  # pass params
35
35
  params,
36
- # provide a plane to use
37
- with: CourseSessionsPlane
36
+ # provide a processor to use
37
+ with: CourseSessionsProcessor
38
38
  )
39
39
  end
40
40
  end
@@ -42,16 +42,16 @@ end
42
42
 
43
43
  Or we can try to infer all the configuration for you:
44
44
 
45
-
46
45
  ```ruby
47
46
  class CourseSessionController < ApplicationController
48
47
  def index
49
- @sessions = planish(CourseSession.all)
48
+ @sessions = rubanok_process(CourseSession.all)
50
49
  end
51
50
  end
52
51
  ```
53
52
 
54
53
  Requirements:
54
+
55
55
  - Ruby ~> 2.5
56
56
  - Rails >= 4.2 (only for using with Rails)
57
57
 
@@ -70,12 +70,12 @@ And run `bundle install`.
70
70
 
71
71
  ## Usage
72
72
 
73
- The core concept of this library is a _plane_ (or _hand plane_, or "рубанок" in Russian). Plane is responsible for mapping parameters to transformations.
73
+ The core concept of this library is a processor (previously called _plane_ or _hand plane_, or "рубанок" in Russian). Processor is responsible for mapping parameters to transformations.
74
74
 
75
75
  From the example above:
76
76
 
77
77
  ```ruby
78
- class CourseSessionsPlane < Rubanok::Plane
78
+ class CourseSessionsProcessor < Rubanok::Processor
79
79
  # You can map keys
80
80
  map :q do |q:|
81
81
  # `raw` is an accessor for input data
@@ -84,7 +84,7 @@ class CourseSessionsPlane < Rubanok::Plane
84
84
  end
85
85
 
86
86
  # The following code
87
- CourseSessionsPlane.call(CourseSession.all, q: "xyz")
87
+ CourseSessionsProcessor.call(CourseSession.all, q: "xyz")
88
88
 
89
89
  # is equal to
90
90
  CourseSession.all.search("xyz")
@@ -93,7 +93,7 @@ CourseSession.all.search("xyz")
93
93
  You can map multiple keys at once:
94
94
 
95
95
  ```ruby
96
- class CourseSessionsPlane < Rubanok::Plane
96
+ class CourseSessionsProcessor < Rubanok::Processor
97
97
  DEFAULT_PAGE_SIZE = 25
98
98
 
99
99
  map :page, :per_page do |page:, per_page: DEFAULT_PAGE_SIZE|
@@ -105,7 +105,7 @@ end
105
105
  There is also `match` method to handle values:
106
106
 
107
107
  ```ruby
108
- class CourseSessionsPlane < Rubanok::Plane
108
+ class CourseSessionsProcessor < Rubanok::Processor
109
109
  SORT_ORDERS = %w(asc desc).freeze
110
110
  SORTABLE_FIELDS = %w(id name created_at).freeze
111
111
 
@@ -132,11 +132,54 @@ class CourseSessionsPlane < Rubanok::Plane
132
132
  raw.order(sort_by => sort)
133
133
  end
134
134
  end
135
+
136
+ # strict matching; if Processor will not match parameter, it will raise Rubanok::UnexpectedInputError
137
+ # You can handle it in controller, for example, with sending 422 Unprocessable Entity to client
138
+ match :filter, fail_when_no_matches: true do
139
+ having "active" do
140
+ raw.active
141
+ end
142
+
143
+ having "finished" do
144
+ raw.finished
145
+ end
146
+ end
135
147
  end
136
148
  ```
137
149
 
150
+ By default, Rubanok will not fail if no matches found in `match` rule. You can change it by setting: `Rubanok.fail_when_no_matches = true`.
151
+ If in example above you will call `CourseSessionsProcessor.call(CourseSession, filter: 'acitve')`, you will get `Rubanok::UnexpectedInputError: Unexpected input: {:filter=>'acitve'}`.
152
+
138
153
  **NOTE:** Rubanok only matches exact values; more complex matching could be added in the future.
139
154
 
155
+ ### Getting the matching params
156
+
157
+ Sometimes it could be useful to get the params that were used to process the data by Rubanok processor (e.g., you can use this data in views to display the actual filters state).
158
+
159
+ In Rails, you can use the `#rubanok_scope` method for that:
160
+
161
+ ```ruby
162
+ class CourseSessionController < ApplicationController
163
+ def index
164
+ @sessions = rubanok_process(CourseSession.all)
165
+ # Returns the Hash of params recognized by the CourseSessionProcessor.
166
+ # For example:
167
+ #
168
+ # params == {q: "search", role_id: 2, date: "2019-08-22"}
169
+ # @session_filter == {q: "search", role_id: 2}
170
+ @sessions_filter = rubanok_scope(
171
+ params.permit(:q, :role_id),
172
+ with: CourseSessionProcessor
173
+ )
174
+
175
+ # You can omit all the arguments
176
+ @sessions_filter = rubanok_scope #=> equals to rubanok_scope(params, with: implicit_rubanok_class)
177
+ end
178
+ end
179
+ ```
180
+
181
+ You can also accesss `rubanok_scope` in views (it's a helper method).
182
+
140
183
  ### Rule activation
141
184
 
142
185
  Rubanok _activates_ a rule by checking whether the corresponding keys are present in the params object. All the fields must be present to apply the rule.
@@ -165,7 +208,7 @@ One of the benefits of having modification logic contained in its own class is t
165
208
 
166
209
  ```ruby
167
210
  # For example, with RSpec
168
- describe CourseSessionsPlane do
211
+ RSpec.describe CourseSessionsProcessor do
169
212
  let(:input ) { CourseSession.all }
170
213
  let(:params) { {} }
171
214
 
@@ -182,19 +225,19 @@ end
182
225
  Now in your controller you only have to test that the specific _plane_ is applied:
183
226
 
184
227
  ```ruby
185
- describe CourseSessionController do
228
+ RSpec.describe CourseSessionController do
186
229
  subject { get :index }
187
230
 
188
231
  specify do
189
- expect { subject }.to have_planished(CourseSession.all).
190
- with(CourseSessionsPlane)
232
+ expect { subject }.to have_rubanok_processed(CourseSession.all).
233
+ with(CourseSessionsProcessor)
191
234
  end
192
235
  end
193
236
  ```
194
237
 
195
238
  **NOTE**: input matching only checks for the class equality.
196
239
 
197
- To use `have_planished` matcher you must add the following line to your `spec_helper.rb` / `rails_helper.rb` (it's added automatically if RSpec defined and `RAILS_ENV`/`RACK_ENV` is equal to `"test"`):
240
+ To use `have_rubanok_processed` matcher you must add the following line to your `spec_helper.rb` / `rails_helper.rb` (it's added automatically if RSpec defined and `RAILS_ENV`/`RACK_ENV` is equal to `"test"`):
198
241
 
199
242
  ```ruby
200
243
  require "rubanok/rspec"
@@ -202,33 +245,20 @@ require "rubanok/rspec"
202
245
 
203
246
  ### Rails vs. non-Rails
204
247
 
205
- Rubanok does not require Rails, but it has some useful Rails extensions such as `planish` helper for controllers (included automatically into `ActionController::Base` and `ActionController::API`).
248
+ Rubanok does not require Rails, but it has some useful Rails extensions such as `rubanok_process` helper for controllers (included automatically into `ActionController::Base` and `ActionController::API`).
206
249
 
207
250
  If you use `ActionController::Metal` you must include the `Rubanok::Controller` module yourself.
208
251
 
209
- ## Questions & Answers
210
-
211
- - **🧐"Planish"? Is there a word?**
212
-
213
- Yes, [it is](https://en.wiktionary.org/wiki/planish).
214
-
215
- - **Where to put my _plane_ classes?**
252
+ ### Processor class inference in Rails controllers
216
253
 
217
- I put mine under `app/planes` (as `<resources>_plane.rb`) in my Rails app.
218
-
219
- - **I don't like the naming ("planes" ✈️?), can I still use the library?**
254
+ By default, `rubanok_process` uses the following algorithm to define a processor class: `"#{controller_path.classify.pluralize}Processor".safe_constantize`.
220
255
 
221
- First, feel free to [propose your variant](https://github.com/palkan/rubanok/issues). We would be glad to discuss it.
222
-
223
- Secondly, you can easily avoid it by adding a few lines to your `ApplicationController`:
256
+ You can change this by overriding the `#implicit_rubanok_class` method:
224
257
 
225
258
  ```ruby
226
259
  class ApplicationController < ActionController::Smth
227
- # add `planish` alias
228
- alias transform_scope planish
229
-
230
- # override the `implicit_plane_class` method
231
- def implicit_plane_class
260
+ # override the `implicit_rubanok_class` method
261
+ def implicit_rubanok_class
232
262
  "#{controller_path.classify.pluralize}Scoper".safe_constantize
233
263
  end
234
264
  end
@@ -239,13 +269,25 @@ Now you can use it like this:
239
269
  ```ruby
240
270
  class CourseSessionsController < ApplicationController
241
271
  def index
242
- @sessions = transform_scope(CourseSession.all, params)
272
+ @sessions = rubanok_process(CourseSession.all, params)
243
273
  # which equals to
244
274
  @sessions = CourseSessionsScoper.call(CourseSession.all, params.to_unsafe_h)
245
275
  end
246
276
  end
247
277
  ```
248
278
 
279
+ **NOTE:** the `planish` method is still available and it uses `#{controller_path.classify.pluralize}Plane".safe_constantize` under the hood (via the `#implicit_plane_class` method).
280
+
281
+ ## Questions & Answers
282
+
283
+ - **Where to put my processor/plane classes?**
284
+
285
+ I put mine under `app/planes` (as `<resources>_plane.rb`) in my Rails app.
286
+
287
+ - **I don't like the naming ("planes" ✈️?), can I still use the library?**
288
+
289
+ Good news—the default naming [has been changed](https://github.com/palkan/rubanok/pull/8). "Planes" are still available if you prefer them (just like me 😉).
290
+
249
291
  ## Contributing
250
292
 
251
293
  Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/rubanok.
@@ -1,6 +1,5 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem "actionpack", "~> 4.2"
4
- gem "actionview", "~> 4.2"
3
+ gem "rails", "~> 4.2"
5
4
 
6
5
  gemspec path: ".."
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rubanok/version"
4
- require "rubanok/plane"
4
+ require "rubanok/processor"
5
5
 
6
6
  require "rubanok/railtie" if defined?(Rails)
7
7
 
@@ -13,15 +13,15 @@ end
13
13
  #
14
14
  # Example:
15
15
  #
16
- # class CourseSessionPlane < Rubanok::Plane
16
+ # class CourseSessionProcessor < Rubanok::Processor
17
17
  # map :q do |q:|
18
- # raw.searh(q)
18
+ # raw.searh(q)
19
19
  # end
20
20
  # end
21
21
  #
22
22
  # class CourseSessionController < ApplicationController
23
23
  # def index
24
- # @sessions = planish(CourseSession.all)
24
+ # @sessions = rubanok_process(CourseSession.all)
25
25
  # end
26
26
  # end
27
27
  module Rubanok
@@ -30,7 +30,10 @@ module Rubanok
30
30
  # When the value is empty and ignored the corresponding matcher/mapper
31
31
  # is not activated (true by default)
32
32
  attr_accessor :ignore_empty_values
33
+ # Define wheter to fail when `match` rule cannot find matching value
34
+ attr_accessor :fail_when_no_matches
33
35
  end
34
36
 
35
37
  self.ignore_empty_values = true
38
+ self.fail_when_no_matches = false
36
39
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rubanok
4
4
  module DSL
5
- # Adds `.map` method to Plane to define key-matching rules:
5
+ # Adds `.map` method to Processor to define key-matching rules:
6
6
  #
7
7
  # map :q do |q:|
8
8
  # # this rule is activated iff "q" (or :q) param is present
@@ -20,12 +20,18 @@ module Rubanok
20
20
  end
21
21
  end
22
22
 
23
- def map(*fields, **options, &block)
24
- rule = Rule.new(fields, options)
23
+ module ClassMethods
24
+ def map(*fields, **options, &block)
25
+ rule = Rule.new(fields, options)
25
26
 
26
- define_method(rule.to_method_name, &block)
27
+ define_method(rule.to_method_name, &block)
27
28
 
28
- rules << rule
29
+ add_rule rule
30
+ end
31
+ end
32
+
33
+ def self.included(base)
34
+ base.extend ClassMethods
29
35
  end
30
36
  end
31
37
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubanok
4
+ class UnexpectedInputError < StandardError; end
5
+
4
6
  module DSL
5
- # Adds `.match` method to Plane class to define key-value-matching rules:
7
+ # Adds `.match` method to Processor class to define key-value-matching rules:
6
8
  #
7
9
  # match :sort, :sort_by do |sort:, sort_by:|
8
10
  # # this rule is activated iff both "sort" and "sort_by" params are present
@@ -64,23 +66,40 @@ module Rubanok
64
66
  end
65
67
  end
66
68
 
67
- def match(*fields, **options, &block)
68
- rule = Rule.new(fields, options)
69
+ module ClassMethods
70
+ def match(*fields, **options, &block)
71
+ rule = Rule.new(fields, options.slice(:activate_on, :activate_always))
69
72
 
70
- rule.instance_eval(&block)
73
+ rule.instance_eval(&block)
71
74
 
72
- define_method(rule.to_method_name) do |params = {}|
73
- clause = rule.matching_clause(params)
74
- next raw unless clause
75
+ define_method(rule.to_method_name) do |params = {}|
76
+ clause = rule.matching_clause(params)
77
+ next default_match_handler(rule, params, options[:fail_when_no_matches]) unless clause
75
78
 
76
- apply_rule! clause.to_method_name, clause.project(params)
77
- end
79
+ apply_rule! clause.to_method_name, clause.project(params)
80
+ end
81
+
82
+ rule.clauses.each do |clause|
83
+ define_method(clause.to_method_name, &clause.block)
84
+ end
78
85
 
79
- rule.clauses.each do |clause|
80
- define_method(clause.to_method_name, &clause.block)
86
+ add_rule rule
81
87
  end
88
+ end
89
+
90
+ def self.included(base)
91
+ base.extend ClassMethods
92
+ end
93
+
94
+ def default_match_handler(rule, params, fail_when_no_matches)
95
+ fail_when_no_matches = Rubanok.fail_when_no_matches if fail_when_no_matches.nil?
96
+ return raw unless fail_when_no_matches
82
97
 
83
- rules << rule
98
+ raise ::Rubanok::UnexpectedInputError, <<~MSG
99
+ Unexpected input: #{params.slice(*rule.fields)}.
100
+ Available values are:
101
+ #{rule.clauses.map(&:values).join("\n ")}
102
+ MSG
84
103
  end
85
104
  end
86
105
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
4
+
3
5
  require "rubanok/rule"
4
6
 
5
7
  require "rubanok/dsl/mapping"
@@ -9,36 +11,37 @@ require "rubanok/ext/symbolize_keys"
9
11
  using Rubanok::SymbolizeKeys
10
12
 
11
13
  module Rubanok
12
- # Base class for transformers (_planes_)
14
+ # Base class for processors (_planes_)
13
15
  #
14
16
  # Define transformation rules via `map` and `match` methods
15
- # and apply them by calling the plane:
17
+ # and apply them by calling the processor:
16
18
  #
17
- # class MyPlane < Rubanok::Plane
19
+ # class MyTransformer < Rubanok::Processor
18
20
  # map :type do
19
21
  # raw.where(type: type)
20
22
  # end
21
23
  # end
22
24
  #
23
- # MyPlane.call(MyModel.all, {type: "public"})
25
+ # MyTransformer.call(MyModel.all, {type: "public"})
24
26
  #
25
27
  # NOTE: the second argument (`params`) MUST be a Hash. Keys could be either Symbols
26
28
  # or Strings (we automatically transform strings to symbols while matching rules).
27
29
  #
28
30
  # All transformation methods are called within the context of the instance of
29
- # a plane class.
31
+ # a processor class.
30
32
  #
31
33
  # You can access the input data via `raw` method.
32
- class Plane
33
- class << self
34
- include DSL::Mapping
35
- include DSL::Matching
34
+ class Processor
35
+ include DSL::Matching
36
+ include DSL::Mapping
36
37
 
38
+ class << self
37
39
  def call(input, params)
38
40
  new(input).call(params)
39
41
  end
40
42
 
41
43
  def add_rule(rule)
44
+ fields_set.merge rule.fields
42
45
  rules << rule
43
46
  end
44
47
 
@@ -46,12 +49,30 @@ module Rubanok
46
49
  return @rules if instance_variable_defined?(:@rules)
47
50
 
48
51
  @rules =
49
- if superclass <= Plane
52
+ if superclass <= Processor
50
53
  superclass.rules.dup
51
54
  else
52
55
  []
53
56
  end
54
57
  end
58
+
59
+ def fields_set
60
+ return @fields_set if instance_variable_defined?(:@fields_set)
61
+
62
+ @fields_set =
63
+ if superclass <= Processor
64
+ superclass.fields_set.dup
65
+ else
66
+ Set.new
67
+ end
68
+ end
69
+
70
+ # Generates a `params` projection including only the keys used
71
+ # by the rules
72
+ def project(params)
73
+ params = params.symbolize_keys
74
+ params.slice(*fields_set)
75
+ end
55
76
  end
56
77
 
57
78
  def initialize(input)
@@ -89,4 +110,6 @@ module Rubanok
89
110
  self.class.rules
90
111
  end
91
112
  end
113
+
114
+ Plane = Processor
92
115
  end
@@ -4,33 +4,76 @@ require "active_support/concern"
4
4
 
5
5
  module Rubanok
6
6
  # Controller concern.
7
- # Adds `planish` method.
7
+ # Adds `rubanok_process` method.
8
8
  module Controller
9
9
  extend ActiveSupport::Concern
10
10
 
11
- # Planish passed data (e.g. ActiveRecord relation) using
12
- # the corrsponding Plane class.
11
+ included do
12
+ helper_method :rubanok_scope
13
+ helper_method :planish_scope
14
+ end
15
+
16
+ # This method process passed data (e.g. ActiveRecord relation) using
17
+ # the corresponding Processor class.
18
+ #
19
+ # Processor is inferred from controller name, e.g.
20
+ # "PostsController -> PostProcessor".
21
+ #
22
+ # You can specify the Processor class explicitly via `with` option.
23
+ #
24
+ # By default, `params` object is passed as parameters, but you
25
+ # can specify the params via `params` option.
26
+ def rubanok_process(data, params = nil, with: nil)
27
+ with_inferred_rubanok_params(with, params) do |rubanok_class, rubanok_params|
28
+ rubanok_class.call(data, rubanok_params)
29
+ end
30
+ end
31
+
32
+ # This method filters the passed params and returns the Hash (!)
33
+ # of the params recongnized by the processor.
13
34
  #
14
- # Plane is inferred from controller name, e.g.
15
- # "PostsController -> PostPlane".
35
+ # Processor is inferred from controller name, e.g.
36
+ # "PostsController -> PostProcessor".
16
37
  #
17
- # You can specify the Plane class explicitly via `with` option.
38
+ # You can specify the Processor class explicitly via `with` option.
18
39
  #
19
- # By default, `params` object is passed as paraters, but you
40
+ # By default, `params` object is passed as parameters, but you
20
41
  # can specify the params via `params` option.
21
- def planish(data, plane_params = nil, with: implicit_plane_class)
42
+ def rubanok_scope(params = nil, with: nil)
43
+ with_inferred_rubanok_params(with, params) do |rubanok_class, rubanok_params|
44
+ rubanok_class.project(rubanok_params)
45
+ end
46
+ end
47
+
48
+ # Tries to infer the rubanok processor class from controller path
49
+ def implicit_rubanok_class
50
+ "#{controller_path.classify.pluralize}Processor".safe_constantize
51
+ end
52
+
53
+ def with_inferred_rubanok_params(with, params)
54
+ with ||= implicit_rubanok_class
55
+
22
56
  if with.nil?
23
- raise ArgumentError, "Failed to find a plane class for #{self.class.name}. " \
24
- "Please, specify the plane class explicitly via `with` option"
57
+ raise ArgumentError, "Failed to find a processor class for #{self.class.name}. " \
58
+ "Please, specify the processor class explicitly via `with` option"
25
59
  end
26
60
 
27
- plane_params ||= params
61
+ params ||= self.params
62
+
63
+ params = params.to_unsafe_h if params.is_a?(ActionController::Parameters)
64
+
65
+ yield with, params
66
+ end
67
+
68
+ # Backward compatibility
69
+ def planish(*args, with: implicit_plane_class)
70
+ rubanok_process(*args, with: with)
71
+ end
28
72
 
29
- plane_params = plane_params.to_unsafe_h if plane_params.is_a?(ActionController::Parameters)
30
- with.call(data, plane_params)
73
+ def planish_scope(*args, with: implicit_plane_class)
74
+ rubanok_scope(*args, with: with)
31
75
  end
32
76
 
33
- # Tries to infer the plane class from controller path
34
77
  def implicit_plane_class
35
78
  "#{controller_path.classify.pluralize}Plane".safe_constantize
36
79
  end
@@ -3,20 +3,26 @@
3
3
  require "rspec/mocks"
4
4
 
5
5
  module Rubanok
6
- class HavePlanished < RSpec::Matchers::BuiltIn::BaseMatcher
6
+ class HaveProcessed < RSpec::Matchers::BuiltIn::BaseMatcher
7
7
  include RSpec::Mocks::ExampleMethods
8
8
 
9
- attr_reader :data_class, :plane, :matcher
9
+ attr_reader :data_class, :processor, :matcher
10
10
 
11
11
  def initialize(data_class = nil)
12
12
  if data_class
13
13
  @data_class = data_class.is_a?(Module) ? data_class : data_class.class
14
14
  end
15
15
  @matcher = have_received(:call)
16
+ @name = "have_rubanok_processed"
16
17
  end
17
18
 
18
- def with(plane)
19
- @plane = plane
19
+ def as(alias_name)
20
+ @name = alias_name
21
+ self
22
+ end
23
+
24
+ def with(processor)
25
+ @processor = processor
20
26
  self
21
27
  end
22
28
 
@@ -25,32 +31,40 @@ module Rubanok
25
31
  end
26
32
 
27
33
  def matches?(proc)
28
- raise ArgumentError, "have_planished only supports block expectations" unless Proc === proc
34
+ raise ArgumentError, "#{name} only supports block expectations" unless Proc === proc
29
35
 
30
- raise ArgumentError, "Plane class is required. Please, specify it using `.with` modifier" if plane.nil?
36
+ raise ArgumentError, "Processor class is required. Please, specify it using `.with` modifier" if processor.nil?
31
37
 
32
- allow(plane).to receive(:call).and_call_original
38
+ allow(processor).to receive(:call).and_call_original
33
39
  proc.call
34
40
 
35
41
  matcher.with(an_instance_of(data_class), anything) if data_class
36
42
 
37
- matcher.matches?(plane)
43
+ matcher.matches?(processor)
38
44
  end
39
45
 
40
46
  def failure_message
41
- "expected to use #{plane.name}#{data_class ? " for #{data_class.name}" : ""}, but didn't"
47
+ "expected to use #{processor.name}#{data_class ? " for #{data_class.name}" : ""}, but didn't"
42
48
  end
43
49
 
44
50
  def failure_message_when_negated
45
- "expected not to use #{plane.name}#{data_class ? " for #{data_class.name} " : ""}, but have used"
51
+ "expected not to use #{processor.name}#{data_class ? " for #{data_class.name} " : ""}, but have used"
46
52
  end
53
+
54
+ private
55
+
56
+ attr_reader :name
47
57
  end
48
58
  end
49
59
 
50
60
  RSpec.configure do |config|
51
61
  config.include(Module.new do
62
+ def have_rubanok_processed(*args)
63
+ Rubanok::HaveProcessed.new(*args)
64
+ end
65
+
52
66
  def have_planished(*args)
53
- Rubanok::HavePlanished.new(*args)
67
+ Rubanok::HaveProcessed.new(*args).as("have_planished")
54
68
  end
55
69
  end)
56
70
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubanok
4
- VERSION = "0.1.3"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -22,6 +22,14 @@ Gem::Specification.new do |spec|
22
22
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
23
  end
24
24
 
25
+ spec.metadata = {
26
+ "bug_tracker_uri" => "http://github.com/palkan/rubanok/issues",
27
+ "changelog_uri" => "https://github.com/palkan/rubanok/blob/master/CHANGELOG.md",
28
+ "documentation_uri" => "http://github.com/palkan/rubanok",
29
+ "homepage_uri" => "http://github.com/palkan/rubanok",
30
+ "source_code_uri" => "http://github.com/palkan/rubanok"
31
+ }
32
+
25
33
  spec.require_paths = ["lib"]
26
34
 
27
35
  spec.add_development_dependency "actionpack", ">= 4.2"
@@ -31,5 +39,5 @@ Gem::Specification.new do |spec|
31
39
  spec.add_development_dependency "rspec", "~> 3.0"
32
40
  spec.add_development_dependency "rspec-rails"
33
41
  spec.add_development_dependency "rubocop-rspec"
34
- spec.add_development_dependency "standard", "~> 0.0.36"
42
+ spec.add_development_dependency "standard", "~> 0.0.39"
35
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubanok
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-06 00:00:00.000000000 Z
11
+ date: 2019-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 0.0.36
117
+ version: 0.0.39
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 0.0.36
124
+ version: 0.0.39
125
125
  description: Parameters-based transformation DSL
126
126
  email:
127
127
  - dementiev.vm@gmail.com
@@ -149,7 +149,7 @@ files:
149
149
  - lib/rubanok/dsl/mapping.rb
150
150
  - lib/rubanok/dsl/matching.rb
151
151
  - lib/rubanok/ext/symbolize_keys.rb
152
- - lib/rubanok/plane.rb
152
+ - lib/rubanok/processor.rb
153
153
  - lib/rubanok/rails/controller.rb
154
154
  - lib/rubanok/railtie.rb
155
155
  - lib/rubanok/rspec.rb
@@ -159,7 +159,12 @@ files:
159
159
  homepage: https://github.com/palkan/rubanok
160
160
  licenses:
161
161
  - MIT
162
- metadata: {}
162
+ metadata:
163
+ bug_tracker_uri: http://github.com/palkan/rubanok/issues
164
+ changelog_uri: https://github.com/palkan/rubanok/blob/master/CHANGELOG.md
165
+ documentation_uri: http://github.com/palkan/rubanok
166
+ homepage_uri: http://github.com/palkan/rubanok
167
+ source_code_uri: http://github.com/palkan/rubanok
163
168
  post_install_message:
164
169
  rdoc_options: []
165
170
  require_paths:
@@ -175,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
180
  - !ruby/object:Gem::Version
176
181
  version: '0'
177
182
  requirements: []
178
- rubygems_version: 3.0.2
183
+ rubygems_version: 3.0.4
179
184
  signing_key:
180
185
  specification_version: 4
181
186
  summary: Parameters-based transformation DSL