snake-eyes 0.0.8 → 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/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +104 -20
- data/Rakefile +29 -1
- data/lib/snake-eyes/engine.rb +12 -0
- data/lib/snake-eyes/interface_changes.rb +117 -71
- data/lib/snake-eyes/version.rb +1 -1
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +26 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/test.log +7104 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/nested_attributes_spec.rb +231 -0
- data/spec/params_spec.rb +154 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/substitutions_spec.rb +286 -0
- metadata +135 -15
- data/.gitignore +0 -22
- data/Gemfile +0 -3
- data/snake-eyes.gemspec +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5d87558dd242a686cdeb61d193ff4785f0dca88
|
4
|
+
data.tar.gz: 3342dc90f6aa0cb230758f303d9ba7694135369a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b52f46eb4f20de9c95d6b00e8ba465c98c40fcae292d06508c3ec6a35b856d25b4f06f41b78cad1364d301c82c0f4ad375ba501f94ee16c63bf89f8db980140
|
7
|
+
data.tar.gz: accbf453ddafdf9b49b598a1e89cc01de69285e204a7417fb157de84918224adfd372b1c8860f76940dab4dbccf721119b2c32d0a6dfbe7d88d34b359b2cfc94
|
data/{LICENSE.txt → LICENSE}
RENAMED
data/README.md
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
# SnakeEyes
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧
|
8
|
-
|
9
|
-
If you are using a version below `0.0.4`, please upgrade to avoid [potentially logging sensitive user information](https://github.com/greena13/snake-eyes/issues/1)
|
3
|
+
[]()
|
4
|
+
[](https://travis-ci.org/greena13/snake-eyes)
|
5
|
+
[](https://github.com/greena13/snake-eyes/blob/master/LICENSE)
|
10
6
|
|
11
|
-
|
7
|
+
Automatically convert between camel case APIs to snake case for your Rails code
|
12
8
|
|
13
9
|
## Installation
|
14
10
|
|
@@ -71,6 +67,8 @@ Once `snake_eyes_params` has been enabled for a controllor, `params` accepts an
|
|
71
67
|
end
|
72
68
|
```
|
73
69
|
|
70
|
+
#### Avoid _attributes suffix on parents: the _ prefix
|
71
|
+
|
74
72
|
To specify nested objects that should not have the `_attributes` suffix (but contain attributes that should), you can prefix them with an underscore:
|
75
73
|
|
76
74
|
|
@@ -92,22 +90,108 @@ To specify nested objects that should not have the `_attributes` suffix (but con
|
|
92
90
|
end
|
93
91
|
```
|
94
92
|
|
95
|
-
|
93
|
+
#### Reference any element of an array: the '*' wildcard
|
94
|
+
|
95
|
+
To apply the `_attributes` suffix to all elements of an array, use the `'*'` wildcard in place of the array index:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
class WithSnakeEyesController < ApplicationController
|
99
|
+
snake_eyes_params
|
100
|
+
|
101
|
+
def show
|
102
|
+
# Given
|
103
|
+
params(nested_attributes: [ _array: { '*' => :string } ])
|
104
|
+
|
105
|
+
# If the params are:
|
106
|
+
#
|
107
|
+
# 'array' => [
|
108
|
+
# { 'string' => 'string' },
|
109
|
+
# { 'string' => 'string2' },
|
110
|
+
# ]
|
111
|
+
#
|
112
|
+
# What will be returned:
|
113
|
+
#
|
114
|
+
# 'array' => [
|
115
|
+
# { 'string_attributes' => 'string' },
|
116
|
+
# { 'string_attributes' => 'string2' },
|
117
|
+
# ]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
## Substitutions
|
96
123
|
|
97
|
-
|
124
|
+
If you want to substitute alternative values for the ones that the controller actually receives, you can do that using the `substitutions` option:
|
98
125
|
|
99
|
-
|
126
|
+
```ruby
|
127
|
+
class WithSnakeEyesController < ApplicationController
|
128
|
+
snake_eyes_params
|
100
129
|
|
101
|
-
|
130
|
+
def show
|
131
|
+
# Given
|
132
|
+
params(substitutions: {
|
133
|
+
shallow_object: {
|
134
|
+
price: { replace: 'FREE', with: 0.00 }
|
135
|
+
}
|
136
|
+
})
|
137
|
+
|
138
|
+
# If params is:
|
139
|
+
#
|
140
|
+
# 'shallowObject' => {
|
141
|
+
# 'price' => 'FREE'
|
142
|
+
# }
|
143
|
+
#
|
144
|
+
# What will be returned:
|
145
|
+
#
|
146
|
+
# 'shallow_object' => {
|
147
|
+
# 'price' => 0.00
|
148
|
+
# }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
```
|
102
152
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
153
|
+
You can also provide multiple substitutions as an array. They are matched left-to-right, and the first matching substitution is the one that is used.
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
class WithSnakeEyesController < ApplicationController
|
157
|
+
snake_eyes_params
|
158
|
+
|
159
|
+
def show
|
160
|
+
# Given
|
161
|
+
params(substitutions: {
|
162
|
+
shallow_object: {
|
163
|
+
price: [
|
164
|
+
{ replace: 'FREE', with: 0.00 } ,
|
165
|
+
{ replace: 'EXPENSIVE', with: 999.00 }
|
166
|
+
]
|
167
|
+
}
|
168
|
+
})
|
169
|
+
|
170
|
+
# If params is:
|
171
|
+
#
|
172
|
+
# 'shallowObject' => {
|
173
|
+
# 'price' => 'FREE'
|
174
|
+
# }
|
175
|
+
#
|
176
|
+
# What will be returned:
|
177
|
+
#
|
178
|
+
# 'shallow_object' => {
|
179
|
+
# 'price' => 0.00
|
180
|
+
# }
|
181
|
+
|
182
|
+
# If params is:
|
183
|
+
#
|
184
|
+
# 'shallowObject' => {
|
185
|
+
# 'price' => 'EXPENSIVE'
|
186
|
+
# }
|
187
|
+
#
|
188
|
+
# What will be returned:
|
189
|
+
#
|
190
|
+
# 'shallow_object' => {
|
191
|
+
# 'price' => 999.00
|
192
|
+
# }
|
193
|
+
end
|
194
|
+
end
|
111
195
|
```
|
112
196
|
|
113
197
|
## Configuration
|
data/Rakefile
CHANGED
@@ -1,2 +1,30 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
2
6
|
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'SnakeEyes'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
Bundler::GemHelper.install_tasks
|
21
|
+
|
22
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
23
|
+
|
24
|
+
require 'rspec/core'
|
25
|
+
require 'rspec/core/rake_task'
|
26
|
+
|
27
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
28
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
29
|
+
|
30
|
+
task :default => :spec
|
@@ -1,81 +1,38 @@
|
|
1
1
|
module SnakeEyes
|
2
2
|
module InterfaceChanges
|
3
|
+
KEYS_ALWAYS_PRESENT = [
|
4
|
+
"controller",
|
5
|
+
"action"
|
6
|
+
]
|
7
|
+
|
3
8
|
def params(options = {})
|
4
9
|
validate_options(options)
|
5
10
|
|
6
11
|
original_params = super()
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
# List of subtrees maintained to mark the depth-first traversal's position
|
11
|
-
# throughout the transformation of the original param's keys, whereby the
|
12
|
-
# last element is the traversal's current position and backtracking (going
|
13
|
-
# from child to parent) is achieved by popping off the last element.
|
14
|
-
|
15
|
-
original_params_sub_trees = [
|
16
|
-
original_params
|
17
|
-
]
|
18
|
-
|
19
|
-
# Convert the relatively flat format used to specify the nested attributes
|
20
|
-
# (easier for specification) into a series of nested objects (easier for
|
21
|
-
# look-ups)
|
13
|
+
params_present = (original_params.keys | KEYS_ALWAYS_PRESENT).length > KEYS_ALWAYS_PRESENT.length
|
22
14
|
|
23
|
-
|
15
|
+
unless params_present
|
16
|
+
return original_params
|
17
|
+
end
|
24
18
|
|
25
19
|
@previous_params ||= { }
|
26
20
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# nested_schema is checked at each point in the traversal of original_params
|
33
|
-
|
34
|
-
nested_schema_sub_trees = [
|
35
|
-
nested_schema
|
36
|
-
]
|
37
|
-
|
38
|
-
transformed_params = original_params.deep_transform_keys do |original_key|
|
39
|
-
# Synchronise the original params sub-tree with the current key being
|
40
|
-
# transformed. We can detect that the sub-tree is stale because the key
|
41
|
-
# being transformed does not appear amongst its own. When the sub-tree is
|
42
|
-
# indeed stale, move the position to its parent for the original params
|
43
|
-
# sub-tree and the nested schema sub-tree and repeat the check.
|
44
|
-
|
45
|
-
while original_params_sub_trees.length > 1 && original_params_sub_trees.last[original_key].nil?
|
46
|
-
original_params_sub_trees.pop
|
47
|
-
nested_schema_sub_trees.pop
|
21
|
+
nested_schema = build_options_schema(options[:nested_attributes] || {}, '') do |target, parent_name|
|
22
|
+
if parent_name.empty? || parent_name.starts_with?('_')
|
23
|
+
target
|
24
|
+
else
|
25
|
+
target.merge({ _attributes_suffix: true })
|
48
26
|
end
|
27
|
+
end
|
49
28
|
|
50
|
-
|
51
|
-
|
52
|
-
# Append the '_attributes' suffix if the original params key has the
|
53
|
-
# same name and is nested in the same place as one mentioned in the
|
54
|
-
# nested_attributes option
|
29
|
+
options[:nested_attributes] = nested_schema
|
55
30
|
|
56
|
-
|
31
|
+
return @previous_params[options] if @previous_params[options]
|
57
32
|
|
58
|
-
|
59
|
-
if nested_schema_sub_trees.last[transformed_key_base]
|
60
|
-
transformed_key_base + '_attributes'
|
61
|
-
else
|
62
|
-
transformed_key_base
|
63
|
-
end
|
33
|
+
transformed_params = deep_transform(original_params, options)
|
64
34
|
|
65
|
-
|
66
|
-
original_params_sub_trees.push(original_params_sub_tree)
|
67
|
-
|
68
|
-
nested_schema_sub_trees.push(
|
69
|
-
nested_schema_sub_trees.last[transformed_key_base] ||
|
70
|
-
nested_schema_sub_trees.last['_' + transformed_key_base] ||
|
71
|
-
{}
|
72
|
-
)
|
73
|
-
end
|
74
|
-
|
75
|
-
transformed_key
|
76
|
-
end
|
77
|
-
|
78
|
-
@previous_params[nested_schema] = @snake_eyes_params = ActionController::Parameters.new(transformed_params)
|
35
|
+
@previous_params[options] = @snake_eyes_params = ActionController::Parameters.new(transformed_params)
|
79
36
|
|
80
37
|
log_snakized_params
|
81
38
|
|
@@ -86,7 +43,7 @@ module SnakeEyes
|
|
86
43
|
|
87
44
|
def validate_options(options)
|
88
45
|
options.keys.each do |key|
|
89
|
-
raise ArgumentError.new("SnakeEyes: params received unrecognised option '#{key}'") if key != :nested_attributes
|
46
|
+
raise ArgumentError.new("SnakeEyes: params received unrecognised option '#{key}'") if key != :nested_attributes && key != :substitutions
|
90
47
|
end
|
91
48
|
end
|
92
49
|
|
@@ -100,28 +57,117 @@ module SnakeEyes
|
|
100
57
|
end
|
101
58
|
end
|
102
59
|
|
103
|
-
def
|
60
|
+
def deep_transform(target, options = {})
|
104
61
|
|
105
|
-
|
62
|
+
nested_attributes = options[:nested_attributes] || {}
|
63
|
+
|
64
|
+
substitutions =
|
65
|
+
if options[:substitutions].kind_of?(Array)
|
66
|
+
options[:substitutions].map(&:stringify_keys)
|
67
|
+
else
|
68
|
+
(options[:substitutions] || {}).stringify_keys
|
69
|
+
end
|
106
70
|
|
107
|
-
|
108
|
-
|
71
|
+
if target.kind_of?(Array)
|
72
|
+
target.map do |targetElement|
|
73
|
+
deep_transform(targetElement, {
|
74
|
+
nested_attributes: nested_attributes['*'],
|
75
|
+
substitutions: substitutions.kind_of?(Array) ? {} : substitutions['*']
|
76
|
+
})
|
109
77
|
end
|
110
78
|
|
111
|
-
elsif
|
79
|
+
elsif target.kind_of?(Hash)
|
112
80
|
|
113
|
-
|
81
|
+
target.inject({}) do |memo, key_and_value|
|
114
82
|
key, value = key_and_value
|
115
|
-
|
83
|
+
|
84
|
+
# Append the '_attributes' suffix if the original params key has the
|
85
|
+
# same name and is nested in the same place as one mentioned in the
|
86
|
+
# nested_attributes option
|
87
|
+
|
88
|
+
transformed_key_base = key.to_s.underscore
|
89
|
+
|
90
|
+
transformed_key =
|
91
|
+
if nested_attributes[transformed_key_base] && nested_attributes[transformed_key_base][:_attributes_suffix]
|
92
|
+
transformed_key_base + '_attributes'
|
93
|
+
else
|
94
|
+
transformed_key_base
|
95
|
+
end
|
96
|
+
|
97
|
+
transformed_value = deep_transform(value,
|
98
|
+
{
|
99
|
+
nested_attributes: nested_attributes[transformed_key_base] || nested_attributes['_' + transformed_key_base],
|
100
|
+
substitutions: substitutions.kind_of?(Array) ? {} : substitutions[transformed_key_base]
|
101
|
+
}
|
102
|
+
)
|
103
|
+
|
104
|
+
memo[transformed_key] = transformed_value
|
105
|
+
|
116
106
|
memo
|
117
107
|
end
|
118
108
|
|
119
109
|
else
|
110
|
+
perform_substitution(target, substitutions)
|
111
|
+
end
|
120
112
|
|
121
|
-
|
113
|
+
end
|
114
|
+
|
115
|
+
def perform_substitution(target, substitution)
|
116
|
+
if substitution.kind_of?(Array)
|
117
|
+
matching_substitution = substitution.find do |substitution_item|
|
118
|
+
has_substitution_keys?(substitution_item) && target === substitution_item["replace"]
|
119
|
+
end
|
120
|
+
|
121
|
+
if matching_substitution
|
122
|
+
matching_substitution["with"]
|
123
|
+
else
|
124
|
+
target
|
125
|
+
end
|
126
|
+
|
127
|
+
else
|
128
|
+
if has_substitution_keys?(substitution)
|
129
|
+
target === substitution["replace"] ? substitution["with"] : target
|
130
|
+
else
|
131
|
+
target
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def has_substitution_keys?(substitution)
|
137
|
+
substitution.has_key?("replace") && substitution.has_key?("with")
|
138
|
+
end
|
139
|
+
|
140
|
+
def build_options_schema(attributes_list = [], parent_name = '', options = {}, &block)
|
141
|
+
|
142
|
+
if attributes_list.kind_of?(Array)
|
143
|
+
attributes_array = attributes_list.inject({}) do |memo, nested_attribute|
|
144
|
+
memo.merge(build_options_schema(nested_attribute, parent_name, options, &block))
|
145
|
+
end
|
146
|
+
|
147
|
+
yield(attributes_array, parent_name)
|
148
|
+
|
149
|
+
elsif attributes_list.kind_of?(Hash) && (!options[:internal_attributes] || (attributes_list.keys & options[:internal_attributes]).length > options[:internal_attributes].length)
|
150
|
+
|
151
|
+
attributes_hash = attributes_list.inject({}) do |memo, key_and_value|
|
152
|
+
key, value = key_and_value
|
153
|
+
|
154
|
+
memo[key.to_s] = yield(build_options_schema(value, '', options, &block), key.to_s)
|
155
|
+
|
156
|
+
memo
|
157
|
+
end
|
158
|
+
|
159
|
+
yield(attributes_hash, parent_name)
|
160
|
+
else
|
161
|
+
|
162
|
+
{
|
163
|
+
attributes_list.to_s.underscore => yield({}, attributes_list.to_s.underscore)
|
164
|
+
}
|
122
165
|
|
123
166
|
end
|
124
167
|
|
125
168
|
end
|
169
|
+
|
170
|
+
|
126
171
|
end
|
172
|
+
|
127
173
|
end
|
data/lib/snake-eyes/version.rb
CHANGED