gorillib 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
1
+ # gorillib's structured data classes -- Record, Model, Builder and Bucket
2
+
3
+ ## Overview
4
+
5
+ Gorillib provides these general flavors of model:
6
+
7
+ * `Gorillib::Record`: **lightweight structured records**. Easily assemble a dynamic data structure that enables both rich behavior and generic manipulation, serialization and transformation. Especially useful when you just need to pull something from JSON, attach some functionality, and get it back on the wire.
8
+
9
+ ```ruby
10
+ class Place
11
+ include Gorillib::Record
12
+ # fields can be simple...
13
+ field :name, String
14
+ field :country_id, String, :doc => 'Country code (2-letter alpha) containing the place'
15
+ # ... or complext
16
+ field :geo, GeoCoordinates, :doc => 'geographic location of the place'
17
+ end
18
+
19
+ class GeoCoordinates
20
+ include Gorillib::Record
21
+ field :latitude, Float, :doc => 'latitude in decimal degrees; negative numbers are south of the equator'
22
+ field :longitude, Float, :doc => 'longitude in decimal degrees; negative numbers are west of Greenwich'
23
+ end
24
+
25
+ # It's simple to instantiate complex nested data structures
26
+ lunch_spot = Place.receive({ :name => "Torchy's Tacos", :country_id => "us",
27
+ :geo => { :latitude => "30.295", :longitude => "-97.745" }})
28
+ ```
29
+
30
+ * `Gorillib::Model`: **rich structured models** offering predictable magic with a disciplined footprint. Comparable to ActiveRecord or Datamapper, but for a world dominated by JSON+HTTP, not relational databases
31
+
32
+ ```ruby
33
+ class GeoCoordinates
34
+ include Gorillib::Model
35
+ field :latitude, Float, :doc => 'latitude in decimal degrees; negative numbers are south of the equator', :validates => { :numericality => { :>= => -90, :<= => 90 } }
36
+ field :longitude, Float, :doc => 'longitude in decimal degrees; negative numbers are west of Greenwich', :validates => { :numericality => { :>= => -180, :<= => 180 } }
37
+ end
38
+ position = GeoCoordinates.from_tuple(30.295, -97.745)
39
+ # A Gorillib::Model obeys the ActiveModel contract
40
+ GeoCoordinates.model_name.human # => 'Geo coordinates'
41
+ ```
42
+
43
+ * `Gorillib::Builder`: **foundation models for elegant ruby DSLs** (Domain-Specific Languages). Relaxes ruby syntax to enable highly-readable specification of complex behavior.
44
+
45
+ ```ruby
46
+ workflow(:bake_pie) do
47
+ step :make_crust
48
+ step :add_filling
49
+ step :bake
50
+ step :cool
51
+ end
52
+ ```
53
+
54
+ * `Gorillib::Bucket` (?name?): **record-style access to freeform hashes**. Provide a disciplined interface on top of arbitrarily-structured data. Used by [[Configliere]] and others.
55
+
56
+ ```ruby
57
+ # pre-defining a field gives you special powers...
58
+ Settings.define :port, Integer, :doc => 'API server port number', :default => 80
59
+ # but you can still store or read anything you'd like...
60
+ Settings[:shout] = "SAN DIMAS HIGH SCHOOL FOOTBALL RULES"
61
+ # and treat the object as a hash when you'd like to
62
+ conn = Connections.open(Settings.merge(user_overrides))
63
+ ```
64
+
65
+ ## Decisions
66
+
67
+ * **initializer** - *does not* inject an initializer; if you want one, do `alias_method :initialize, :receive!`
68
+ * **frills** --
69
+ - *does inject*: `inspect` on the class, `inspect`, `to_s` on the instance
70
+ - *does inject*: accessors (`foo`, `foo=`) for each field.
71
+ - *does inject*: `==` on the instance
72
+ - *does not define*: `schema`, `initialize`
73
+ * **field defaults** - evaluated on first read, at which point its value is fixed on the record.
74
+
75
+ * **define_metamodel_record** -- visibility=false does not remove an existing method from the metamodel
76
+
77
+ * ?? **hash vs mash** -- does `attributes` return a mash or hash?
78
+ * are these basic functionality:
79
+ - **extra_attributes** -- ??
80
+ - **default** -- ??
81
+
82
+ * Record:
83
+ - class methods -- `field`, `fields`, `field_names`, `has_field?`, `metamodel`, `receive`, `inspect`
84
+ - instance methods -- `read_attribute`, `write_attribute`, `unset_attribute`, `attribute_set?`, `attributes`, `receive!`, `update`, `inspect`, `to_s`, `==`
85
+ - with each attribute -- `receive_foo`, `foo=`, `foo`
86
+
87
+ * Builder:
88
+ - defining classes before they're used
89
+
90
+
91
+ ## Features
92
+
93
+ ### `Record`
94
+
95
+ * `Record::Ordered` -- sort order on fields (and thus record)
96
+ * `Record::FieldAliasing` -- aliases for fields and receivers
97
+ * `Record::Defaults` -- default values
98
+ * `Record::Schema` -- icss schema
99
+ * `Record::HashAccessors` -- adds `[]`, `[]=`, `delete`, `keys`, `#has_key?`, `to_hash`, and `update`. This allows it to be `hashlike`, but you must include that explicitly.
100
+ * `Record::Hashlike` -- mixes in `Record::HashAccessors` and `Gorillib::Hashlike`, making it behave in almost every respect like a hash. Use this when you want something that will *behave* like a hash but *be* a record. If you want something to *be* a hash but *behave* like a record, use the `Gorillib::MashRecord`
101
+
102
+ ### `Builder`
103
+
104
+ * `Builder::GetsetField` --
105
+
106
+ ### `HashRecord`
107
+
108
+ ### `Model`
109
+
110
+ * `Model::Naming`
111
+ * `Model::Conversion` --
112
+
113
+ ### active_model / active_model_lite
114
+
115
+ From `active_model` or `active_model_lite`:
116
+
117
+ * `Model::Callbacks` --
118
+ * `Model::Dirty` --
119
+ * `Model::Serialization` --
120
+ * `Model::Validations` -- implies `Model::Errors`, `Model::Callbacks`
121
+
122
+
123
+ ## Why, in a world with ActiveRecord, Datamapper, Hashie, Struct, ..., do we need yet another damn model framework?
124
+
125
+ ActiveRecord and Datamapper excel in a world where data (and truth) live in the database. ActiveRecord sets the standard for elegant magic, but is fairly heavyweight (I don't want to include a full XML serialization suite just so I can validate records). This often means it's overkill for the myriad flyweight scripts, Goliath apps, and such that we deploy. Datamapper does a remarkable job of delivering power while still being light on its toes, but ultimately is too tightly bound to an ORM view of the world. Hashie, Structs and OStructs behave as both hashes and records. In my experience, this interface is too generous -- their use leads to mealymouthed code.
126
+
127
+ More importantly, our data spends most of its time on the wire or being handled as an opaque blob of data; a good amount of time being handled as a generic bundle of properties; and (though most important) a relatively small amount of time as an active, assertive object. So type conversion and validation are fundamental actions, but shouldn't crud up my critical path or be required. Models should offer predictable and disciplined features, but be accessable as generic bags of facts.
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'gorillib/array/hashify'
3
+
4
+ describe Array, :simple_spec, :only do
5
+
6
+ describe '#hashify' do
7
+ it 'returns a hash pairing elements with value from block' do
8
+ [1,2,3].hashify{|x| x * x }.should == { 1 => 1, 2 => 4, 3 => 9 }
9
+ [1,2,3].hashify{|x| x > 2 ? nil : x }.should == { 1 => 1, 2 => 2, 3 => nil }
10
+ end
11
+ it "returns an empty hash on an empty array" do
12
+ [].hashify{}.should == {}
13
+ end
14
+ it "fails if no block given" do
15
+ expect{ [1,2,3].hashify }.to raise_error(ArgumentError)
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -17,8 +17,8 @@ describe Gorillib::Builder, :model_spec => true, :builder_spec => true do
17
17
  context 'examples:' do
18
18
  let(:subject_class ){ car_class }
19
19
  it 'type-converts values' do
20
- obj = subject_class.receive( :name => 'wildcat', :make_model => 'Buick Wildcat', :year => "1968", :doors => "2" )
21
- obj.attributes.should == { :name => :wildcat, :make_model => 'Buick Wildcat', :year => 1968, :doors => 2, :engine => nil }
20
+ obj = subject_class.receive( :name => 'wildcat', :make_model => 'Buick Wildcat', :year => "1968", :doors => "2" )
21
+ obj.attributes.should == { :name => :wildcat, :make_model => 'Buick Wildcat', :year => 1968, :doors => 2, :engine => nil }
22
22
  end
23
23
  it 'handles nested structures' do
24
24
  obj = subject_class.receive(
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- # require 'support/factory_test_helpers'
2
+ require 'support/factory_test_helpers'
3
3
 
4
4
  require 'gorillib/object/blank'
5
5
  require 'gorillib/object/try_dup'
@@ -8,11 +8,9 @@ require 'gorillib/metaprogramming/class_attribute'
8
8
  require 'gorillib/string/inflector'
9
9
 
10
10
  require 'gorillib/collection'
11
- require 'gorillib/model/factories'
11
+ require 'gorillib/factories'
12
12
 
13
- require 'factory_test_helpers'
14
-
15
- describe '', :model_spec, :factory_spec, :only do
13
+ describe '', :model_spec, :factory_spec do
16
14
 
17
15
  describe Gorillib::Factory do
18
16
  describe 'Factory()' do
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require 'support/model_test_helpers'
3
+
4
+ require 'multi_json'
5
+ #
6
+ require 'gorillib/model'
7
+ require 'gorillib/builder'
8
+ require 'gorillib/model/serialization/tsv'
9
+
10
+ describe Gorillib::Model, :model_spec, :builder_spec do
11
+
12
+ context ".load_tsv" do
13
+ # it "respects blank characters at end of line, so '1\\t2\\t\\t\\t becomes [\"1\",\"2\",\"\",\"\",\"\"]" do
14
+ # # make sure
15
+ # end
16
+ end
17
+ end
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+ require 'support/factory_test_helpers'
3
+ require 'gorillib/object/blank'
4
+ #
5
+ require 'gorillib/type/ip_address'
6
+
7
+ shared_examples_for(:ip_addresslike) do
8
+
9
+ its(:dotted){ should == '1.2.3.4' }
10
+ its(:quads){ should == [1, 2, 3, 4] }
11
+ its(:packed){ should == packed_1234 }
12
+
13
+ context '.from_packed' do
14
+ subject{ described_class.from_packed(packed_1234) }
15
+ it{ should be_a(described_class) }
16
+ its(:dotted){ should == '1.2.3.4' }
17
+ end
18
+
19
+ context '#bitness_min' do
20
+ it('is unchanged for /32'){ subject.bitness_min(32).should == subject.packed }
21
+ { 0 => 0, # '0.0.0.0'
22
+ 6 => 0, # '0.0.0.0'
23
+ 8 => 0x01000000, # '1.0.0.0',
24
+ 30 => 0x01020304, # '1.2.3.4',
25
+ }.each do |bitness, result|
26
+ it("returns #{result} for #{bitness}"){ subject.bitness_min(bitness).should == result }
27
+ end
28
+ it 'raises an error if bitness is out of range' do
29
+ expect{ subject.bitness_min(33) }.to raise_error(ArgumentError, /only 32 bits.*33/)
30
+ expect{ subject.bitness_min(-1) }.to raise_error(ArgumentError, /only 32 bits.*-1/)
31
+ expect{ subject.bitness_min('bob') }.to raise_error(ArgumentError, /only 32 bits.*bob/)
32
+ end
33
+ end
34
+
35
+ context '#bitness_max' do
36
+ it 'returns an integer' do
37
+ subject.bitness_max(24).should equal(16909311)
38
+ end
39
+ it('is unchanged for /32' ){ subject.bitness_max(32).should == subject.packed }
40
+ { 0 => 0xFFFFFFFF, # '255.255.255.255'
41
+ 6 => 0x03FFFFFF, # '3.255.255.255'
42
+ 8 => 0x01FFFFFF, # '1.255.255.255'
43
+ 31 => 0x01020305, # '1.2.3.5'
44
+ }.each do |bitness, result|
45
+ it("returns #{result} for #{bitness}"){ subject.bitness_max(bitness).should == result }
46
+ end
47
+ it 'raises an error if bitness is out of range' do
48
+ expect{ subject.bitness_max(33) }.to raise_error(ArgumentError, /only 32 bits.*33/)
49
+ expect{ subject.bitness_max(-1) }.to raise_error(ArgumentError, /only 32 bits.*-1/)
50
+ expect{ subject.bitness_max('bob') }.to raise_error(ArgumentError, /only 32 bits.*bob/)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe ::IpAddress do
56
+ let(:packed_1234){ 16909060 }
57
+ let(:dotted_1234){ '1.2.3.4' }
58
+ subject{ described_class.new('1.2.3.4') }
59
+
60
+ it_should_behave_like(:ip_addresslike)
61
+
62
+ its(:to_i){ should equal(packed_1234) }
63
+ it{ expect{ subject.to_int }.to raise_error(NoMethodError, /undefined method.*\`to_int\'/) }
64
+ end
65
+
66
+ describe ::IpNumeric do
67
+ let(:packed_1234){ 16909060 }
68
+ let(:dotted_1234){ '1.2.3.4' }
69
+ subject{ described_class.new(packed_1234) }
70
+
71
+ it_should_behave_like(:ip_addresslike)
72
+
73
+ context '.from_dotted' do
74
+ subject{ described_class.from_dotted(dotted_1234) }
75
+ it('memoizes #dotted') do
76
+ val = subject.instance_variable_get('@dotted')
77
+ val.should == '1.2.3.4'
78
+ val.should be_frozen
79
+ end
80
+ it('memoizes #quads') do
81
+ val = subject.instance_variable_get('@quads')
82
+ val.should == [1,2,3,4]
83
+ val.should be_frozen
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+
90
+ describe ::IpRange do
91
+ let(:beg_1234){ 16909060 }
92
+ let(:end_1234){ 16909384 }
93
+ subject{ described_class.new(beg_1234 .. end_1234) }
94
+
95
+ its(:min){ should == beg_1234 }
96
+ its(:max){ should == end_1234 }
97
+ its(:min){ should be_a(IpNumeric) }
98
+ its(:max){ should be_a(IpNumeric) }
99
+
100
+ context 'bitness_blocks' do
101
+ it 'emits nothing if empty' do
102
+ described_class.new(50, 49).bitness_blocks(24).should == []
103
+ end
104
+ it 'emits only one block if min and max are in same bitness block' do
105
+ described_class.new(40, 80).bitness_blocks(24).should == [ [40, 80] ]
106
+ end
107
+ it 'emits only one block if min and max are the same' do
108
+ described_class.new(40, 40).bitness_blocks(24).should == [ [40, 40] ]
109
+ end
110
+ it 'emits two short blocks if min and max are in neighboring blocks' do
111
+ described_class.new(40, 260).bitness_blocks(24).should == [ [40, 255], [256, 260] ]
112
+ end
113
+ it 'emits intermediate blocks if min and max are far apart' do
114
+ described_class.new(40, 520).bitness_blocks(24).should == [ [40, 255], [256, 511], [512, 520] ]
115
+ end
116
+
117
+ {
118
+ [ '255.255.255.255', '255.255.255.255', 0 ] => [ [0xFFFFFFFF, 0xFFFFFFFF] ],
119
+ [ '255.255.255.255', '255.255.255.255', 24 ] => [ [0xFFFFFFFF, 0xFFFFFFFF] ],
120
+ [ '255.255.255.255', '255.255.255.255', 32 ] => [ [0xFFFFFFFF, 0xFFFFFFFF] ],
121
+ [ '255.255.254.1', '255.255.255.255', 0 ] => [ [0xFFFFFe01, 0xFFFFFFFF] ],
122
+ [ '255.255.254.1', '255.255.255.255', 24 ] => [ [0xFFFFFe01, 0xFFFFFeFF], [0xFFFFFF00, 0xFFFFFFFF] ],
123
+ [ '255.255.253.1', '255.255.255.7', 24 ] => [ [0xFFFFFd01, 0xFFFFFdFF], [0xFFFFFe00, 0xFFFFFeFF], [0xFFFFFF00, 0xFFFFFF07] ],
124
+ [ '0.0.0.0', '0.0.0.0', 24 ] => [ [0x0, 0x0], ],
125
+ [ '0.0.0.255', '0.0.1.0', 24 ] => [ [0xFF, 0xFF], [0x100, 0x100] ],
126
+ [ '0.0.0.7', '0.0.0.10', 31 ] => [ [0x7, 0x7], [0x8, 0x9], [0xa, 0xa], ],
127
+ [ '0.0.0.7', '0.0.0.11', 31 ] => [ [0x7, 0x7], [0x8, 0x9], [0xa, 0xb], ],
128
+ [ '0.0.0.0', '0.0.0.0', 24 ] => [ [0x0, 0x0], ],
129
+ [ '0.0.1.7', '0.0.1.255', 24 ] => [ [0x107, 0x1FF], ],
130
+ [ '0.0.1.7', '0.0.2.7', 24 ] => [ [0x107, 0x1FF], [0x200, 0x207] ],
131
+ [ '0.0.1.7', '0.0.3.7', 24 ] => [ [0x107, 0x1FF], [0x200, 0x2FF], [0x300, 0x307] ],
132
+ }.each do |(beg_ip, end_ip, bitness), blocks|
133
+ it "/#{bitness}-bit blocks of #{beg_ip}..#{end_ip} are #{blocks}" do
134
+ beg_ip = IpNumeric.from_dotted(beg_ip)
135
+ end_ip = IpNumeric.from_dotted(end_ip)
136
+ subject = IpRange.new(beg_ip .. end_ip)
137
+ subject.bitness_blocks(bitness).should == blocks
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: gorillib
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.4.2
5
+ version: 0.5.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Infochimps
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-09-06 00:00:00 Z
13
+ date: 2012-10-22 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: multi_json
@@ -118,8 +118,18 @@ executables: []
118
118
  extensions: []
119
119
 
120
120
  extra_rdoc_files:
121
+ - CHANGELOG.md
121
122
  - LICENSE.md
122
123
  - README.md
124
+ - TODO.md
125
+ - notes/HOWTO.md
126
+ - notes/bucket.md
127
+ - notes/builder.md
128
+ - notes/collection.md
129
+ - notes/factories.md
130
+ - notes/model-overlay.md
131
+ - notes/model.md
132
+ - notes/structured-data-classes.md
123
133
  files:
124
134
  - .gitignore
125
135
  - .rspec
@@ -141,6 +151,7 @@ files:
141
151
  - lib/gorillib/array/compact_blank.rb
142
152
  - lib/gorillib/array/deep_compact.rb
143
153
  - lib/gorillib/array/extract_options.rb
154
+ - lib/gorillib/array/hashify.rb
144
155
  - lib/gorillib/array/simple_statistics.rb
145
156
  - lib/gorillib/array/wrap.rb
146
157
  - lib/gorillib/base.rb
@@ -203,12 +214,16 @@ files:
203
214
  - lib/gorillib/model/schema_magic.rb
204
215
  - lib/gorillib/model/serialization.rb
205
216
  - lib/gorillib/model/serialization/csv.rb
217
+ - lib/gorillib/model/serialization/json.rb
218
+ - lib/gorillib/model/serialization/lines.rb
219
+ - lib/gorillib/model/serialization/tsv.rb
206
220
  - lib/gorillib/model/validate.rb
207
221
  - lib/gorillib/numeric/clamp.rb
208
222
  - lib/gorillib/object/blank.rb
209
223
  - lib/gorillib/object/try.rb
210
224
  - lib/gorillib/object/try_dup.rb
211
225
  - lib/gorillib/pathname.rb
226
+ - lib/gorillib/pathname/utils.rb
212
227
  - lib/gorillib/serialization/to_wire.rb
213
228
  - lib/gorillib/some.rb
214
229
  - lib/gorillib/string/constantize.rb
@@ -219,16 +234,26 @@ files:
219
234
  - lib/gorillib/string/truncate.rb
220
235
  - lib/gorillib/type/boolean.rb
221
236
  - lib/gorillib/type/extended.rb
237
+ - lib/gorillib/type/ip_address.rb
222
238
  - lib/gorillib/type/url.rb
223
239
  - lib/gorillib/utils/capture_output.rb
224
240
  - lib/gorillib/utils/console.rb
225
241
  - lib/gorillib/utils/edge_cases.rb
226
242
  - lib/gorillib/utils/nuke_constants.rb
243
+ - notes/HOWTO.md
244
+ - notes/bucket.md
245
+ - notes/builder.md
246
+ - notes/collection.md
247
+ - notes/factories.md
248
+ - notes/model-overlay.md
249
+ - notes/model.md
250
+ - notes/structured-data-classes.md
227
251
  - spec/examples/builder/ironfan_spec.rb
228
252
  - spec/extlib/hash_spec.rb
229
253
  - spec/extlib/mash_spec.rb
230
254
  - spec/gorillib/array/compact_blank_spec.rb
231
255
  - spec/gorillib/array/extract_options_spec.rb
256
+ - spec/gorillib/array/hashify_spec.rb
232
257
  - spec/gorillib/array/simple_statistics_spec.rb
233
258
  - spec/gorillib/builder_spec.rb
234
259
  - spec/gorillib/collection_spec.rb
@@ -237,6 +262,7 @@ files:
237
262
  - spec/gorillib/datetime/to_flat_spec.rb
238
263
  - spec/gorillib/enumerable/sum_spec.rb
239
264
  - spec/gorillib/exception/raisers_spec.rb
265
+ - spec/gorillib/factories_spec.rb
240
266
  - spec/gorillib/hash/compact_spec.rb
241
267
  - spec/gorillib/hash/deep_compact_spec.rb
242
268
  - spec/gorillib/hash/deep_merge_spec.rb
@@ -254,9 +280,9 @@ files:
254
280
  - spec/gorillib/metaprogramming/delegation_spec.rb
255
281
  - spec/gorillib/metaprogramming/singleton_class_spec.rb
256
282
  - spec/gorillib/model/defaults_spec.rb
257
- - spec/gorillib/model/factories_spec.rb
258
283
  - spec/gorillib/model/lint_spec.rb
259
284
  - spec/gorillib/model/overlay_spec.rb
285
+ - spec/gorillib/model/serialization/tsv_spec.rb
260
286
  - spec/gorillib/model/serialization_spec.rb
261
287
  - spec/gorillib/model_spec.rb
262
288
  - spec/gorillib/numeric/clamp_spec.rb
@@ -270,6 +296,7 @@ files:
270
296
  - spec/gorillib/string/inflector_test_cases.rb
271
297
  - spec/gorillib/string/truncate_spec.rb
272
298
  - spec/gorillib/type/extended_spec.rb
299
+ - spec/gorillib/type/ip_address_spec.rb
273
300
  - spec/gorillib/utils/capture_output_spec.rb
274
301
  - spec/spec_helper.rb
275
302
  - spec/support/factory_test_helpers.rb
@@ -297,7 +324,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
297
324
  requirements:
298
325
  - - ">="
299
326
  - !ruby/object:Gem::Version
300
- hash: -2648422057706542462
327
+ hash: 4018535107500327818
301
328
  segments:
302
329
  - 0
303
330
  version: "0"
@@ -345,6 +372,7 @@ test_files:
345
372
  - spec/gorillib/utils/capture_output_spec.rb
346
373
  - spec/gorillib/collection_spec.rb
347
374
  - spec/gorillib/type/extended_spec.rb
375
+ - spec/gorillib/type/ip_address_spec.rb
348
376
  - spec/gorillib/hash/zip_spec.rb
349
377
  - spec/gorillib/hash/reverse_merge_spec.rb
350
378
  - spec/gorillib/hash/slice_spec.rb
@@ -359,11 +387,12 @@ test_files:
359
387
  - spec/gorillib/hashlike/hashlike_via_accessors_spec.rb
360
388
  - spec/gorillib/hashlike/behave_same_as_hash_spec.rb
361
389
  - spec/gorillib/configurable_spec.rb
390
+ - spec/gorillib/factories_spec.rb
391
+ - spec/gorillib/model/serialization/tsv_spec.rb
362
392
  - spec/gorillib/model/lint_spec.rb
363
393
  - spec/gorillib/model/defaults_spec.rb
364
394
  - spec/gorillib/model/serialization_spec.rb
365
395
  - spec/gorillib/model/overlay_spec.rb
366
- - spec/gorillib/model/factories_spec.rb
367
396
  - spec/gorillib/pathname_spec.rb
368
397
  - spec/gorillib/enumerable/sum_spec.rb
369
398
  - spec/gorillib/object/try_spec.rb
@@ -372,6 +401,7 @@ test_files:
372
401
  - spec/gorillib/array/compact_blank_spec.rb
373
402
  - spec/gorillib/array/simple_statistics_spec.rb
374
403
  - spec/gorillib/array/extract_options_spec.rb
404
+ - spec/gorillib/array/hashify_spec.rb
375
405
  - spec/gorillib/logger/log_spec.rb
376
406
  - spec/spec_helper.rb
377
407
  - spec/examples/builder/ironfan_spec.rb