rubanok 0.0.1 → 0.1.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 +4 -4
- data/.rubocop.yml +63 -0
- data/.travis.yml +17 -2
- data/CHANGELOG.md +11 -0
- data/Gemfile +9 -2
- data/Gemfile.local +2 -0
- data/Gemfile.lock +136 -0
- data/README.md +182 -14
- data/Rakefile +5 -1
- data/bin/console +3 -9
- data/gemfiles/rails42.gemfile +6 -0
- data/gemfiles/rails52.gemfile +6 -0
- data/gemfiles/railsmaster.gemfile +5 -0
- data/lib/rubanok.rb +32 -1
- data/lib/rubanok/dsl/mapping.rb +32 -0
- data/lib/rubanok/dsl/matching.rb +87 -0
- data/lib/rubanok/ext/symbolize_keys.rb +13 -0
- data/lib/rubanok/plane.rb +92 -0
- data/lib/rubanok/rails/controller.rb +38 -0
- data/lib/rubanok/railtie.rb +13 -0
- data/lib/rubanok/rspec.rb +56 -0
- data/lib/rubanok/rule.rb +67 -0
- data/lib/rubanok/version.rb +3 -1
- data/rubanok.gemspec +8 -1
- metadata +89 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c22d92e478499cec879d532e120f57d6a07f2655c3f0ba112b613e5e4123c97e
|
|
4
|
+
data.tar.gz: 2010c2271258c7a39cace169a0a6eaf7b90c1c2029392a1d5ec4e9dce49c91be
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1ae97af6ca1cb1e7ab77f0ac84011a6dcb31882415d96ed6cf89f55d74c47898187d653285b130d7e80b8e97e23d8c23e106954efb455f89189346fad05e3b84
|
|
7
|
+
data.tar.gz: 35296df1a1434889966c90bd3c309ac25c5a6719035b8088b4964d5071fc8ad9e509833b4a8851bd576bde727a344c5c43610e423247d639953192bc9f1b068d
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require:
|
|
2
|
+
- standard/cop/semantic_blocks
|
|
3
|
+
- rubocop-rspec
|
|
4
|
+
|
|
5
|
+
inherit_gem:
|
|
6
|
+
standard: config/base.yml
|
|
7
|
+
|
|
8
|
+
AllCops:
|
|
9
|
+
Exclude:
|
|
10
|
+
- 'bin/*'
|
|
11
|
+
- 'tmp/**/*'
|
|
12
|
+
- 'Gemfile'
|
|
13
|
+
- 'vendor/**/*'
|
|
14
|
+
- 'gemfiles/**/*'
|
|
15
|
+
DisplayCopNames: true
|
|
16
|
+
|
|
17
|
+
Standard/SemanticBlocks:
|
|
18
|
+
Enabled: false
|
|
19
|
+
|
|
20
|
+
Style/TrailingCommaInArrayLiteral:
|
|
21
|
+
EnforcedStyleForMultiline: no_comma
|
|
22
|
+
|
|
23
|
+
Style/TrailingCommaInHashLiteral:
|
|
24
|
+
EnforcedStyleForMultiline: no_comma
|
|
25
|
+
|
|
26
|
+
Style/FrozenStringLiteralComment:
|
|
27
|
+
Enabled: true
|
|
28
|
+
|
|
29
|
+
RSpec/Focus:
|
|
30
|
+
Enabled: true
|
|
31
|
+
|
|
32
|
+
RSpec/EmptyExampleGroup:
|
|
33
|
+
Enabled: true
|
|
34
|
+
|
|
35
|
+
RSpec/EmptyLineAfterExampleGroup:
|
|
36
|
+
Enabled: true
|
|
37
|
+
|
|
38
|
+
RSpec/EmptyLineAfterFinalLet:
|
|
39
|
+
Enabled: true
|
|
40
|
+
|
|
41
|
+
RSpec/EmptyLineAfterHook:
|
|
42
|
+
Enabled: true
|
|
43
|
+
|
|
44
|
+
RSpec/EmptyLineAfterSubject:
|
|
45
|
+
Enabled: true
|
|
46
|
+
|
|
47
|
+
RSpec/HooksBeforeExamples:
|
|
48
|
+
Enabled: true
|
|
49
|
+
|
|
50
|
+
RSpec/ImplicitExpect:
|
|
51
|
+
Enabled: true
|
|
52
|
+
|
|
53
|
+
RSpec/IteratedExpectation:
|
|
54
|
+
Enabled: true
|
|
55
|
+
|
|
56
|
+
RSpec/LetBeforeExamples:
|
|
57
|
+
Enabled: true
|
|
58
|
+
|
|
59
|
+
RSpec/MissingExampleGroupArgument:
|
|
60
|
+
Enabled: true
|
|
61
|
+
|
|
62
|
+
RSpec/ReceiveCounts:
|
|
63
|
+
Enabled: true
|
data/.travis.yml
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
sudo: false
|
|
2
2
|
language: ruby
|
|
3
3
|
rvm:
|
|
4
|
-
- 2.
|
|
5
|
-
|
|
4
|
+
- 2.6.0
|
|
5
|
+
|
|
6
|
+
notifications:
|
|
7
|
+
email: false
|
|
8
|
+
|
|
9
|
+
matrix:
|
|
10
|
+
fast_finish: true
|
|
11
|
+
include:
|
|
12
|
+
- rvm: ruby-head
|
|
13
|
+
gemfile: gemfiles/railsmaster.gemfile
|
|
14
|
+
- rvm: 2.6.0
|
|
15
|
+
gemfile: gemfiles/rails52.gemfile
|
|
16
|
+
- rvm: 2.5.1
|
|
17
|
+
gemfile: gemfiles/rails42.gemfile
|
|
18
|
+
allow_failures:
|
|
19
|
+
- rvm: ruby-head
|
|
20
|
+
gemfile: gemfiles/railsmaster.gemfile
|
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
source "https://rubygems.org"
|
|
2
2
|
|
|
3
|
-
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
|
4
|
-
|
|
5
3
|
# Specify your gem's dependencies in rubanok.gemspec
|
|
6
4
|
gemspec
|
|
5
|
+
|
|
6
|
+
gem "pry-byebug", platform: :mri
|
|
7
|
+
gem "simplecov"
|
|
8
|
+
|
|
9
|
+
local_gemfile = 'Gemfile.local'
|
|
10
|
+
|
|
11
|
+
if File.exist?(local_gemfile)
|
|
12
|
+
eval(File.read(local_gemfile)) # rubocop:disable Security/Eval
|
|
13
|
+
end
|
data/Gemfile.local
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
rubanok (0.1.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
actionpack (5.2.2)
|
|
10
|
+
actionview (= 5.2.2)
|
|
11
|
+
activesupport (= 5.2.2)
|
|
12
|
+
rack (~> 2.0)
|
|
13
|
+
rack-test (>= 0.6.3)
|
|
14
|
+
rails-dom-testing (~> 2.0)
|
|
15
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
16
|
+
actionview (5.2.2)
|
|
17
|
+
activesupport (= 5.2.2)
|
|
18
|
+
builder (~> 3.1)
|
|
19
|
+
erubi (~> 1.4)
|
|
20
|
+
rails-dom-testing (~> 2.0)
|
|
21
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
22
|
+
activesupport (5.2.2)
|
|
23
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
24
|
+
i18n (>= 0.7, < 2)
|
|
25
|
+
minitest (~> 5.1)
|
|
26
|
+
tzinfo (~> 1.1)
|
|
27
|
+
ast (2.4.0)
|
|
28
|
+
builder (3.2.3)
|
|
29
|
+
byebug (10.0.2)
|
|
30
|
+
coderay (1.1.2)
|
|
31
|
+
concurrent-ruby (1.1.4)
|
|
32
|
+
crass (1.0.4)
|
|
33
|
+
diff-lcs (1.3)
|
|
34
|
+
docile (1.3.1)
|
|
35
|
+
erubi (1.8.0)
|
|
36
|
+
i18n (1.4.0)
|
|
37
|
+
concurrent-ruby (~> 1.0)
|
|
38
|
+
jaro_winkler (1.5.2)
|
|
39
|
+
json (2.1.0)
|
|
40
|
+
loofah (2.2.3)
|
|
41
|
+
crass (~> 1.0.2)
|
|
42
|
+
nokogiri (>= 1.5.9)
|
|
43
|
+
method_source (0.9.2)
|
|
44
|
+
mini_portile2 (2.4.0)
|
|
45
|
+
minitest (5.11.3)
|
|
46
|
+
nokogiri (1.10.0)
|
|
47
|
+
mini_portile2 (~> 2.4.0)
|
|
48
|
+
parallel (1.12.1)
|
|
49
|
+
parser (2.5.3.0)
|
|
50
|
+
ast (~> 2.4.0)
|
|
51
|
+
powerpack (0.1.2)
|
|
52
|
+
pry (0.12.2)
|
|
53
|
+
coderay (~> 1.1.0)
|
|
54
|
+
method_source (~> 0.9.0)
|
|
55
|
+
pry-byebug (3.6.0)
|
|
56
|
+
byebug (~> 10.0)
|
|
57
|
+
pry (~> 0.10)
|
|
58
|
+
rack (2.0.6)
|
|
59
|
+
rack-test (1.1.0)
|
|
60
|
+
rack (>= 1.0, < 3)
|
|
61
|
+
rails-dom-testing (2.0.3)
|
|
62
|
+
activesupport (>= 4.2.0)
|
|
63
|
+
nokogiri (>= 1.6)
|
|
64
|
+
rails-html-sanitizer (1.0.4)
|
|
65
|
+
loofah (~> 2.2, >= 2.2.2)
|
|
66
|
+
railties (5.2.2)
|
|
67
|
+
actionpack (= 5.2.2)
|
|
68
|
+
activesupport (= 5.2.2)
|
|
69
|
+
method_source
|
|
70
|
+
rake (>= 0.8.7)
|
|
71
|
+
thor (>= 0.19.0, < 2.0)
|
|
72
|
+
rainbow (3.0.0)
|
|
73
|
+
rake (10.5.0)
|
|
74
|
+
rspec (3.8.0)
|
|
75
|
+
rspec-core (~> 3.8.0)
|
|
76
|
+
rspec-expectations (~> 3.8.0)
|
|
77
|
+
rspec-mocks (~> 3.8.0)
|
|
78
|
+
rspec-core (3.8.0)
|
|
79
|
+
rspec-support (~> 3.8.0)
|
|
80
|
+
rspec-expectations (3.8.2)
|
|
81
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
82
|
+
rspec-support (~> 3.8.0)
|
|
83
|
+
rspec-mocks (3.8.0)
|
|
84
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
85
|
+
rspec-support (~> 3.8.0)
|
|
86
|
+
rspec-rails (3.8.1)
|
|
87
|
+
actionpack (>= 3.0)
|
|
88
|
+
activesupport (>= 3.0)
|
|
89
|
+
railties (>= 3.0)
|
|
90
|
+
rspec-core (~> 3.8.0)
|
|
91
|
+
rspec-expectations (~> 3.8.0)
|
|
92
|
+
rspec-mocks (~> 3.8.0)
|
|
93
|
+
rspec-support (~> 3.8.0)
|
|
94
|
+
rspec-support (3.8.0)
|
|
95
|
+
rubocop (0.62.0)
|
|
96
|
+
jaro_winkler (~> 1.5.1)
|
|
97
|
+
parallel (~> 1.10)
|
|
98
|
+
parser (>= 2.5, != 2.5.1.1)
|
|
99
|
+
powerpack (~> 0.1)
|
|
100
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
101
|
+
ruby-progressbar (~> 1.7)
|
|
102
|
+
unicode-display_width (~> 1.4.0)
|
|
103
|
+
rubocop-rspec (1.31.0)
|
|
104
|
+
rubocop (>= 0.60.0)
|
|
105
|
+
ruby-progressbar (1.10.0)
|
|
106
|
+
simplecov (0.16.1)
|
|
107
|
+
docile (~> 1.1)
|
|
108
|
+
json (>= 1.8, < 3)
|
|
109
|
+
simplecov-html (~> 0.10.0)
|
|
110
|
+
simplecov-html (0.10.2)
|
|
111
|
+
standard (0.0.24)
|
|
112
|
+
rubocop (>= 0.60)
|
|
113
|
+
thor (0.20.3)
|
|
114
|
+
thread_safe (0.3.6)
|
|
115
|
+
tzinfo (1.2.5)
|
|
116
|
+
thread_safe (~> 0.1)
|
|
117
|
+
unicode-display_width (1.4.1)
|
|
118
|
+
|
|
119
|
+
PLATFORMS
|
|
120
|
+
ruby
|
|
121
|
+
|
|
122
|
+
DEPENDENCIES
|
|
123
|
+
actionpack (~> 5.2)
|
|
124
|
+
actionview (~> 5.2)
|
|
125
|
+
bundler (~> 1.16)
|
|
126
|
+
pry-byebug
|
|
127
|
+
rake (~> 10.0)
|
|
128
|
+
rspec (~> 3.0)
|
|
129
|
+
rspec-rails
|
|
130
|
+
rubanok!
|
|
131
|
+
rubocop-rspec
|
|
132
|
+
simplecov
|
|
133
|
+
standard
|
|
134
|
+
|
|
135
|
+
BUNDLED WITH
|
|
136
|
+
1.17.2
|
data/README.md
CHANGED
|
@@ -1,38 +1,206 @@
|
|
|
1
|
+
[](https://rubygems.org/gems/rubanok) [](https://travis-ci.org/palkan/rubanok)
|
|
2
|
+
|
|
1
3
|
# Rubanok
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
Rubanok provides a DSL to build parameters-based data transformers.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
The typical usage is to describe all the possible collection manipulation for REST `index` action, e.g. filtering, sorting, searching, pagination, etc..
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
So, instead of:
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
```ruby
|
|
12
|
+
class CourseSessionController < ApplicationController
|
|
13
|
+
def index
|
|
14
|
+
@sessions = CourseSession.
|
|
15
|
+
search(params[:q]).
|
|
16
|
+
by_course_type(params[:course_type_id]).
|
|
17
|
+
by_role(params[:role_id]).
|
|
18
|
+
paginate(page_params).
|
|
19
|
+
order(ordering_params)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
You have:
|
|
10
25
|
|
|
11
26
|
```ruby
|
|
12
|
-
|
|
27
|
+
class CourseSessionController < ApplicationController
|
|
28
|
+
def index
|
|
29
|
+
@sessions = planish(
|
|
30
|
+
# pass input
|
|
31
|
+
CourseSession.all,
|
|
32
|
+
# pass params
|
|
33
|
+
params,
|
|
34
|
+
# provide a plane to use
|
|
35
|
+
with: CourseSessionsPlane
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
13
39
|
```
|
|
14
40
|
|
|
15
|
-
|
|
41
|
+
Or we can try to infer all the configuration for you:
|
|
16
42
|
|
|
17
|
-
$ bundle
|
|
18
43
|
|
|
19
|
-
|
|
44
|
+
```ruby
|
|
45
|
+
class CourseSessionController < ApplicationController
|
|
46
|
+
def index
|
|
47
|
+
@sessions = planish(CourseSession.all)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
```
|
|
20
51
|
|
|
21
|
-
|
|
52
|
+
Requirements:
|
|
53
|
+
- Ruby ~> 2.5
|
|
54
|
+
- Rails >= 4.2 (only for using with Rails)
|
|
55
|
+
|
|
56
|
+
<a href="https://evilmartians.com/">
|
|
57
|
+
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
**This gem hasn't been released (and even built) yet.**
|
|
22
62
|
|
|
23
63
|
## Usage
|
|
24
64
|
|
|
25
|
-
|
|
65
|
+
The core concept of this library is a _plane_ (or _hand plane_, or "рубанок" in Russian). Plane is responsible for mapping parameters to transformrations.
|
|
66
|
+
|
|
67
|
+
From the example above:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
class CourseSessionsPlane < Rubanok::Plane
|
|
71
|
+
# You can map keys
|
|
72
|
+
map :q do |q:|
|
|
73
|
+
# `raw` is an accessor for input data
|
|
74
|
+
raw.searh(q)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# The following code
|
|
79
|
+
CourseSessionsPlane.call(CourseSession.all, q: "xyz")
|
|
80
|
+
|
|
81
|
+
# is equal to
|
|
82
|
+
CourseSession.all.search("xyz")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
You can map multiple keys at once:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
class CourseSessionsPlane < Rubanok::Plane
|
|
89
|
+
DEFAULT_PAGE_SIZE = 25
|
|
90
|
+
|
|
91
|
+
map :page, :per_page do |page:, per_page: DEFAULT_PAGE_SIZE|
|
|
92
|
+
raw.paginate(page: page, per_page: per_page)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
There is also `match` method to handle values:
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
class CourseSessionsPlane < Rubanok::Plane
|
|
101
|
+
SORT_ORDERS = %w(asc desc).freeze
|
|
102
|
+
SORTABLE_FIELDS = %w(id name created_at).freeze
|
|
103
|
+
|
|
104
|
+
match :sort_by, :sort do
|
|
105
|
+
having "course_id", "desc" do
|
|
106
|
+
raw.joins(:courses).order("courses.id desc nulls last")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
having "course_id", "asc" do
|
|
110
|
+
raw.joins(:courses).order("courses.id asc nulls first")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Match any value for the second arg
|
|
114
|
+
having "type" do |sort: "asc"|
|
|
115
|
+
# Prevent SQL injections
|
|
116
|
+
raise "Possible injection: #{sort}" unless SORT_ORDERS.include?(sort)
|
|
117
|
+
raw.joins(:course_type).order("course_types.name #{sort}")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Match any value
|
|
121
|
+
default do |sort_by:, sort: "asc"|
|
|
122
|
+
raise "Possible injection: #{sort}" unless SORT_ORDERS.include?(sort)
|
|
123
|
+
raise "The field is not sortable: #{sort_by}" unless SORTABLE_FIELDS.include?(sort_by)
|
|
124
|
+
raw.order(sort_by => sort)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**NOTE:** matching only match the exact values; more complex matching could be added in the future.
|
|
131
|
+
|
|
132
|
+
### Rule activation
|
|
133
|
+
|
|
134
|
+
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.
|
|
135
|
+
|
|
136
|
+
Sometimes you might want to make some fields optional (or event all of them). You can use `activate_on` and `activate_always` options for that:
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
# Always apply the rule; use default values for keyword args
|
|
140
|
+
map :page, :per_page, activate_always: true do |page: 1, per_page: 2|
|
|
141
|
+
raw.page(page).per(per_page)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Only require `sort_by` to be preset to activate sorting rule
|
|
145
|
+
match :sort_by, :sort, activate_on: :sort_by do
|
|
146
|
+
# ...
|
|
147
|
+
end
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
By default, Rubanok ignores empty param values (using `#empty?` under the hood) and do not activate the matching rules (i.e. `{ q: "" }` or `{ q: nil }` won't activate the `map :q` rule).
|
|
151
|
+
|
|
152
|
+
You can change this behaviour by setting: `Rubanok.ignore_empty_values = false`.
|
|
153
|
+
|
|
154
|
+
### Testing
|
|
155
|
+
|
|
156
|
+
One of the benefits of having all the modification logic in its own class is the ability to test it in isolation:
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
# For example, with RSpec
|
|
160
|
+
describe CourseSessionsPlane do
|
|
161
|
+
let(:input ) { CourseSession.all }
|
|
162
|
+
let(:params) { {} }
|
|
163
|
+
|
|
164
|
+
subject { described_class.call(input, params) }
|
|
165
|
+
|
|
166
|
+
specify "searching" do
|
|
167
|
+
params[:q] = "wood"
|
|
168
|
+
|
|
169
|
+
expect(subject).to eq input.search("wood")
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Now in your controller you only have to test that the specific _plane_ is applied:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
describe CourseSessionController do
|
|
178
|
+
subject { get :index }
|
|
179
|
+
|
|
180
|
+
specify do
|
|
181
|
+
expect { subject }.to have_planished(CourseSession.all).
|
|
182
|
+
with(CourseSessionsPlane)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**NOTE**: input matching only checks for the class equality.
|
|
188
|
+
|
|
189
|
+
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"`):
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
require "rubanok/rspec"
|
|
193
|
+
```
|
|
26
194
|
|
|
27
|
-
|
|
195
|
+
### Rails vs. non-Rails
|
|
28
196
|
|
|
29
|
-
|
|
197
|
+
Rubanok is a Rails-free library but has some useful Rails extensions, such as `planish` helper for controllers (included automatically into `ActionController::Base` and `ActionController::API`).
|
|
30
198
|
|
|
31
|
-
|
|
199
|
+
If you use `ActionController::Metal` you must include the `Rubanok::Controller` module yourself.
|
|
32
200
|
|
|
33
201
|
## Contributing
|
|
34
202
|
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
203
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/rubanok.
|
|
36
204
|
|
|
37
205
|
## License
|
|
38
206
|
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "bundler/setup"
|
|
4
4
|
require "rubanok"
|
|
5
|
+
require "pry"
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
-
# require "pry"
|
|
11
|
-
# Pry.start
|
|
12
|
-
|
|
13
|
-
require "irb"
|
|
14
|
-
IRB.start(__FILE__)
|
|
7
|
+
require "pry"
|
|
8
|
+
Pry.start
|
data/lib/rubanok.rb
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "rubanok/version"
|
|
4
|
+
require "rubanok/plane"
|
|
5
|
+
|
|
6
|
+
require "rubanok/railtie" if defined?(Rails)
|
|
2
7
|
|
|
8
|
+
if defined?(RSpec) && (ENV["RACK_ENV"] == "test" || ENV["RAILS_ENV"] == "test")
|
|
9
|
+
require "rubanok/rspec"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Rubanok provides a DSL to build parameters-based data transformers.
|
|
13
|
+
#
|
|
14
|
+
# Example:
|
|
15
|
+
#
|
|
16
|
+
# class CourseSessionPlane < Rubanok::Plane
|
|
17
|
+
# map :q do |q:|
|
|
18
|
+
# raw.searh(q)
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# class CourseSessionController < ApplicationController
|
|
23
|
+
# def index
|
|
24
|
+
# @sessions = planish(CourseSession.all)
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
3
27
|
module Rubanok
|
|
4
|
-
|
|
28
|
+
class << self
|
|
29
|
+
# Define whether to ignore empty values in params or not.
|
|
30
|
+
# When the value is empty and ignored the corresponding matcher/mapper
|
|
31
|
+
# is not activated (true by default)
|
|
32
|
+
attr_accessor :ignore_empty_values
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
self.ignore_empty_values = true
|
|
5
36
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubanok
|
|
4
|
+
module DSL
|
|
5
|
+
# Adds `.map` method to Plane to define key-matching rules:
|
|
6
|
+
#
|
|
7
|
+
# map :q do |q:|
|
|
8
|
+
# # this rule is activated iff "q" (or :q) param is present
|
|
9
|
+
# # the value is passed to the handler
|
|
10
|
+
# end
|
|
11
|
+
module Mapping
|
|
12
|
+
class Rule < Rubanok::Rule
|
|
13
|
+
METHOD_PREFIX = "__map"
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# prefix rule method name to avoid collisions
|
|
18
|
+
def build_method_name
|
|
19
|
+
"#{METHOD_PREFIX}#{super}"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def map(*fields, **options, &block)
|
|
24
|
+
rule = Rule.new(fields, options)
|
|
25
|
+
|
|
26
|
+
define_method(rule.to_method_name, &block)
|
|
27
|
+
|
|
28
|
+
rules << rule
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubanok
|
|
4
|
+
module DSL
|
|
5
|
+
# Adds `.match` method to Plane class to define key-value-matching rules:
|
|
6
|
+
#
|
|
7
|
+
# match :sort, :sort_by do |sort:, sort_by:|
|
|
8
|
+
# # this rule is activated iff both "sort" and "sort_by" params are present
|
|
9
|
+
# # the values are passed to the matcher
|
|
10
|
+
# #
|
|
11
|
+
# # then we match against values
|
|
12
|
+
# having "name" do |sort_by:|
|
|
13
|
+
# raw.joins(:user).order("users.name #{sort_by}")
|
|
14
|
+
# end
|
|
15
|
+
# end
|
|
16
|
+
module Matching
|
|
17
|
+
class Rule < Rubanok::Rule
|
|
18
|
+
METHOD_PREFIX = "__match"
|
|
19
|
+
|
|
20
|
+
class Clause < Rubanok::Rule
|
|
21
|
+
attr_reader :values, :id, :block
|
|
22
|
+
|
|
23
|
+
def initialize(id, fields, values = [], **options, &block)
|
|
24
|
+
super(fields, options)
|
|
25
|
+
@id = id
|
|
26
|
+
@block = block
|
|
27
|
+
@values = Hash[fields.take(values.size).zip(values)].freeze
|
|
28
|
+
@fields = (fields - @values.keys).freeze
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def applicable?(params)
|
|
32
|
+
values.all? { |key, matcher| params.key?(key) && (matcher == params[key]) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
alias to_method_name id
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
attr_reader :clauses
|
|
39
|
+
|
|
40
|
+
def initialize(*)
|
|
41
|
+
super
|
|
42
|
+
@clauses = []
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def matching_clause(params)
|
|
46
|
+
clauses.detect do |clause|
|
|
47
|
+
clause.applicable?(params)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def having(*values, &block)
|
|
52
|
+
clauses << Clause.new("#{to_method_name}_#{clauses.size}", fields, values, &block)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def default(&block)
|
|
56
|
+
clauses << Clause.new("#{to_method_name}_default", fields, activate_always: true, &block)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# prefix rule method name to avoid collisions
|
|
62
|
+
def build_method_name
|
|
63
|
+
"#{METHOD_PREFIX}#{super}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def match(*fields, **options, &block)
|
|
68
|
+
rule = Rule.new(fields, options)
|
|
69
|
+
|
|
70
|
+
rule.instance_eval(&block)
|
|
71
|
+
|
|
72
|
+
define_method(rule.to_method_name) do |params|
|
|
73
|
+
clause = rule.matching_clause(params)
|
|
74
|
+
next raw unless clause
|
|
75
|
+
|
|
76
|
+
apply_rule! clause.to_method_name, clause.project(params)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
rule.clauses.each do |clause|
|
|
80
|
+
define_method(clause.to_method_name, &clause.block)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
rules << rule
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rubanok/rule"
|
|
4
|
+
|
|
5
|
+
require "rubanok/dsl/mapping"
|
|
6
|
+
require "rubanok/dsl/matching"
|
|
7
|
+
|
|
8
|
+
require "rubanok/ext/symbolize_keys"
|
|
9
|
+
using Rubanok::SymbolizeKeys
|
|
10
|
+
|
|
11
|
+
module Rubanok
|
|
12
|
+
# Base class for transformers (_planes_)
|
|
13
|
+
#
|
|
14
|
+
# Define transformation rules via `map` and `match` methods
|
|
15
|
+
# and apply them by calling the plane:
|
|
16
|
+
#
|
|
17
|
+
# class MyPlane < Rubanok::Plane
|
|
18
|
+
# map :type do
|
|
19
|
+
# raw.where(type: type)
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# MyPlane.call(MyModel.all, {type: "public"})
|
|
24
|
+
#
|
|
25
|
+
# NOTE: the second argument (`params`) MUST be a Hash. Keys could be either Symbols
|
|
26
|
+
# or Strings (we automatically transform strings to symbols while matching rules).
|
|
27
|
+
#
|
|
28
|
+
# All transformation methods are called within the context of the instance of
|
|
29
|
+
# a plane class.
|
|
30
|
+
#
|
|
31
|
+
# You can access the input data via `raw` method.
|
|
32
|
+
class Plane
|
|
33
|
+
class << self
|
|
34
|
+
include DSL::Mapping
|
|
35
|
+
include DSL::Matching
|
|
36
|
+
|
|
37
|
+
def call(input, params)
|
|
38
|
+
new(input).call(params)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def add_rule(rule)
|
|
42
|
+
rules << rule
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def rules
|
|
46
|
+
return @rules if instance_variable_defined?(:@rules)
|
|
47
|
+
|
|
48
|
+
@rules =
|
|
49
|
+
if superclass <= Plane
|
|
50
|
+
superclass.rules.dup
|
|
51
|
+
else
|
|
52
|
+
[]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def initialize(input)
|
|
58
|
+
@input = input
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def call(params)
|
|
62
|
+
params = params.symbolize_keys
|
|
63
|
+
|
|
64
|
+
rules.each do |rule|
|
|
65
|
+
next unless rule.applicable?(params)
|
|
66
|
+
|
|
67
|
+
apply_rule! rule.to_method_name, rule.project(params)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
input
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
attr_accessor :input
|
|
76
|
+
|
|
77
|
+
alias raw input
|
|
78
|
+
|
|
79
|
+
def apply_rule!(method_name, data)
|
|
80
|
+
self.input =
|
|
81
|
+
if data.empty?
|
|
82
|
+
send(method_name)
|
|
83
|
+
else
|
|
84
|
+
send(method_name, **data)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def rules
|
|
89
|
+
self.class.rules
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module Rubanok
|
|
6
|
+
# Controller concern.
|
|
7
|
+
# Adds `planish` method.
|
|
8
|
+
module Controller
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
# Planish passed data (e.g. ActiveRecord relation) using
|
|
12
|
+
# the corrsponding Plane class.
|
|
13
|
+
#
|
|
14
|
+
# Plane is inferred from controller name, e.g.
|
|
15
|
+
# "PostsController -> PostPlane".
|
|
16
|
+
#
|
|
17
|
+
# You can specify the Plane class explicitly via `with` option.
|
|
18
|
+
#
|
|
19
|
+
# By default, `params` object is passed as paraters, but you
|
|
20
|
+
# can specify the params via `params` option.
|
|
21
|
+
def planish(data, plane_params = nil, with: implicit_plane_class)
|
|
22
|
+
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"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
plane_params ||= params
|
|
28
|
+
|
|
29
|
+
plane_params = plane_params.to_unsafe_h if plane_params.is_a?(ActionController::Parameters)
|
|
30
|
+
with.call(data, plane_params)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Tries to infer the plane class from controller path
|
|
34
|
+
def implicit_plane_class
|
|
35
|
+
"#{controller_path.classify.pluralize}Plane".safe_constantize
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubanok # :nodoc:
|
|
4
|
+
class Railtie < ::Rails::Railtie # :nodoc:
|
|
5
|
+
config.to_prepare do |_app|
|
|
6
|
+
ActiveSupport.on_load(:action_controller) do
|
|
7
|
+
require "rubanok/rails/controller"
|
|
8
|
+
|
|
9
|
+
ActionController::Base.include Rubanok::Controller
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rspec/mocks"
|
|
4
|
+
|
|
5
|
+
module Rubanok
|
|
6
|
+
class HavePlanished < RSpec::Matchers::BuiltIn::BaseMatcher
|
|
7
|
+
include RSpec::Mocks::ExampleMethods
|
|
8
|
+
|
|
9
|
+
attr_reader :data_class, :plane, :matcher
|
|
10
|
+
|
|
11
|
+
def initialize(data_class = nil)
|
|
12
|
+
if data_class
|
|
13
|
+
@data_class = data_class.is_a?(Module) ? data_class : data_class.class
|
|
14
|
+
end
|
|
15
|
+
@matcher = have_received(:call)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def with(plane)
|
|
19
|
+
@plane = plane
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def supports_block_expectations?
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def matches?(proc)
|
|
28
|
+
raise ArgumentError, "have_planished only supports block expectations" unless Proc === proc
|
|
29
|
+
|
|
30
|
+
raise ArgumentError, "Plane class is required. Please, specify it using `.with` modifier" if plane.nil?
|
|
31
|
+
|
|
32
|
+
allow(plane).to receive(:call)
|
|
33
|
+
proc.call
|
|
34
|
+
|
|
35
|
+
matcher.with(an_instance_of(data_class), anything) if data_class
|
|
36
|
+
|
|
37
|
+
matcher.matches?(plane)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def failure_message
|
|
41
|
+
"expected to use #{plane.name}#{data_class ? " for #{data_class.name}" : ""}, but didn't"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def failure_message_when_negated
|
|
45
|
+
"expected not to use #{plane.name}#{data_class ? " for #{data_class.name} " : ""}, but have used"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
RSpec.configure do |config|
|
|
51
|
+
config.include(Module.new do
|
|
52
|
+
def have_planished(*args)
|
|
53
|
+
Rubanok::HavePlanished.new(*args)
|
|
54
|
+
end
|
|
55
|
+
end)
|
|
56
|
+
end
|
data/lib/rubanok/rule.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubanok
|
|
4
|
+
class Rule # :nodoc:
|
|
5
|
+
UNDEFINED = :__undef__
|
|
6
|
+
|
|
7
|
+
attr_reader :fields, :activate_on, :activate_always
|
|
8
|
+
|
|
9
|
+
def initialize(fields, activate_on: fields, activate_always: false)
|
|
10
|
+
@fields = fields.freeze
|
|
11
|
+
@activate_on = Array(activate_on).freeze
|
|
12
|
+
@activate_always = activate_always
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def project(params)
|
|
16
|
+
fields.each_with_object({}) do |field, acc|
|
|
17
|
+
val = fetch_value params, field
|
|
18
|
+
next acc if val == UNDEFINED
|
|
19
|
+
|
|
20
|
+
acc[field] = val
|
|
21
|
+
acc
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def applicable?(params)
|
|
26
|
+
return true if activate_always == true
|
|
27
|
+
|
|
28
|
+
activate_on.all? { |field| params.key?(field) && !empty?(params[field]) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_method_name
|
|
32
|
+
@method_name ||= build_method_name
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def build_method_name
|
|
38
|
+
"__#{fields.join("_")}__"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def fetch_value(params, field)
|
|
42
|
+
return UNDEFINED if !params.key?(field) || empty?(params[field])
|
|
43
|
+
|
|
44
|
+
params[field]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
using(Module.new do
|
|
48
|
+
refine NilClass do
|
|
49
|
+
def empty?
|
|
50
|
+
true
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
refine Object do
|
|
55
|
+
def empty?
|
|
56
|
+
false
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end)
|
|
60
|
+
|
|
61
|
+
def empty?(val)
|
|
62
|
+
return false unless Rubanok.ignore_empty_values
|
|
63
|
+
|
|
64
|
+
val.empty?
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/rubanok/version.rb
CHANGED
data/rubanok.gemspec
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
3
|
lib = File.expand_path("../lib", __FILE__)
|
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
@@ -13,16 +14,22 @@ Gem::Specification.new do |spec|
|
|
|
13
14
|
spec.description = "Parameters-based transformation DSL"
|
|
14
15
|
spec.homepage = "https://github.com/palkan/rubanok"
|
|
15
16
|
spec.license = "MIT"
|
|
17
|
+
spec.required_ruby_version = ">= 2.5.0"
|
|
16
18
|
|
|
17
19
|
# Specify which files should be added to the gem when it is released.
|
|
18
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
19
|
-
spec.files
|
|
21
|
+
spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
|
|
20
22
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
spec.require_paths = ["lib"]
|
|
24
26
|
|
|
27
|
+
spec.add_development_dependency "actionpack", ">= 4.2"
|
|
28
|
+
spec.add_development_dependency "actionview", ">= 4.2"
|
|
25
29
|
spec.add_development_dependency "bundler", "~> 1.16"
|
|
26
30
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
27
31
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
32
|
+
spec.add_development_dependency "rspec-rails"
|
|
33
|
+
spec.add_development_dependency "rubocop-rspec"
|
|
34
|
+
spec.add_development_dependency "standard"
|
|
28
35
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubanok
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.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:
|
|
11
|
+
date: 2019-01-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: actionpack
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '4.2'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '4.2'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: actionview
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '4.2'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '4.2'
|
|
13
41
|
- !ruby/object:Gem::Dependency
|
|
14
42
|
name: bundler
|
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,6 +80,48 @@ dependencies:
|
|
|
52
80
|
- - "~>"
|
|
53
81
|
- !ruby/object:Gem::Version
|
|
54
82
|
version: '3.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rspec-rails
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rubocop-rspec
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: standard
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
55
125
|
description: Parameters-based transformation DSL
|
|
56
126
|
email:
|
|
57
127
|
- dementiev.vm@gmail.com
|
|
@@ -61,14 +131,29 @@ extra_rdoc_files: []
|
|
|
61
131
|
files:
|
|
62
132
|
- ".gitignore"
|
|
63
133
|
- ".rspec"
|
|
134
|
+
- ".rubocop.yml"
|
|
64
135
|
- ".travis.yml"
|
|
136
|
+
- CHANGELOG.md
|
|
65
137
|
- Gemfile
|
|
138
|
+
- Gemfile.local
|
|
139
|
+
- Gemfile.lock
|
|
66
140
|
- LICENSE.txt
|
|
67
141
|
- README.md
|
|
68
142
|
- Rakefile
|
|
69
143
|
- bin/console
|
|
70
144
|
- bin/setup
|
|
145
|
+
- gemfiles/rails42.gemfile
|
|
146
|
+
- gemfiles/rails52.gemfile
|
|
147
|
+
- gemfiles/railsmaster.gemfile
|
|
71
148
|
- lib/rubanok.rb
|
|
149
|
+
- lib/rubanok/dsl/mapping.rb
|
|
150
|
+
- lib/rubanok/dsl/matching.rb
|
|
151
|
+
- lib/rubanok/ext/symbolize_keys.rb
|
|
152
|
+
- lib/rubanok/plane.rb
|
|
153
|
+
- lib/rubanok/rails/controller.rb
|
|
154
|
+
- lib/rubanok/railtie.rb
|
|
155
|
+
- lib/rubanok/rspec.rb
|
|
156
|
+
- lib/rubanok/rule.rb
|
|
72
157
|
- lib/rubanok/version.rb
|
|
73
158
|
- rubanok.gemspec
|
|
74
159
|
homepage: https://github.com/palkan/rubanok
|
|
@@ -83,15 +168,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
83
168
|
requirements:
|
|
84
169
|
- - ">="
|
|
85
170
|
- !ruby/object:Gem::Version
|
|
86
|
-
version:
|
|
171
|
+
version: 2.5.0
|
|
87
172
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
173
|
requirements:
|
|
89
174
|
- - ">="
|
|
90
175
|
- !ruby/object:Gem::Version
|
|
91
176
|
version: '0'
|
|
92
177
|
requirements: []
|
|
93
|
-
|
|
94
|
-
rubygems_version: 2.7.7
|
|
178
|
+
rubygems_version: 3.0.1
|
|
95
179
|
signing_key:
|
|
96
180
|
specification_version: 4
|
|
97
181
|
summary: Parameters-based transformation DSL
|