ecoportal-api-v2 0.8.5 → 0.8.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +60 -25
- data/bin/console +1 -1
- data/ecoportal-api-v2.gemspec +5 -5
- data/lib/ecoportal/api-v2.rb +2 -2
- data/lib/ecoportal/api/common.v2.rb +8 -0
- data/lib/ecoportal/api/common/content.rb +1 -0
- data/lib/ecoportal/api/common/content/array_model.rb +8 -6
- data/lib/ecoportal/api/common/content/collection_model.rb +10 -4
- data/lib/ecoportal/api/common/content/double_model.rb +15 -4
- data/lib/ecoportal/api/common/content/hash_diff_patch.rb +31 -19
- data/lib/ecoportal/api/common/content/model_helpers.rb +36 -0
- data/lib/ecoportal/api/v2/page.rb +2 -0
- data/lib/ecoportal/api/v2/page/component.rb +33 -3
- data/lib/ecoportal/api/v2/page/component/action.rb +13 -5
- data/lib/ecoportal/api/v2/page/component/action_field.rb +37 -2
- data/lib/ecoportal/api/v2/page/component/chart_field.rb +43 -5
- data/lib/ecoportal/api/v2/page/component/chart_field/benchmark.rb +30 -0
- data/lib/ecoportal/api/v2/page/component/chart_field/config.rb +23 -0
- data/lib/ecoportal/api/v2/page/component/chart_field/frequency.rb +3 -4
- data/lib/ecoportal/api/v2/page/component/chart_field/heatmap.rb +1 -3
- data/lib/ecoportal/api/v2/page/component/chart_field/indicator.rb +4 -5
- data/lib/ecoportal/api/v2/page/component/chart_field/multiseries.rb +3 -5
- data/lib/ecoportal/api/v2/page/component/chart_field/sankey.rb +1 -3
- data/lib/ecoportal/api/v2/page/component/chart_field/serie.rb +3 -4
- data/lib/ecoportal/api/v2/page/component/chart_field/series_config.rb +5 -7
- data/lib/ecoportal/api/v2/page/component/chart_fr_field.rb +7 -5
- data/lib/ecoportal/api/v2/page/component/checklist_field.rb +1 -1
- data/lib/ecoportal/api/v2/page/component/checklist_item.rb +3 -2
- data/lib/ecoportal/api/v2/page/component/date_field.rb +71 -4
- data/lib/ecoportal/api/v2/page/component/file.rb +3 -2
- data/lib/ecoportal/api/v2/page/component/gauge_field.rb +2 -2
- data/lib/ecoportal/api/v2/page/component/geo_coordinates.rb +13 -0
- data/lib/ecoportal/api/v2/page/component/geo_field.rb +4 -1
- data/lib/ecoportal/api/v2/page/component/images_field.rb +57 -1
- data/lib/ecoportal/api/v2/page/component/people_field.rb +104 -6
- data/lib/ecoportal/api/v2/page/component/plain_text_field.rb +34 -2
- data/lib/ecoportal/api/v2/page/component/reference_field.rb +32 -3
- data/lib/ecoportal/api/v2/page/component/selection_field.rb +71 -4
- data/lib/ecoportal/api/v2/page/component/selection_option.rb +12 -2
- data/lib/ecoportal/api/v2/page/component/tag_field.rb +31 -1
- data/lib/ecoportal/api/v2/page/components.rb +8 -3
- data/lib/ecoportal/api/v2/page/permission_flags.rb +67 -0
- data/lib/ecoportal/api/v2/page/permit.rb +14 -0
- data/lib/ecoportal/api/v2/page/section.rb +65 -5
- data/lib/ecoportal/api/v2/page/sections.rb +64 -6
- data/lib/ecoportal/api/v2/page/stage.rb +11 -3
- data/lib/ecoportal/api/v2/page/stages.rb +7 -7
- data/lib/ecoportal/api/v2/pages/page_stage.rb +3 -3
- data/lib/ecoportal/api/v2_version.rb +5 -0
- metadata +16 -10
- data/lib/ecoportal/api/common.rb +0 -18
- data/lib/ecoportal/api/v2/version.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5833ea731a9d1360c346264d234ce9f95266db92a5480180a17f413459f196de
|
4
|
+
data.tar.gz: f3ef175580d2ab47c0ee7f51dc2731c0b42bd27ffa9f59600fb506d5cde38a4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52064ec37a32470e699f846ac1c6c57f8f535fb0be1d689a4f086836a47f9fb2805c0f84533842f5aaa4ee9e538c60e61c1569177ae16a015520c65248347174
|
7
|
+
data.tar.gz: 0f8cd291558cad37b5157dba7ffdea6670548952b6aa5b0c90b34820abd2693d8f484009265bbc4c8c83fb46684f2968e9a40d4a65563e0ffc6704cca30885d0
|
data/CHANGELOG.md
CHANGED
@@ -1,24 +1,79 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
-
## [0.8.
|
4
|
+
## [0.8.9] - 2021-06-xx
|
5
5
|
|
6
6
|
### Added
|
7
|
+
- `Ecoportal::API::Common::Content::ModelHelpers`
|
8
|
+
- Starting with `#same_string?`, this lib aims to offer a full set of helper comparers.
|
9
|
+
- `Ecoportal::API::V2::Page::Components#get_by_id`
|
10
|
+
- In future changes, method `[]` might be overriding the method of the parent class `CollectionModel`.
|
11
|
+
- `Ecoportal::API::V2::Page::PermissionFlags` to offer support to `can_permission`, `subscribed`, etc.
|
12
|
+
- `Ecoportal::API::V2::Page::Section#add_component` super handy helper to add fields to sections.
|
13
|
+
- `Ecoportal::API::V2::Page::Component::PeopleField#delete` to remove people using their ids.
|
14
|
+
- `Ecoportal::API::V2::Page::Component::GeoField` **added** suport for `#coordinates`
|
15
|
+
- With embedded object `Ecoportal::API::V2::Page::Component::GeoCoordinates`
|
16
|
+
- `#configure` method, to all the `Component` types (but `ChartField` s)
|
17
|
+
- This method allows to quickly set field properties.
|
18
|
+
- `Ecoportal::API::V2::Page::Sections#between`, aiming to obtain sections between two other ones
|
19
|
+
|
20
|
+
|
7
21
|
### Changed
|
8
|
-
|
22
|
+
- `Ecoportal::API::V2::Page::Section` real support for `split` sections (right vs left)
|
23
|
+
- This is actually a **FIX**
|
24
|
+
- `Ecoportal::API::V2::Page::Component::DateField#create_event`
|
25
|
+
- Now it switches to _allow future dates_
|
26
|
+
- `Ecoportal::API::V2::Page::Component::SelectionField#add_option`, the `name` parameter is now optional
|
27
|
+
- This change aims to make it consistent with the back-end.
|
28
|
+
- To its actual `Boolean` type some properties
|
29
|
+
- All `Component` types
|
9
30
|
|
31
|
+
### Fixed
|
32
|
+
- `Ecoportal::API::Common::Content::HashDiffPatch` did not support `Hash` values without `id` or `patch_ver`
|
33
|
+
- This fix should allow them to flow to the update
|
34
|
+
- `Ecoportal::API::V2::Page::Component::ChartField` did not have `doc` for `mode` based configuration
|
35
|
+
- `Ecoportal::API::Common::Content::ArrayModel#insert_one`
|
36
|
+
- When `pos`, `before` & `after` were `nil` it was failing to attach the element.
|
37
|
+
- `Ecoportal::API::V2::Page::Sections#add` was not adding in the correct order
|
38
|
+
- `Ecoportal::API::V2::Page::Section.new_doc` was unnecessarily giving `9999` as default weight
|
10
39
|
|
11
|
-
## [0.8.
|
40
|
+
## [0.8.8] - 2021-08-03
|
12
41
|
|
13
42
|
### Added
|
43
|
+
- `Ecoportal::API::V2::Page::Component::ChartField::Benchmark`
|
44
|
+
- Support to convert selection options between numeric and text type.
|
45
|
+
- Add `Permit` object to `Page` and `Stage`
|
46
|
+
|
14
47
|
### Changed
|
15
|
-
-
|
48
|
+
- Removed `Stages.ordered_stages`: detected bug with Enumerable iteration
|
49
|
+
|
50
|
+
|
51
|
+
## [0.8.7] - 2021-05-25
|
52
|
+
|
53
|
+
### Changed
|
54
|
+
- Upgrade `ecoportal-api` gem
|
16
55
|
|
17
56
|
### Fixed
|
57
|
+
- Requiring `api/common` from 2 different ends
|
18
58
|
|
19
|
-
## [0.8.
|
59
|
+
## [0.8.6] - 2021-02-23
|
20
60
|
|
21
61
|
### Added
|
62
|
+
- `Ecoportal::API::V2::Page::Component::SelectionField` added methods `#numeric!` `#text!`
|
63
|
+
|
64
|
+
## [0.8.5] - 2021-02-22
|
65
|
+
|
66
|
+
### Changed
|
67
|
+
- upgrade
|
68
|
+
|
69
|
+
|
70
|
+
## [0.8.4] - 2021-02-22
|
71
|
+
|
72
|
+
### Changed
|
73
|
+
- roll back `ecoportal-api` dependency
|
74
|
+
|
75
|
+
## [0.8.3] - 2021-02-22
|
76
|
+
|
22
77
|
### Changed
|
23
78
|
- updated `rspec` for expected patch data on `delete` operation.
|
24
79
|
|
@@ -27,23 +82,16 @@ All notable changes to this project will be documented in this file.
|
|
27
82
|
|
28
83
|
## [0.8.2] - 2021-02-21
|
29
84
|
|
30
|
-
### Added
|
31
85
|
### Changed
|
32
86
|
- some necessary changes for the new gem to be active
|
33
87
|
|
34
|
-
### Fixed
|
35
|
-
|
36
88
|
## [0.8.1] - 2021-02-21
|
37
89
|
|
38
|
-
### Added
|
39
90
|
### Changed
|
40
91
|
- renamed parameters (backwards incompatible change):
|
41
92
|
- `Ecoportal::API::V2::Pages::Stages` methods `#get` and `#update`
|
42
93
|
- updated `Ecoportal::API::V2::Pages#` accordingly
|
43
94
|
|
44
|
-
### Fixed
|
45
|
-
|
46
|
-
|
47
95
|
## MOVED TO GEM `ecoportal-api-v2` ##
|
48
96
|
|
49
97
|
## [0.7.5] - 2021-02-21
|
@@ -137,42 +185,29 @@ All notable changes to this project will be documented in this file.
|
|
137
185
|
- automatic key builder
|
138
186
|
- helper: `Ecoportal::API::V2::v2key` method
|
139
187
|
- `user_key:` and `org_key:` as constructor methods for `Ecoportal::API::V2`
|
140
|
-
### Changed
|
141
|
-
### Fixed
|
142
188
|
|
143
189
|
## [0.7.1] - 2020-10-07
|
144
190
|
|
145
|
-
### Added
|
146
191
|
### Changed
|
147
192
|
- updated dependencies
|
148
|
-
### Fixed
|
149
|
-
|
150
193
|
|
151
194
|
## [0.7.0] - 2020-09-10
|
152
195
|
|
153
|
-
### Added
|
154
196
|
### Changed
|
155
197
|
- updated dependencies
|
156
|
-
### Fixed
|
157
198
|
|
158
199
|
## [0.6.1] - 2020-07-11
|
159
200
|
|
160
201
|
### Added
|
161
202
|
- `Ecoportal::API::Common::Content::DoubleModel#pretty_print`
|
162
|
-
### Changed
|
163
|
-
### Fixed
|
164
203
|
|
165
204
|
## [0.6.0] - 2020-07-11
|
166
205
|
|
167
|
-
### Added
|
168
206
|
### Changed
|
169
207
|
- upgraded `ecoportal-api` gem
|
170
|
-
### Fixed
|
171
208
|
|
172
209
|
## [0.5.9] - 2020-07-02
|
173
210
|
|
174
211
|
### Added
|
175
212
|
- helper `Ecoportal::API::Common::Content::StringDigest#indexable_label`: to see the part of a label that gets indexed
|
176
213
|
- this `CHANGELOG.md` file
|
177
|
-
### Changed
|
178
|
-
### Fixed
|
data/bin/console
CHANGED
data/ecoportal-api-v2.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
|
2
2
|
lib = File.expand_path("../lib", __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "ecoportal/api/
|
4
|
+
require "ecoportal/api/v2_version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "ecoportal-api-v2"
|
8
|
-
spec.version = Ecoportal::API::
|
8
|
+
spec.version = Ecoportal::API::GEM2_VERSION
|
9
9
|
spec.authors = ["Oscar Segura"]
|
10
10
|
spec.email = ["rien@ecoportal.co.nz", "oscar@ecoportal.co.nz", "bozydar@ecoportal.co.nz"]
|
11
11
|
|
@@ -22,12 +22,12 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
-
spec.add_development_dependency "bundler", ">= 2.2.
|
25
|
+
spec.add_development_dependency "bundler", ">= 2.2.17", "< 2.3"
|
26
26
|
spec.add_development_dependency "rspec", ">= 3.10.0", "< 3.11"
|
27
27
|
spec.add_development_dependency "rake", ">= 13.0.3", "< 13.1"
|
28
28
|
spec.add_development_dependency "yard", ">= 0.9.26", "< 0.10"
|
29
29
|
spec.add_development_dependency "redcarpet", ">= 3.5.1", "< 3.6"
|
30
|
-
spec.add_development_dependency "pry" , "
|
30
|
+
spec.add_development_dependency "pry" , ">= 0.14"
|
31
31
|
|
32
|
-
spec.add_dependency 'ecoportal-api', '>= 0.8.
|
32
|
+
spec.add_dependency 'ecoportal-api', '>= 0.8.3', '< 0.9'
|
33
33
|
end
|
data/lib/ecoportal/api-v2.rb
CHANGED
@@ -9,6 +9,7 @@ end
|
|
9
9
|
|
10
10
|
require 'ecoportal/api/common/content/class_helpers'
|
11
11
|
require 'ecoportal/api/common/content/string_digest'
|
12
|
+
require 'ecoportal/api/common/content/model_helpers'
|
12
13
|
require 'ecoportal/api/common/content/double_model'
|
13
14
|
require 'ecoportal/api/common/content/array_model'
|
14
15
|
require 'ecoportal/api/common/content/collection_model'
|
@@ -3,7 +3,8 @@ module Ecoportal
|
|
3
3
|
module Common
|
4
4
|
module Content
|
5
5
|
# Class to handle a plain Array embedded in a Hashed model.
|
6
|
-
# @note
|
6
|
+
# @note
|
7
|
+
# - Its purpose is to handle an Array of basic objects (i.e. `Date`, `String`, `Number`)
|
7
8
|
class ArrayModel < Content::DoubleModel
|
8
9
|
class TypeMismatchedComparison < Exception
|
9
10
|
def initialize (this: nil, that: msg = "Trying to compare objects with different behavior.")
|
@@ -222,15 +223,16 @@ module Ecoportal
|
|
222
223
|
def insert_one(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
|
223
224
|
i = index(value)
|
224
225
|
return i if (i && uniq?)
|
225
|
-
|
226
226
|
pos = case
|
227
227
|
when used_param?(pos)
|
228
|
-
pos
|
228
|
+
pos || length
|
229
229
|
when used_param?(before)
|
230
|
-
index(before)
|
230
|
+
before ? index(before) : length
|
231
231
|
when used_param?(after)
|
232
|
-
if
|
233
|
-
i + 1
|
232
|
+
if after
|
233
|
+
if i = index(after) then i + 1 end
|
234
|
+
else
|
235
|
+
length
|
234
236
|
end
|
235
237
|
else
|
236
238
|
length
|
@@ -2,6 +2,9 @@ module Ecoportal
|
|
2
2
|
module API
|
3
3
|
module Common
|
4
4
|
module Content
|
5
|
+
# CollectionModel aims to deal with Arrays of actual objects.
|
6
|
+
# @note to be able to refer to the correct element of the Collection,
|
7
|
+
# it is required that those elements have a unique `key` that allows to identify them
|
5
8
|
class CollectionModel < Content::DoubleModel
|
6
9
|
|
7
10
|
class << self
|
@@ -167,6 +170,7 @@ module Ecoportal
|
|
167
170
|
end
|
168
171
|
end
|
169
172
|
|
173
|
+
# Deletes `value` from this `CollectionModel` instance
|
170
174
|
def delete!(value)
|
171
175
|
unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel)
|
172
176
|
raise "'Content::DoubleModel' or 'Hash' doc required"
|
@@ -186,7 +190,7 @@ module Ecoportal
|
|
186
190
|
variables_remove!
|
187
191
|
end
|
188
192
|
|
189
|
-
# Gets the `key` of the object
|
193
|
+
# Gets the `key` of the object `value`
|
190
194
|
def get_key(value)
|
191
195
|
case value
|
192
196
|
when Content::DoubleModel
|
@@ -235,10 +239,13 @@ module Ecoportal
|
|
235
239
|
super
|
236
240
|
end
|
237
241
|
|
242
|
+
# Deletes `value` from `doc` (here referred as `_doc_items`)
|
243
|
+
# @return [Object] the element deleted from `doc`
|
238
244
|
def _doc_delete(value)
|
239
245
|
if current_pos = _doc_key(value)
|
240
|
-
_doc_items.delete_at(current_pos)
|
241
|
-
|
246
|
+
_doc_items.delete_at(current_pos).tap do |deleted|
|
247
|
+
on_change
|
248
|
+
end
|
242
249
|
end
|
243
250
|
end
|
244
251
|
|
@@ -250,7 +257,6 @@ module Ecoportal
|
|
250
257
|
when used_param?(before)
|
251
258
|
_doc_key(before)
|
252
259
|
when used_param?(after)
|
253
|
-
#puts "to add after #{after.id}"
|
254
260
|
if i = _doc_key(after)
|
255
261
|
i + 1
|
256
262
|
end
|
@@ -10,6 +10,7 @@ module Ecoportal
|
|
10
10
|
class DoubleModel < Common::BaseModel
|
11
11
|
NOT_USED = Common::Content::ClassHelpers::NOT_USED
|
12
12
|
extend Common::Content::ClassHelpers
|
13
|
+
include Common::Content::ModelHelpers
|
13
14
|
|
14
15
|
class UnlinkedModel < Exception
|
15
16
|
def initialize (msg = "Something went wrong when linking the document.", from: nil, key: nil)
|
@@ -109,6 +110,16 @@ module Ecoportal
|
|
109
110
|
self
|
110
111
|
end
|
111
112
|
|
113
|
+
# To link as a `Boolean` to a subjacent `Hash` model property
|
114
|
+
# @param read_only [Boolean] should it only define the reader?
|
115
|
+
def passboolean(*methods, read_only: false)
|
116
|
+
pass_reader(*methods) {|value| value}
|
117
|
+
unless read_only
|
118
|
+
pass_writer(*methods) {|value| !!value}
|
119
|
+
end
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
112
123
|
# To link as plain `Array` to a subjacent `Hash` model property
|
113
124
|
# @param order_matters [Boolean] does the order matter
|
114
125
|
# @param uniq [Boolean] should it contain unique elements
|
@@ -151,7 +162,7 @@ module Ecoportal
|
|
151
162
|
else
|
152
163
|
raise "You should either specify the 'klass' of the elements or the 'enum_class'"
|
153
164
|
end
|
154
|
-
embed(method, key: key, multiple: true, klass: eclass)
|
165
|
+
embed(method, key: key, multiple: true, klass: eclass)
|
155
166
|
end
|
156
167
|
|
157
168
|
private
|
@@ -258,10 +269,10 @@ module Ecoportal
|
|
258
269
|
|
259
270
|
def reset!(key = nil)
|
260
271
|
if key
|
261
|
-
keys = [].
|
272
|
+
keys = [key].flatten.compact
|
262
273
|
odoc = original_doc.dig(*keys)
|
263
|
-
|
264
|
-
|
274
|
+
odoc = odoc && JSON.parse(odoc.to_json)
|
275
|
+
dig_set(doc, keys, odoc)
|
265
276
|
else
|
266
277
|
replace_doc(JSON.parse(original_doc.to_json))
|
267
278
|
end
|
@@ -85,31 +85,43 @@ module Ecoportal
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def patch_delete(b)
|
88
|
-
return NO_CHANGES unless b.is_a?(Hash)
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
88
|
+
return NO_CHANGES unless b.is_a?(Hash)
|
89
|
+
if id = get_id(b, exception: false)
|
90
|
+
{
|
91
|
+
"id" => id,
|
92
|
+
"operation" => "deleted",
|
93
|
+
"data" => patch_data(b, delete: true)
|
94
|
+
}
|
95
|
+
else
|
96
|
+
nil
|
97
|
+
end
|
94
98
|
end
|
95
99
|
|
96
100
|
def patch_new(a)
|
97
|
-
return NO_CHANGES unless a.is_a?(Hash)
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
101
|
+
return NO_CHANGES unless a.is_a?(Hash)
|
102
|
+
if id = get_id(a, exception: false)
|
103
|
+
{
|
104
|
+
"id" => id,
|
105
|
+
"operation" => "new",
|
106
|
+
"data" => patch_data(a)
|
107
|
+
}
|
108
|
+
else
|
109
|
+
a
|
110
|
+
end
|
103
111
|
end
|
104
112
|
|
105
113
|
def patch_update(a, b)
|
106
|
-
return NO_CHANGES unless a.is_a?(Hash)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
114
|
+
return NO_CHANGES unless a.is_a?(Hash)
|
115
|
+
if id = get_id(a, exception: false)
|
116
|
+
{
|
117
|
+
"id" => id,
|
118
|
+
"operation" => "changed",
|
119
|
+
"data" => patch_data(a, b)
|
120
|
+
}.tap do |update_hash|
|
121
|
+
return nil unless update_hash["data"] != NO_CHANGES
|
122
|
+
end
|
123
|
+
else
|
124
|
+
a
|
113
125
|
end
|
114
126
|
end
|
115
127
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Ecoportal
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module Content
|
5
|
+
module ModelHelpers
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
# Offers multiple ways to compare two strings
|
10
|
+
def same_string?(value1, value2, exact: false)
|
11
|
+
case
|
12
|
+
when value1.is_a?(String) && value2.is_a?(String)
|
13
|
+
if exact
|
14
|
+
value1 == value2
|
15
|
+
else
|
16
|
+
value1.to_s.strip.downcase == value2.to_s.strip.downcase
|
17
|
+
end
|
18
|
+
when value1.is_a?(Regexp) && value2.is_a?(String)
|
19
|
+
value2 =~ value1
|
20
|
+
when value1.is_a?(String) && value2.is_a?(Regexp)
|
21
|
+
value1 =~ value2
|
22
|
+
else
|
23
|
+
value1 == value2
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def hash_except(hash, *keys)
|
28
|
+
keys.each {|key| hash.delete(key)}
|
29
|
+
hash
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|