ecoportal-api-oozes 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +55 -0
  5. data/.travis.yml +5 -0
  6. data/.yardopts +10 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE +21 -0
  9. data/README.md +20 -0
  10. data/Rakefile +27 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/ecoportal-api-oozes.gemspec +31 -0
  14. data/lib/ecoportal/api-oozes.rb +10 -0
  15. data/lib/ecoportal/api/common.rb +18 -0
  16. data/lib/ecoportal/api/common/content.rb +18 -0
  17. data/lib/ecoportal/api/common/content/array_model.rb +287 -0
  18. data/lib/ecoportal/api/common/content/class_helpers.rb +111 -0
  19. data/lib/ecoportal/api/common/content/client.rb +40 -0
  20. data/lib/ecoportal/api/common/content/collection_model.rb +223 -0
  21. data/lib/ecoportal/api/common/content/doc_helpers.rb +67 -0
  22. data/lib/ecoportal/api/common/content/double_model.rb +334 -0
  23. data/lib/ecoportal/api/common/content/hash_diff_patch.rb +162 -0
  24. data/lib/ecoportal/api/common/content/string_digest.rb +22 -0
  25. data/lib/ecoportal/api/common/content/wrapped_response.rb +42 -0
  26. data/lib/ecoportal/api/v2.rb +48 -0
  27. data/lib/ecoportal/api/v2/page.rb +30 -0
  28. data/lib/ecoportal/api/v2/page/component.rb +105 -0
  29. data/lib/ecoportal/api/v2/page/component/action.rb +17 -0
  30. data/lib/ecoportal/api/v2/page/component/action_field.rb +16 -0
  31. data/lib/ecoportal/api/v2/page/component/checklist_field.rb +16 -0
  32. data/lib/ecoportal/api/v2/page/component/checklist_item.rb +15 -0
  33. data/lib/ecoportal/api/v2/page/component/date_field.rb +13 -0
  34. data/lib/ecoportal/api/v2/page/component/file.rb +16 -0
  35. data/lib/ecoportal/api/v2/page/component/files_field.rb +14 -0
  36. data/lib/ecoportal/api/v2/page/component/gauge_field.rb +13 -0
  37. data/lib/ecoportal/api/v2/page/component/geo_field.rb +13 -0
  38. data/lib/ecoportal/api/v2/page/component/image.rb +16 -0
  39. data/lib/ecoportal/api/v2/page/component/images_field.rb +14 -0
  40. data/lib/ecoportal/api/v2/page/component/law_field.rb +12 -0
  41. data/lib/ecoportal/api/v2/page/component/number_field.rb +12 -0
  42. data/lib/ecoportal/api/v2/page/component/people_field.rb +13 -0
  43. data/lib/ecoportal/api/v2/page/component/plain_text_field.rb +13 -0
  44. data/lib/ecoportal/api/v2/page/component/reference_field.rb +12 -0
  45. data/lib/ecoportal/api/v2/page/component/rich_text_field.rb +13 -0
  46. data/lib/ecoportal/api/v2/page/component/selection_field.rb +18 -0
  47. data/lib/ecoportal/api/v2/page/component/selection_option.rb +15 -0
  48. data/lib/ecoportal/api/v2/page/component/signature_field.rb +14 -0
  49. data/lib/ecoportal/api/v2/page/component/tag_field.rb +12 -0
  50. data/lib/ecoportal/api/v2/page/components.rb +20 -0
  51. data/lib/ecoportal/api/v2/page/section.rb +36 -0
  52. data/lib/ecoportal/api/v2/page/sections.rb +14 -0
  53. data/lib/ecoportal/api/v2/page/stage.rb +16 -0
  54. data/lib/ecoportal/api/v2/page/stages.rb +14 -0
  55. data/lib/ecoportal/api/v2/pages.rb +63 -0
  56. data/lib/ecoportal/api/v2/register.rb +36 -0
  57. data/lib/ecoportal/api/v2/registers.rb +35 -0
  58. data/lib/ecoportal/api/v2/template.rb +10 -0
  59. data/lib/ecoportal/api/v2/version.rb +7 -0
  60. metadata +219 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f6ecefc83d30f273a4ededb781765cde494d5e6e0facad8a501fc6ef023e73ee
