to_collection 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +314 -0
- data/lib/ext/object.rb +6 -0
- data/lib/to_collection.rb +44 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3c2310783afe2173cf5010b1aee2dcce714b525b
|
4
|
+
data.tar.gz: f398002762833fb9aaef65653c94843f88c15ded
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 428fdd1b1bf6b14316e4145e5dd8bdec6979d67c4797196190ad22f6c6a7409c1abf8557b62879321a3b12b5f88990fd0742af4e7871dfd622751035c1ea015a
|
7
|
+
data.tar.gz: 6d333a07fdc5191bcd23f1bf1c6687ab6642c93fb2a7e6b3fcc039467395910848c9f170a9943c39b50a9dd402d5fe7756799c76284ec92cb684c5ac368745b6
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Andy Maleh
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
# `to_collection` v1.0.0
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/to_collection.svg)](http://badge.fury.io/rb/to_collection)
|
3
|
+
[![Build Status](https://travis-ci.org/AndyObtiva/to_collection.svg?branch=master)](https://travis-ci.org/AndyObtiva/to_collection)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/to_collection/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/to_collection?branch=master)
|
5
|
+
|
6
|
+
Treat an array of objects and a singular object uniformly as a collection of objects.
|
7
|
+
Especially useful in processing REST Web Service API JSON responses in a functional approach.
|
8
|
+
|
9
|
+
## Introduction
|
10
|
+
|
11
|
+
Canonicalize data to treat uniformly whether it comes in as a single object or an array of objects, dropping `nils` out automatically.
|
12
|
+
|
13
|
+
API: `object.to_collection(compact)` where `compact` is a boolean for whether to compact collection or not. It is true by default.
|
14
|
+
|
15
|
+
Example:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
city_counts = {}
|
19
|
+
people_http_request.to_collection.each do |person|
|
20
|
+
city_counts[person["city"]] ||= 0
|
21
|
+
city_counts[person["city"]] += 1
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
Wanna keep `nil` values? No problem! Just pass `false` as an argument:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
bad_people_count = 0
|
29
|
+
city_counts = {}
|
30
|
+
people_http_request.to_collection(false).each do |person|
|
31
|
+
if person.nil?
|
32
|
+
bad_people_count += 1
|
33
|
+
else
|
34
|
+
city_counts[person["city"]] ||= 0
|
35
|
+
city_counts[person["city"]] += 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
## Background
|
41
|
+
|
42
|
+
I'm sure you've encountered REST Web Service APIs that operate as follows:
|
43
|
+
|
44
|
+
HTTP Request: =>
|
45
|
+
```
|
46
|
+
GET /people
|
47
|
+
```
|
48
|
+
|
49
|
+
<= 1 person
|
50
|
+
JSON Response:
|
51
|
+
```JSON
|
52
|
+
{"first_name":"karim","last_name":"akram","city":"Dubai"}
|
53
|
+
```
|
54
|
+
|
55
|
+
HTTP Request: =>
|
56
|
+
```
|
57
|
+
GET /people
|
58
|
+
```
|
59
|
+
|
60
|
+
<= 3 people
|
61
|
+
JSON Response:
|
62
|
+
|
63
|
+
```JSON
|
64
|
+
[{"first_name":"karim","last_name":"akram","city":"Dubai"}, {"first_name":"muhsen","last_name":"asaad","city":"Amman"}, {"first_name":"assaf","last_name":"munir","city":"Qatar"}]
|
65
|
+
```
|
66
|
+
|
67
|
+
How do you work with the varied JSON responses in Ruby?
|
68
|
+
|
69
|
+
One approach for an app that needs to count people in cities:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
city_counts = {}
|
73
|
+
json_response = people_http_request
|
74
|
+
if json_response.is_a?(Hash)
|
75
|
+
city_counts[json_response["city"]] ||= 0
|
76
|
+
city_counts[json_response["city"] += 1
|
77
|
+
elsif json_response.is_a?(Array)
|
78
|
+
json_response.each do |person|
|
79
|
+
city_counts[person["city"]] ||= 0
|
80
|
+
city_counts[person["city"]] += 1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
Not only is the code above repetitive (unDRY) and complicated, but it also breaks common Ruby and object oriented development standards by relying on explicit type checking instead of duck-typing, polymorphism, or design patterns.
|
86
|
+
|
87
|
+
A slightly better version relying on duck-typing would be:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
city_counts = {}
|
91
|
+
json_response = people_http_request
|
92
|
+
if json_response.respond_to?(:each_pair)
|
93
|
+
city_counts[json_response["city"]] ||= 0
|
94
|
+
city_counts[json_response["city"] += 1
|
95
|
+
elsif json_response.respond_to?(:each_index)
|
96
|
+
json_response.each do |person|
|
97
|
+
city_counts[person["city"]] ||= 0
|
98
|
+
city_counts[person["city"]] += 1
|
99
|
+
end
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
A slightly clearer version relying on design patterns (Strategy) and parametric polymorphism (functional) would be:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
city_counts = {}
|
107
|
+
city_counting_strategies = {
|
108
|
+
Hash: -> { |json_response|
|
109
|
+
city_counts[json_response["city"]] ||= 0
|
110
|
+
city_counts[json_response["city"] += 1
|
111
|
+
},
|
112
|
+
Array: -> { |json_response|
|
113
|
+
json_response.each do |person|
|
114
|
+
city_counts[person["city"]] ||= 0
|
115
|
+
city_counts[person["city"]] += 1
|
116
|
+
end
|
117
|
+
}
|
118
|
+
}
|
119
|
+
json_response = people_http_request
|
120
|
+
city_counting_strategies[json_response.class].call(json_response)
|
121
|
+
```
|
122
|
+
|
123
|
+
A more radical version relying on object-oriented polymorphism and Ruby open-classes would be:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
Hash.class_eval do
|
127
|
+
def process_json_response(&processor)
|
128
|
+
processor.call(self)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
Array.class_eval do
|
133
|
+
def process_json_response(&processor)
|
134
|
+
each(&processor)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
city_counts = {}
|
139
|
+
json_response = people_http_request
|
140
|
+
json_response.process_json_response do |person|
|
141
|
+
city_counts[person["city"]] ||= 0
|
142
|
+
city_counts[person["city"]] += 1
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
This version is quite elegant, clear, and Ruby idiomatic, but aren't we using a Nuclear device against a fly that sometimes comes as a swarm of flies? I'm sure we can have a much simpler solution, especially in a language like Ruby.
|
147
|
+
|
148
|
+
Well, how about this functional solution?
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
city_counts = {}
|
152
|
+
[people_http_request].flatten.each do |person|
|
153
|
+
city_counts[person["city"]] ||= 0
|
154
|
+
city_counts[person["city"]] += 1
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
Yes, hybrid functional/object-oriented programming to the rescue.
|
159
|
+
|
160
|
+
One may wonder what to do if the response comes in as nil or includes nil values in an array. Well, this approach can scale to handle that too should ignoring nil be the requirement.
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
city_counts = {}
|
164
|
+
[people_http_request].flatten.compact.each do |person|
|
165
|
+
city_counts[person["city"]] ||= 0
|
166
|
+
city_counts[person["city"]] += 1
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
170
|
+
Can we generalize this elegant solution beyond counting cities? After all, the key problem with the code on top is it gets quite expensive to maintain in a real-world production app containing many integrations with REST Web Service APIs.
|
171
|
+
|
172
|
+
This functional generalization should work by allowing you to switch json_response variable and process_json_response proc anyway you want:
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
[json_response].flatten.compact.each(&:process_json_response)
|
176
|
+
```
|
177
|
+
|
178
|
+
Example:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
[cities_json_response].flatten.compact.each(&:group_by_country)
|
182
|
+
```
|
183
|
+
|
184
|
+
How about go one step further and bake this into all objects using our previous approach of object-oriented polymorphism and Ruby open-classes? That way, we don't just collapse the difference between dealing with arrays of hashes vs hashes but also arrays of objects vs singular objects by adding. Note the use of flatten(1) below to prevent arrays or arrays from collapsing more than one level.
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
Object.class_eval do
|
188
|
+
def to_collection
|
189
|
+
[self].flatten(1).compact
|
190
|
+
end
|
191
|
+
end
|
192
|
+
```
|
193
|
+
|
194
|
+
Example usage (notice how more readable this is than the explicit version above by hiding flatten and compact):
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
city_counts = {}
|
198
|
+
people_http_request.to_collection.each do |person|
|
199
|
+
city_counts[person["city"]] ||= 0
|
200
|
+
city_counts[person["city"]] += 1
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
A refactored version including optional compacting would be:
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
Object.class_eval do
|
208
|
+
def to_collection(compact=true)
|
209
|
+
collection = [self].flatten(1)
|
210
|
+
compact ? collection.compact : collection
|
211
|
+
end
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
Example usage of `to_collection(compact)` to count bad person hashes coming as nil:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
bad_people_count = 0
|
219
|
+
city_counts = {}
|
220
|
+
people_http_request.to_collection(false).each do |person|
|
221
|
+
if person.nil?
|
222
|
+
bad_people_count += 1
|
223
|
+
else
|
224
|
+
city_counts[person["city"]] ||= 0
|
225
|
+
city_counts[person["city"]] += 1
|
226
|
+
end
|
227
|
+
end
|
228
|
+
```
|
229
|
+
|
230
|
+
You asked for "Elegant" didn't you? I hope that was what you were looking for.
|
231
|
+
|
232
|
+
## How It Works
|
233
|
+
|
234
|
+
A [super_module](https://github.com/AndyObtiva/super_module) called `ToCollection`
|
235
|
+
contains the `#to_collection` method and is included (mixed) into `Object`, providing
|
236
|
+
`#to_collection` method to inheriting classes.
|
237
|
+
|
238
|
+
## Options
|
239
|
+
|
240
|
+
### `Object.to_collection_already_implemented_strategy`
|
241
|
+
Possible Values: `"raise_error"` (default), `"keep"`, `"overwrite"`
|
242
|
+
|
243
|
+
Setting this option allows developer to configure handling of the case when
|
244
|
+
`Object#to_collection` already exists before loading `to_collection` library.
|
245
|
+
|
246
|
+
#### `"raise_error"` (default)
|
247
|
+
|
248
|
+
For safety reasons, the library will raise AlreadyImplementedError by default to
|
249
|
+
alert the developer and provide information about the other options.
|
250
|
+
This prevents later surprises and puts control in the hand of the developer to
|
251
|
+
responsibly decide what option to pick next.
|
252
|
+
|
253
|
+
#### `"keep"`
|
254
|
+
|
255
|
+
This keeps existing `to_collection` untouched, disabling this library.
|
256
|
+
|
257
|
+
#### `"overwrite"`
|
258
|
+
|
259
|
+
This overwrites existing `to_collection` method, fully enabling this library.
|
260
|
+
|
261
|
+
### `ENV['TO_COLLECTION_ALREADY_IMPLEMENTED_STRATEGY']`
|
262
|
+
Possible Values: `"raise_error"` (default), `"keep"`, `"overwrite"`
|
263
|
+
|
264
|
+
Same function as `Object.to_collection_already_implemented_strategy`.
|
265
|
+
Environment variable takes precedence over class accessor variable though.
|
266
|
+
|
267
|
+
### `ENV['TO_COLLECTION_OBJECT_INCLUDE']`
|
268
|
+
Possible Values: `"true"` (default), `"false"`
|
269
|
+
|
270
|
+
Must be set before requiring/loading library. When using bundler, ensure `require` option is set to `false` or `nil`.
|
271
|
+
|
272
|
+
`ToCollection` [super_module](https://github.com/AndyObtiva/super_module) is automatically included in `Object` except when `ENV['TO_COLLECTION_OBJECT_INCLUDE']` is set to `"false"`, providing developer with the option to **manually** include (mix in) `ToCollection` [super_module](https://github.com/AndyObtiva/super_module) into classes that need it.
|
273
|
+
|
274
|
+
Example:
|
275
|
+
|
276
|
+
Bundler would have gem require option as false:
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
require 'to_collection', require: false
|
280
|
+
```
|
281
|
+
|
282
|
+
Ruby code would then set that environment variable **manually** before requiring library:
|
283
|
+
|
284
|
+
```ruby
|
285
|
+
ENV['TO_COLLECTION_OBJECT_INCLUDE'] = false
|
286
|
+
require 'to_collection'
|
287
|
+
Hash.instance_eval do
|
288
|
+
include ToCollection #enables to_collection method
|
289
|
+
end
|
290
|
+
Array.instance_eval do
|
291
|
+
include ToCollection #enables to_collection method
|
292
|
+
end
|
293
|
+
response_data = people_http_request #returns single hash or array of hashes
|
294
|
+
response_data.to_collection.each do |person_hash|
|
295
|
+
# do some work
|
296
|
+
end
|
297
|
+
```
|
298
|
+
|
299
|
+
## Contributing
|
300
|
+
|
301
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
302
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
303
|
+
* Fork the project.
|
304
|
+
* Start a feature/bugfix branch.
|
305
|
+
* `gem install bundler`
|
306
|
+
* `bundle`
|
307
|
+
* Commit and push until you are happy with your contribution.
|
308
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. Also, do not upgrade `jeweler`. It is intentionally at an old version that is compatible with running tests in Travis with older verison of Ruby as well as supporting Coveralls, Simplecov, and Code Climate.
|
309
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
310
|
+
|
311
|
+
## Copyright
|
312
|
+
|
313
|
+
Copyright (c) 2017 Andy Maleh. See LICENSE.txt for
|
314
|
+
further details.
|
data/lib/ext/object.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'super_module'
|
2
|
+
|
3
|
+
super_module :ToCollection do
|
4
|
+
class AlreadyImplementedError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.to_collection_already_implemented_strategy=(strategy)
|
8
|
+
@to_collection_already_implemented_strategy = strategy
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.to_collection_already_implemented_strategy
|
12
|
+
(ENV['TO_COLLECTION_ALREADY_IMPLEMENTED_STRATEGY'] ||
|
13
|
+
@to_collection_already_implemented_strategy ||
|
14
|
+
:raise_error).to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def __to_collection__(compact=true)
|
18
|
+
collection = [self].flatten(1)
|
19
|
+
compact ? collection.compact : collection
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.define_to_collection_method
|
23
|
+
define_method(:to_collection, instance_method(:__to_collection__))
|
24
|
+
send(:remove_method, :__to_collection__)
|
25
|
+
end
|
26
|
+
|
27
|
+
case self.to_collection_already_implemented_strategy
|
28
|
+
when :raise_error
|
29
|
+
begin
|
30
|
+
instance_method(:to_collection)
|
31
|
+
message = "#to_collection is already implemented on Object. Please specify Object.to_collection_already_implemented_strategy or ENV['TO_COLLECTION_ALREADY_IMPLEMENTED_STRATEGY'] as :keep or :overwrite to handle this appropriately."
|
32
|
+
raise AlreadyImplementedError.new(message)
|
33
|
+
rescue NameError
|
34
|
+
self.define_to_collection_method
|
35
|
+
end
|
36
|
+
when :keep
|
37
|
+
# do nothing
|
38
|
+
when :overwrite
|
39
|
+
self.define_to_collection_method
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
require File.expand_path(File.join(__FILE__, '..', 'ext', 'object'))
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: to_collection
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andy Maleh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: super_module
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jeweler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.3.3
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.3.3
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: coveralls
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.8.19
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.8.19
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.5.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.5.0
|
69
|
+
description: "\n Treat an array of objects and a singular object uniformly as a
|
70
|
+
collection of objects. Especially useful in processing REST Web Service API JSON
|
71
|
+
responses in a functional approach.\n\n Canonicalize data to treat uniformly
|
72
|
+
whether it comes in as a single object or an array of objects, dropping `nils` out
|
73
|
+
automatically.\n\n API: `object.to_collection(compact)` where `compact` is a
|
74
|
+
boolean for whether to compact collection or not. It is true by default.\n\n Example:\n\n
|
75
|
+
\ ```ruby\n city_counts = {}\n people_http_request.to_collection.each do
|
76
|
+
|person|\n city_counts[person[\"city\"]] ||= 0\n city_counts[person[\"city\"]]
|
77
|
+
+= 1\n end\n ```\n\n Wanna keep `nil` values? No problem! Just pass `false`
|
78
|
+
as an argument:\n\n ```ruby\n bad_people_count = 0\n city_counts = {}\n
|
79
|
+
\ people_http_request.to_collection(false).each do |person|\n if person.nil?\n
|
80
|
+
\ bad_people_count += 1\n else\n city_counts[person[\"city\"]]
|
81
|
+
||= 0\n city_counts[person[\"city\"]] += 1\n end\n end\n ```\n
|
82
|
+
\ "
|
83
|
+
email: andy.am@gmail.com
|
84
|
+
executables: []
|
85
|
+
extensions: []
|
86
|
+
extra_rdoc_files:
|
87
|
+
- LICENSE
|
88
|
+
- README.md
|
89
|
+
files:
|
90
|
+
- LICENSE
|
91
|
+
- README.md
|
92
|
+
- lib/ext/object.rb
|
93
|
+
- lib/to_collection.rb
|
94
|
+
homepage: http://github.com/AndyObtiva/to_collection
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.6.10
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Treat an array of objects and a singular object uniformly as a collection
|
118
|
+
of objects
|
119
|
+
test_files: []
|