4
+ data.tar.gz: b1e0b4dab879d237580bb4d599ea500fe7a5154f6f7f874e418360bfefc9700d
5
+ SHA512:
6
+ metadata.gz: 98694b883b97f35fc71c0633717d92ec9da2704bd6d63c824a38c43eca512645ab6f3ecd77924d41ae527d112ffc36fcc4190f82e06b4b6453d21ccca44de22d
7
+ data.tar.gz: 9ef14de17795590c48d3b3cb0a6e2ba96be3b1d1ff76cee959a1860cbf1689d17b0551842352b7326094d1c8071a8569cf58c6b8feb5dd8933da8cd6a69521fc
@@ -0,0 +1,20 @@
1
+ # it's a gem, ignore the lockfile
2
+ Gemfile.lock
3
+
4
+ # build artifacts
5
+ *.gem
6
+ /.bundle/
7
+ /vendor/bundle
8
+ /spec/reports/
9
+ /tmp/
10
+ /pkg/
11
+
12
+ # docs
13
+ /.yardoc
14
+ /_yardoc/
15
+ /coverage/
16
+ /doc/
17
+
18
+ # rspec failure tracking
19
+ .rspec_status
20
+ scratch.rb
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,55 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5.1
3
+ Exclude:
4
+ - 'config/routes.rb'
5
+
6
+ Metrics/LineLength:
7
+ Enabled: false
8
+ Metrics/BlockLength:
9
+ ExcludedMethods: [context, describe]
10
+ Max: 50
11
+ Metrics/MethodLength:
12
+ Max: 50
13
+ Metrics/ClassLength:
14
+ Max: 200
15
+ Metrics/AbcSize:
16
+ Max: 30
17
+
18
+ ParameterLists:
19
+ Max: 5
20
+ CountKeywordArgs: false
21
+
22
+ Style/StringLiterals:
23
+ Enabled: false
24
+ Style/FrozenStringLiteralComment:
25
+ Enabled: false
26
+ Style/CommentedKeyword:
27
+ Enabled: false
28
+ Style/MultilineBlockChain:
29
+ Enabled: false
30
+ Style/Documentation:
31
+ Enabled: false
32
+ Style/StringLiteralsInInterpolation:
33
+ Enabled: false
34
+ Style/AndOr:
35
+ Enabled: false
36
+
37
+ Layout/SpaceInsideHashLiteralBraces:
38
+ Enabled: false
39
+ Layout/SpaceInsideBlockBraces:
40
+ Enabled: false
41
+ Layout/SpaceAroundOperators:
42
+ Enabled: false
43
+ Layout/ExtraSpacing:
44
+ AllowForAlignment: true
45
+ Layout/AccessModifierIndentation:
46
+ EnforcedStyle: indent
47
+ Layout/DotPosition:
48
+ EnforcedStyle: trailing
49
+ Layout/MultilineMethodCallIndentation:
50
+ EnforcedStyle: indented
51
+ Layout/IndentHash:
52
+ Enabled: false
53
+
54
+ Naming/VariableNumber:
55
+ EnforcedStyle: snake_case
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ before_install: gem install bundler -v 1.16.1
@@ -0,0 +1,10 @@
1
+ --readme README.md
2
+ --charset utf-8
3
+ --markup-provider=redcarpet
4
+ --markup=markdown
5
+ --no-private
6
+ --output-dir ./doc
7
+ 'lib/**/*.rb'
8
+ -
9
+ CHANGELOG.md
10
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in ecoportal-api.gemspec
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 ecoPortal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,20 @@
1
+ # Ecoportal::API::V2
2
+
3
+ This is a basic collection of helpers that can be used to interact with the
4
+ [ecoPortal](https://www.ecoportal.com) `APIv2`
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'ecoportal-api-oozes', require: %w[ecoportal/api-oozes]
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install ecoportal-api-oozes
@@ -0,0 +1,27 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "yard"
4
+ require "redcarpet"
5
+
6
+ desc "run the specs"
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ desc "run rspec showing backtrace"
10
+ RSpec::Core::RakeTask.new(:spec_trace) do |task|
11
+ task.rspec_opts = ['--backtrace']
12
+ end
13
+
14
+ desc "run rspec stopping on first fail, and show backtrace"
15
+ RSpec::Core::RakeTask.new(:spec_fast) do |task|
16
+ task.rspec_opts = ['--fail-fast', '--backtrace']
17
+ end
18
+
19
+ # default task name is yard
20
+ desc "Yard: generate all the documentation"
21
+ YARD::Rake::YardocTask.new(:doc) do |t|
22
+ #t.files = ['lib/**/*.rb']
23
+ end
24
+
25
+ task :default => [:spec]
26
+ task :rspec_trace => :spec_trace
27
+ task :rspec_fast => :spec_fast
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ecoportal/api"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "pry"
14
+ Pry.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,31 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "ecoportal/api/v2/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ecoportal-api-oozes"
8
+ spec.version = Ecoportal::API::V2::VERSION
9
+ spec.authors = ["Oscar Segura"]
10
+ spec.email = ["rien@ecoportal.co.nz", "oscar@ecoportal.co.nz", "bozydar@ecoportal.co.nz"]
11
+
12
+ spec.summary = %q{A collection of helpers for interacting with the ecoPortal MS's V2 API}
13
+ spec.homepage = "https://www.ecoportal.com"
14
+ spec.licenses = %w[MIT]
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 2.1"
24
+ spec.add_development_dependency "rake", "~> 12.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ spec.add_development_dependency "yard", "~> 0.9", ">= 0.9.18"
27
+ spec.add_development_dependency "redcarpet", "~> 3.5", ">= 3.5.0"
28
+ spec.add_development_dependency "pry"
29
+
30
+ spec.add_dependency 'ecoportal-api', '~> 0.5', '>= 0.5.5'
31
+ end
@@ -0,0 +1,10 @@
1
+ require "ecoportal/api"
2
+ require "ecoportal/api/v2/version"
3
+
4
+ module Ecoportal
5
+ module API
6
+ end
7
+ end
8
+
9
+ require "ecoportal/api/common"
10
+ require "ecoportal/api/v2"
@@ -0,0 +1,18 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ end
5
+ end
6
+ end
7
+
8
+ require 'ecoportal/api/common/base_class'
9
+ require 'ecoportal/api/common/hash_diff'
10
+ require 'ecoportal/api/common/base_model'
11
+ require 'ecoportal/api/common/doc_helpers'
12
+ require 'ecoportal/api/common/logging'
13
+ require 'ecoportal/api/common/client'
14
+ require 'ecoportal/api/common/response'
15
+ require 'ecoportal/api/common/wrapped_response'
16
+ require 'ecoportal/api/common/batch_response'
17
+ require 'ecoportal/api/common/batch_operation'
18
+ require 'ecoportal/api/common/content'
@@ -0,0 +1,18 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ end
6
+ end
7
+ end
8
+ end
9
+
10
+ require 'ecoportal/api/common/content/class_helpers'
11
+ require 'ecoportal/api/common/content/string_digest'
12
+ require 'ecoportal/api/common/content/double_model'
13
+ require 'ecoportal/api/common/content/array_model'
14
+ require 'ecoportal/api/common/content/collection_model'
15
+ require 'ecoportal/api/common/content/doc_helpers'
16
+ require 'ecoportal/api/common/content/hash_diff_patch'
17
+ require 'ecoportal/api/common/content/client'
18
+ require 'ecoportal/api/common/content/wrapped_response'
@@ -0,0 +1,287 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ # Class to handle a plain Array embedded in a Hashed model.
6
+ # @note its purpose is to handle an Array of basic objects (i.e. `Date`, `String`, `Number`)
7
+ class ArrayModel < Content::DoubleModel
8
+ class TypeMismatchedComparison < Exception
9
+ def initialize (this: nil, that: msg = "Trying to compare objects with different behavior.")
10
+ if this
11
+ msg += " From object with 'order_matters: #{this.order_matters?}' and 'uniq: #{this.uniq?}'."
12
+ end
13
+ if that
14
+ msg += " To object where 'order_matters: #{that.order_matters?}' and 'uniq: #{that.uniq?}'."
15
+ end
16
+ super(msg)
17
+ end
18
+ end
19
+
20
+ include Enumerable
21
+
22
+ class << self
23
+ attr_accessor :order_matters, :uniq
24
+
25
+ # @param a [ArrayModel]
26
+ # @param b [ArrayModel]
27
+ # @return [Boolean] `true` if both elements have same behaviour
28
+ def same_type?(a, b)
29
+ raise "To use this comparison both objects should be `ArrayModel`" unless a.is_a?(ArrayModel) && b.is_a?(ArrayModel)
30
+ (a.order_matters? == b.order_matters?) && (a.uniq? == b.uniq?)
31
+ end
32
+
33
+ end
34
+
35
+ def initialize(doc = {}, parent: self, key: nil)
36
+ super(doc, parent: parent, key: key)
37
+ end
38
+
39
+ def order_matters?; self.class.order_matters; end
40
+ def uniq?; self.class.uniq; end
41
+
42
+ def length; count; end
43
+ def empty?; count == 0; end
44
+ def present?; count > 0; end
45
+
46
+ def each(&block)
47
+ return to_enum(:each) unless block
48
+ _items.each(&block)
49
+ end
50
+
51
+ # @return [Array] the array element represented by this object
52
+ def _items
53
+ replace_doc([]) unless doc.is_a?(Array)
54
+ doc.tap {|d| d.uniq! if uniq?}
55
+ end
56
+
57
+ # @see #_items
58
+ # @return [Array] a **copy** of the `Array` elements
59
+ def to_a
60
+ _items.slice(0..-1)
61
+ end
62
+
63
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) of the new object
64
+ # @return [ArrayModel] a new object with the current class
65
+ def new_from(value)
66
+ self.class.new(into_a(value))
67
+ end
68
+
69
+ # @return [ArrayModel] a copy of the current object
70
+ def dup
71
+ new_from(to_a)
72
+ end
73
+
74
+ # @return [Integer] the position of the element in the `Array`
75
+ def index(value)
76
+ _items.index(value)
77
+ end
78
+
79
+ # Retrieves the element of a certain position
80
+ # @param pos [Integer] the position of the element
81
+ # @return [Date, String, Number]
82
+ def [](pos)
83
+ _items[pos]
84
+ end
85
+
86
+ # Sets the element of a certain position
87
+ # @param pos [Integer] the position of the element
88
+ # @param value [String, Date, Number] the element
89
+ # @return [Date, String, Number]
90
+ def []=(post, value)
91
+ _items[pos] = value
92
+ on_change
93
+ self[pos]
94
+ end
95
+
96
+ # Compares with an `Array` or another `ArrayModel`
97
+ # @param a [ArrayModel, Array]
98
+ def ==(a)
99
+ return true if self.equal?(a)
100
+ return false unless (a.class == self.class) || a.is_a?(Array)
101
+ case a
102
+ when Array
103
+ self == new_from(a)
104
+ when ArrayModel
105
+ return true if
106
+ raise TypeMismatchedComparison.new(this: self, that: a) unless self.class.same_type?(self, a)
107
+
108
+ if self.order_matters?
109
+ _items == a.to_a
110
+ else
111
+ (_items - a.to_a).empty? && (a.to_a - _items).empty?
112
+ end
113
+ end
114
+ end
115
+
116
+ # @return [Boolean] `true` if `value` is present, `false` otherwise
117
+ def include?(value)
118
+ _items.include?(value)
119
+ end
120
+
121
+ def include_any?(*value)
122
+ value.any? {|v| _items.include?(v)}
123
+ end
124
+
125
+ def include_all?(*value)
126
+ value.all? {|v| _items.include?(v)}
127
+ end
128
+
129
+ # Adds an element to the subjacent `Array`
130
+ # @note if the class variable `uniq` is `true`, it skips duplicates
131
+ def <<(value)
132
+ _items.concat(into_a(value)).tap do |doc|
133
+ doc.uniq! if uniq?
134
+ end
135
+ on_change
136
+ self
137
+ end
138
+
139
+ # @see #<<
140
+ def push!(value)
141
+ self << value
142
+ end
143
+
144
+ # @see #<<
145
+ # @note same as #push! but for multiple elements
146
+ def concat!(values)
147
+ self << values
148
+ end
149
+
150
+ # Resets the `Array` by keeping its reference and adds the value(s)
151
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) to be added
152
+ # @param values [Array]
153
+ def <(values)
154
+ _items.clear
155
+ self << values
156
+ end
157
+
158
+ # Clears the `Array` keeping its reference
159
+ def clear!
160
+ _items.clear
161
+ on_change
162
+ self
163
+ end
164
+
165
+ # Concat to new
166
+ def +(value)
167
+ new_from(self.to_a + into_a(value))
168
+ end
169
+
170
+ # Join
171
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) to be joined
172
+ # @return [ArrayModel] a new object instance with the intersection done
173
+ def |(value)
174
+ new = new_from(value) - self
175
+ new_from(to_a + new.to_a)
176
+ end
177
+
178
+ # Intersect
179
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) to be deleted
180
+ # @return [ArrayModel] a new object instance with the intersection done
181
+ def &(value)
182
+ self.dup.tap do |out|
183
+ self.dup.tap do |delta|
184
+ delta.delete!(*into_a(value))
185
+ out.delete!(*into_a(delta))
186
+ end
187
+ end
188
+ end
189
+
190
+ # Subtract
191
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) to be deleted
192
+ # @return [ArrayModel] a **copy** of the object with the elements subtracted
193
+ def -(value)
194
+ self.dup.tap do |copy|
195
+ copy.delete!(*into_a(value))
196
+ end
197
+ end
198
+
199
+ # Deletes `values` from the `Array`
200
+ def delete!(*values)
201
+ values.map do |v|
202
+ deletion!(v)
203
+ end.tap do |r|
204
+ on_change
205
+ end
206
+ end
207
+
208
+ # Swaps two values' positions
209
+ # @note this will work with first instances when **not** `uniq?`
210
+ # @param val1 [Object] the first value to swap
211
+ # @param val2 [Object] the second value to swap
212
+ # @return [Integer] the new of `value1`, `nil` if it wasn't moved
213
+ def swap(value1, value2)
214
+ index(value2).tap do |dest|
215
+ if dest && pos = index(value1)
216
+ _items[dest] = value1
217
+ _items[pos] = value2
218
+ end
219
+ end
220
+ end
221
+
222
+ # TODO
223
+ def insert_one(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
224
+ i = index(value)
225
+ return i if (i && uniq?)
226
+
227
+ pos = case
228
+ when used_param?(pos)
229
+ pos
230
+ when used_param?(before)
231
+ index(before)
232
+ when used_param?(after)
233
+ if i = index(after)
234
+ i + 1
235
+ end
236
+ else
237
+ length
238
+ end
239
+
240
+ pos.tap do |i|
241
+ unless !i
242
+ _items.insert(pos, value)
243
+ on_change
244
+ end
245
+ end
246
+ end
247
+
248
+ # TODO
249
+ def move(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
250
+ if i = index(value)
251
+ unless i == pos
252
+
253
+ on_change
254
+ end
255
+ pos
256
+ end
257
+ end
258
+
259
+ protected
260
+
261
+ def on_change
262
+ # to be overriden by child classes
263
+ end
264
+
265
+ private
266
+
267
+ def into_a(value)
268
+ raise "Can't convert to 'Array' a 'Hash', as is a key_value pair Enumerable" if value.is_a?(Hash)
269
+ return value.to_a.slice(0..-1) if value.is_a?(Enumerable)
270
+ [].push(value).compact
271
+ end
272
+
273
+ def deletion!(value)
274
+ if !uniq?
275
+ if i = _items.index(value)
276
+ _items.slice!(i)
277
+ end
278
+ else
279
+ _items.delete(value)
280
+ end
281
+ end
282
+
283
+ end
284
+ end
285
+ end
286
+ end
287
+ end