namo 0.17.0 → 0.18.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f84d86032e9397e8db4a4d7a4150474c30215cf9339ec2158a38802aad1a4d49
4
- data.tar.gz: 830b874e04b36acd7ab848351419ed5ceb848e0108b95602184b7e6131812e01
3
+ metadata.gz: 1ed38a77d14075cbe3c28ff53097d539c2fefe4a91ff515e165e02c280311fd3
4
+ data.tar.gz: ba009835a59174f2e60327f3583753d2e6c2b0e81b06f23de4a15dd792981526
5
5
  SHA512:
6
- metadata.gz: 1b409a5004f78619ea60186404e6f156fadbc660755cc9fc3c64cfe8a028a3ffdd7bb35dd252510ece1814674f0b02af7b347b1ea42c1280390e7d7a982f0de0
7
- data.tar.gz: 15c2c28dafa605ee5a93e5ebb3bee4e5a1f8301dca7cfb11675e1f197db62052975a0f9b74a9096638062b4d40cd85b5752ce5e52c3b0c5f39463fed421aec6c
6
+ metadata.gz: 7328696a6b4032561fa1b68c0eb4d3e7d3582ee4d5fc24926779112413867f1713100d9c21a3aa2f18c299e50802c866b5c95e874e569f4ef41f40e7b3924f58
7
+ data.tar.gz: 8028776be37f1f183ca2cefc3c9de7229d6918cf37d05b5f925675dd2ffd7d05603cc21ba53665f74094991b0c8d08f22479de2f50ee35e36ba772777bdbfb6e
data/CHANGELOG CHANGED
@@ -1,6 +1,46 @@
1
1
  CHANGELOG
2
2
  _________
3
3
 
4
+ 20260613
5
+ 0.18.0: + Namo::Collection — hierarchical aggregate of named Namos with summary/detail views.
6
+
7
+ 1. + lib/Namo/Collection.rb: Namo::Collection < Namo, the first family member beyond Namo
8
+ itself. Holds @members (an Array of named Namos, attr_reader :members) whose substance is
9
+ the collection; the inherited @data is a derived view rebuilt from the members. << accepts a
10
+ member or an array (via flatten), replaces by name (last-write-wins, unless member.name.nil?
11
+ so nil-named members always append), rebuilds @data = detail.data, and returns self. find(name)
12
+ returns the named member or nil (find(nil) is nil, and an unnamed member never matches),
13
+ shadowing Enumerable#find on Collections. summary(dimension, by: :member, reducer: :sum)
14
+ reduces each member to a {by => member.name, dimension => reduced} row; detail(by: :member)
15
+ unions the members' rows under the inject-iff-absent conditional
16
+ (row.key?(by) ? row : row.merge(by => member.name)); both return a fresh Namo (non-mutating).
17
+ as_summary(...) and as_detail(by = :member) set @data to the chosen view and return self
18
+ (mutating); the view persists until the next << re-materialises detail. Private initialize
19
+ sets @members = [] before super.
20
+ 2. ~ lib/namo.rb: + require_relative './Namo/Collection'.
21
+ 3. + test/Namo/Collection_test.rb: empty members; lazy detail materialisation and selection;
22
+ pure-live reflection of <<; <<' add/replace-by-name/array/unnamed/returns-self; find by name,
23
+ absent, nil, and unnamed; summary's labelled rows, custom by:, :mean reducer (local Array#mean),
24
+ and non-mutation; detail's plain-Namo return, inject-iff-absent both ways, and non-mutation;
25
+ live recomputation after <<; as_summary/as_detail mutate and return self and surface the
26
+ summary's columns in dimensions; as_* view persistence until the next <<; assembly round-trip
27
+ (as_detail(:assembly) injects and is retained, removed only by contraction); subclass default
28
+ by: via a Car < Namo::Collection overriding summary/detail.
29
+ 4. ~ README.md: + Collections section after Named Namos — the GT-budget example (Car/SubAssembly,
30
+ <<, summary, detail), lazy detail materialisation, the four view methods and the
31
+ non-mutating/mutating split, the inject-iff-absent rule, << replace-by-name with use-site
32
+ handling of unnamed members, the rebuild-on-<< view lifetime, and forward-notes to group_by
33
+ (0.19.0) and freeze-gated memoisation (2.x).
34
+ 5. ~ ROADMAP.md: Promote 0.18.0 to shipped, recording the resolved materialisation model
35
+ (rebuild-on-<<, as_* persists until the next <<, diverging from the drafted pure-live-per-access
36
+ / transient wording) and the find/Enumerable#find shadowing; Current state -> 0.18.0; Summary
37
+ folds in Namo::Collection; next phase -> group_by (0.19.0). The upcoming 0.18.0 design block
38
+ folded into the shipped entry.
39
+ 6. ~ COMPARISON.md: Aggregation entry repointed — Namo::Collection shipped (0.18.0), group_by
40
+ constructor still planned (0.19.0); persistent-named-collection retained as Namo's novelty
41
+ versus the other tools' transient group-by intermediates.
42
+ 7. ~ Namo::VERSION: /0.17.0/0.18.0/
43
+
4
44
  20260613
5
45
  0.17.0: + parameterised formulae — formulae with required parameters beyond (row, namo) receive arguments at access time through Row#[].
6
46
 
data/README.md CHANGED
@@ -925,6 +925,121 @@ end
925
925
 
926
926
  `super` with no parentheses forwards every argument — positional and keyword — to `Namo#initialize` unchanged. The `return unless name` guard means a subclass need not override every operator to stop the result of `*` or `select` from re-running its construction side effects: it guards on `name` instead.
927
927
 
928
+ ### Collections
929
+
930
+ `Namo::Collection` is a hierarchical aggregate — a Namo that holds an Array of named Namos (its `members`) and exposes summary and detail views across them. It is the first member of the Namo family beyond `Namo` itself.
931
+
932
+ The motivating case is a hierarchical budget. Each sub-assembly of a car (`powertrain`, `chassis`, `body`, ...) is a Namo with shared columns; the whole car is a `Collection` of those sub-assemblies, queryable both at summary level ("weight by assembly") and detail level ("every line item across all assemblies"):
933
+
934
+ ```ruby
935
+ class Car < Namo::Collection
936
+ def summary(dimension, by: :assembly, reducer: :sum)
937
+ super
938
+ end
939
+
940
+ def detail(by: :assembly)
941
+ super
942
+ end
943
+ end
944
+
945
+ class SubAssembly < Namo; end
946
+
947
+ powertrain = SubAssembly.new(name: :powertrain, data: [
948
+ {component: 'engine', weight: 200, cost: 50000},
949
+ {component: 'gearbox', weight: 80, cost: 20000}
950
+ ])
951
+ chassis = SubAssembly.new(name: :chassis, data: [{component: 'frame', weight: 150, cost: 30000}])
952
+ body = SubAssembly.new(name: :body, data: [{component: 'panels', weight: 60, cost: 15000}])
953
+
954
+ gt = Car.new
955
+ gt << [powertrain, chassis, body]
956
+
957
+ gt.summary(:weight).to_a
958
+ # => [
959
+ # {assembly: :powertrain, weight: 280},
960
+ # {assembly: :chassis, weight: 150},
961
+ # {assembly: :body, weight: 60}
962
+ # ]
963
+
964
+ gt.summary(:weight).values(:weight).sum # total weight by summing the assembly summaries
965
+ # => 490
966
+
967
+ gt.detail.values(:weight).sum # total weight by summing every line item
968
+ # => 490
969
+ ```
970
+
971
+ `Car` overrides `summary`/`detail` only to set `by: :assembly` as the per-class default and then calls `super`. A bare `Namo::Collection.new` works equally well, defaulting `by:` to `:member` and taking it at the call site.
972
+
973
+ #### Lazy detail, behaving as its line items
974
+
975
+ A Collection's substance is its `members`; the inherited `@data` is a *derived view* of them. Any inherited row-operation — selection, projection, `each`, `values`, the set and composition operators — reads that view, so a Collection transparently behaves as its **detail** (the lossless union of its members' rows). Nothing has to be called first:
976
+
977
+ ```ruby
978
+ gt.values(:weight)
979
+ # => [200, 80, 150, 60]
980
+
981
+ gt[component: 'engine'].values(:cost)
982
+ # => [50000]
983
+ ```
984
+
985
+ Detail is the lazy view because a Collection's rows simply *are* its members' rows; a summary is a reduction you pose against them, so it is never reached by accident — only through `summary` or `as_summary`.
986
+
987
+ #### Four view methods
988
+
989
+ The views come in a non-mutating pair and a mutating pair:
990
+
991
+ - `summary(dimension, by:, reducer:)` and `detail(by:)` are **non-mutating** — each returns a fresh `Namo` derived from the members, leaving the Collection untouched. Use these when you want a view to keep: assign the result to a variable and operate on it independently.
992
+ - `as_summary(dimension, by:, reducer:)` and `as_detail(by)` are **mutating** — each sets the Collection's data to the chosen view and returns `self`, for a fluent step. (`as_detail` carries no `dimension`, so its label argument is positional: `as_detail(:assembly)`.)
993
+
994
+ ```ruby
995
+ gt.summary(:cost, reducer: :mean) # a fresh Namo; gt is unchanged
996
+ gt.as_summary(:weight) # gt's data becomes the summary; returns gt
997
+ gt.as_detail(:assembly) # gt's data becomes the detail; returns gt
998
+ ```
999
+
1000
+ `reducer:` is any method the member's column responds to — `:sum` (the default) and `:mean` are typical (`:mean` via a statistics gem that adds `Array#mean`).
1001
+
1002
+ #### Inject-iff-absent
1003
+
1004
+ `detail(by:)` unions the members' rows and labels each with its origin, but only when that label isn't already present:
1005
+
1006
+ - If `by` is **already a dimension** in a member's rows, the row passes through untouched — the dimension is intrinsic.
1007
+ - If `by` is **not** present, `detail` injects it (`row.merge(by => member.name)`), promoting the member's name into a dimension.
1008
+
1009
+ This single conditional is where assembly (`<<`, members named extrinsically) and partition (`group_by`, members named by an intrinsic value — 0.19.0) meet. For an assembled Collection, `as_detail(:assembly)` is the dimension-creating step: it promotes the member name into real data and **retains** it. From then on the structure is intrinsic and round-trips are exact; the promoted dimension is removed only by explicit contraction (`gt[-:assembly]`), never automatically.
1010
+
1011
+ #### `<<` and unnamed members
1012
+
1013
+ `<<` accepts a single member or an array of them. A member whose `name` collides with an existing member's **replaces** it (last-write-wins), making the name → member mapping a dictionary rather than a multimap:
1014
+
1015
+ ```ruby
1016
+ gt << SubAssembly.new(name: :powertrain, data: [...]) # replaces the existing :powertrain
1017
+ gt << [front_suspension, rear_suspension] # adds each
1018
+ ```
1019
+
1020
+ There is no insertion-time guard against unnamed members. An unnamed member is simply appended (no name to collide on) and is unfindable by `find` — the honest consequence of having no name, not an error. `find(name)` returns the member with that name, or `nil`:
1021
+
1022
+ ```ruby
1023
+ gt.find(:chassis) # => the chassis SubAssembly
1024
+ gt.find(:missing) # => nil
1025
+ ```
1026
+
1027
+ (`find(name)` is member lookup; it shadows `Enumerable#find` on Collections. Predicate search over rows remains available as `detect`.)
1028
+
1029
+ #### View lifetime and liveness
1030
+
1031
+ Materialisation is pure-live: the Collection rebuilds its data view from the current members on every `<<`, with no memoisation. So a mutation is reflected immediately — add a member, then summarise or detail, and the new member is included.
1032
+
1033
+ A mutating `as_summary`/`as_detail` view **persists until the next `<<`**, which re-materialises detail. So `as_summary` is for "be the summary for this immediate chain":
1034
+
1035
+ ```ruby
1036
+ gt.as_summary(:weight).values(:weight) # => [280, 150, 60] (the summary)
1037
+ gt << front_suspension # re-materialises detail
1038
+ gt.values(:weight) # => [200, 80, 150, 60, ...] (line items again)
1039
+ ```
1040
+
1041
+ Freeze-gated memoisation is a 2.x optimisation — opt-in via `freeze`, transparent, and never changing this observable behaviour. `group_by` (0.19.0) is the partition-side constructor for the same type: it splits a Namo into a `Collection`, the mirror of assembling one with `<<`.
1042
+
928
1043
  ## Why?
929
1044
 
930
1045
  Every other multi-dimensional array library requires you to pre-shape your data before you can work with it. Namo takes it in the form it likely already comes in.
@@ -0,0 +1,52 @@
1
+ # Namo/Collection.rb
2
+ # Namo::Collection
3
+
4
+ class Namo
5
+ class Collection < Namo
6
+ attr_reader :members
7
+
8
+ def <<(*members)
9
+ members.flatten.each do |member|
10
+ @members.reject!{|existing| existing.name == member.name} unless member.name.nil?
11
+ @members << member
12
+ end
13
+ @data = detail.data
14
+ self
15
+ end
16
+
17
+ def find(name)
18
+ @members.find{|member| member.name == name} unless name.nil?
19
+ end
20
+
21
+ def summary(dimension, by: :member, reducer: :sum)
22
+ rows = @members.map do |member|
23
+ {by => member.name, dimension => member.values(dimension).send(reducer)}
24
+ end
25
+ Namo.new(rows)
26
+ end
27
+
28
+ def detail(by: :member)
29
+ rows = @members.flat_map do |member|
30
+ member.data.map{|row| row.key?(by) ? row : row.merge(by => member.name)}
31
+ end
32
+ Namo.new(rows)
33
+ end
34
+
35
+ def as_summary(dimension, by: :member, reducer: :sum)
36
+ @data = summary(dimension, by: by, reducer: reducer).data
37
+ self
38
+ end
39
+
40
+ def as_detail(by = :member)
41
+ @data = detail(by: by).data
42
+ self
43
+ end
44
+
45
+ private
46
+
47
+ def initialize(positional_data = nil, data: [], formulae: {}, name: nil)
48
+ @members = []
49
+ super
50
+ end
51
+ end
52
+ end
data/lib/Namo/VERSION.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # Namo::VERSION
3
3
 
4
4
  class Namo
5
- VERSION = '0.17.0'
5
+ VERSION = '0.18.0'
6
6
  end
data/lib/namo.rb CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  require_relative './Namo/NegatedDimension'
5
5
  require_relative './Namo/Row'
6
+ require_relative './Namo/Collection'
6
7
  require_relative './Namo/Enumerable'
7
8
  require_relative './Namo/VERSION'
8
9
  require_relative './Symbol'
@@ -0,0 +1,250 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest-spec-context'
3
+
4
+ require_relative '../../lib/namo'
5
+
6
+ class Array
7
+ def mean
8
+ sum.to_f / size
9
+ end
10
+ end unless [].respond_to?(:mean)
11
+
12
+ class SubAssembly < Namo; end
13
+
14
+ class Car < Namo::Collection
15
+ def summary(dimension, by: :assembly, reducer: :sum)
16
+ super
17
+ end
18
+
19
+ def detail(by: :assembly)
20
+ super
21
+ end
22
+ end
23
+
24
+ describe Namo::Collection do
25
+ let(:powertrain) do
26
+ SubAssembly.new(name: :powertrain, data: [
27
+ {component: 'engine', weight: 200, cost: 50000},
28
+ {component: 'gearbox', weight: 80, cost: 20000},
29
+ ])
30
+ end
31
+
32
+ let(:chassis) do
33
+ SubAssembly.new(name: :chassis, data: [{component: 'frame', weight: 150, cost: 30000}])
34
+ end
35
+
36
+ let(:body) do
37
+ SubAssembly.new(name: :body, data: [{component: 'panels', weight: 60, cost: 15000}])
38
+ end
39
+
40
+ let(:wheels) do
41
+ SubAssembly.new(name: :wheels, data: [{component: 'tyres', weight: 40, cost: 8000}])
42
+ end
43
+
44
+ let(:collection) do
45
+ Namo::Collection.new.tap{|c| c << [powertrain, chassis, body]}
46
+ end
47
+
48
+ describe "construction" do
49
+ it "starts with empty members" do
50
+ _(Namo::Collection.new.members).must_equal []
51
+ end
52
+ end
53
+
54
+ describe "lazy detail materialisation" do
55
+ it "materialises detail on a bare row-operation without a prior as_detail" do
56
+ _(collection.values(:weight)).must_equal [200, 80, 150, 60]
57
+ end
58
+
59
+ it "supports selection against the materialised detail" do
60
+ _(collection[component: 'engine'].values(:component)).must_equal ['engine']
61
+ end
62
+
63
+ it "reflects a newly added member on the next operation" do
64
+ before = collection.values(:weight)
65
+ collection << wheels
66
+ _(collection.values(:weight)).must_equal before + [40]
67
+ end
68
+ end
69
+
70
+ describe "#<<" do
71
+ it "adds a member" do
72
+ collection = Namo::Collection.new
73
+ collection << powertrain
74
+ _(collection.members.size).must_equal 1
75
+ _(collection.find(:powertrain)).must_be_same_as powertrain
76
+ end
77
+
78
+ it "replaces a member with a colliding name (last-write-wins)" do
79
+ collection = Namo::Collection.new
80
+ collection << powertrain
81
+ replacement = SubAssembly.new(name: :powertrain, data: [{component: 'hybrid', weight: 250, cost: 70000}])
82
+ collection << replacement
83
+ _(collection.members.size).must_equal 1
84
+ _(collection.find(:powertrain)).must_be_same_as replacement
85
+ end
86
+
87
+ it "adds each member from an array" do
88
+ collection = Namo::Collection.new
89
+ collection << [powertrain, chassis]
90
+ _(collection.members.size).must_equal 2
91
+ end
92
+
93
+ it "appends an unnamed member, which is unfindable by name" do
94
+ collection = Namo::Collection.new
95
+ collection << SubAssembly.new(data: [{component: 'misc', weight: 5, cost: 100}])
96
+ collection << SubAssembly.new(data: [{component: 'other', weight: 6, cost: 200}])
97
+ _(collection.members.size).must_equal 2
98
+ end
99
+
100
+ it "returns self" do
101
+ collection = Namo::Collection.new
102
+ _(collection << powertrain).must_be_same_as collection
103
+ end
104
+ end
105
+
106
+ describe "#find" do
107
+ it "returns the member with the given name" do
108
+ _(collection.find(:chassis)).must_be_same_as chassis
109
+ end
110
+
111
+ it "returns nil for an absent name" do
112
+ _(collection.find(:engine)).must_be_nil
113
+ end
114
+
115
+ it "returns nil for find(nil)" do
116
+ _(collection.find(nil)).must_be_nil
117
+ end
118
+
119
+ it "never matches an unnamed member" do
120
+ collection = Namo::Collection.new
121
+ collection << SubAssembly.new(data: [{component: 'misc', weight: 5, cost: 100}])
122
+ _(collection.find(nil)).must_be_nil
123
+ end
124
+ end
125
+
126
+ describe "#summary" do
127
+ it "reduces each member to a labelled row" do
128
+ summary = collection.summary(:weight)
129
+ _(summary.values(:member)).must_equal [:powertrain, :chassis, :body]
130
+ _(summary.values(:weight)).must_equal [280, 150, 60]
131
+ end
132
+
133
+ it "labels with a custom by dimension" do
134
+ summary = collection.summary(:weight, by: :assembly)
135
+ _(summary.values(:assembly)).must_equal [:powertrain, :chassis, :body]
136
+ end
137
+
138
+ it "reduces with a custom reducer" do
139
+ summary = collection.summary(:weight, reducer: :mean)
140
+ _(summary.values(:weight)).must_equal [140.0, 150.0, 60.0]
141
+ end
142
+
143
+ it "is non-mutating — leaves the collection's data untouched" do
144
+ collection.summary(:weight)
145
+ _(collection.values(:weight)).must_equal [200, 80, 150, 60]
146
+ end
147
+ end
148
+
149
+ describe "#detail" do
150
+ it "returns a plain Namo" do
151
+ _(collection.detail).must_be_instance_of Namo
152
+ end
153
+
154
+ it "unions the members' rows, injecting the by dimension when absent" do
155
+ detail = collection.detail
156
+ _(detail.values(:member)).must_equal [:powertrain, :powertrain, :chassis, :body]
157
+ _(detail.values(:weight)).must_equal [200, 80, 150, 60]
158
+ end
159
+
160
+ it "does not inject when the by dimension is already present" do
161
+ collection = Namo::Collection.new
162
+ collection << SubAssembly.new(name: :ignored, data: [{member: :preexisting, weight: 5}])
163
+ _(collection.detail.values(:member)).must_equal [:preexisting]
164
+ end
165
+
166
+ it "is non-mutating — leaves the collection's data untouched" do
167
+ collection.detail(by: :assembly)
168
+ _(collection.dimensions).wont_include :assembly
169
+ end
170
+ end
171
+
172
+ describe "live recomputation (no memoisation in 1.x)" do
173
+ it "reflects a mutation on the next detail call" do
174
+ before = collection.detail.values(:weight).size
175
+ collection << wheels
176
+ _(collection.detail.values(:weight).size).must_equal before + 1
177
+ end
178
+
179
+ it "reflects a mutation on the next summary call" do
180
+ collection << wheels
181
+ _(collection.summary(:weight).values(:member)).must_equal [:powertrain, :chassis, :body, :wheels]
182
+ end
183
+ end
184
+
185
+ describe "#as_summary / #as_detail" do
186
+ it "as_summary sets the data to the summary view and returns self" do
187
+ result = collection.as_summary(:weight)
188
+ _(result).must_be_same_as collection
189
+ _(collection.values(:weight)).must_equal [280, 150, 60]
190
+ end
191
+
192
+ it "as_detail sets the data to the detail view and returns self" do
193
+ collection.as_summary(:weight)
194
+ result = collection.as_detail
195
+ _(result).must_be_same_as collection
196
+ _(collection.values(:weight)).must_equal [200, 80, 150, 60]
197
+ end
198
+
199
+ it "exposes the summary's columns in dimensions immediately after as_summary" do
200
+ collection.as_summary(:weight)
201
+ _(collection.dimensions.sort).must_equal [:member, :weight].sort
202
+ end
203
+ end
204
+
205
+ describe "as_* view lifetime (rebuild-on-<<: persists until the next <<)" do
206
+ it "keeps the summary view across a bare row-operation" do
207
+ collection.as_summary(:weight)
208
+ _(collection.values(:weight)).must_equal [280, 150, 60]
209
+ _(collection[member: :powertrain].values(:weight)).must_equal [280]
210
+ end
211
+
212
+ it "re-materialises detail on the next <<" do
213
+ collection.as_summary(:weight)
214
+ collection << wheels
215
+ _(collection.values(:weight)).must_equal [200, 80, 150, 60, 40]
216
+ end
217
+ end
218
+
219
+ describe "assembly round-trip" do
220
+ it "injects :assembly via as_detail and retains it through a later << and as_detail" do
221
+ collection.as_detail(:assembly)
222
+ _(collection.dimensions).must_include :assembly
223
+ _(collection.coordinates(:assembly)).must_equal [:powertrain, :chassis, :body]
224
+ collection << wheels
225
+ collection.as_detail(:assembly)
226
+ _(collection.dimensions).must_include :assembly
227
+ _(collection.coordinates(:assembly)).must_equal [:powertrain, :chassis, :body, :wheels]
228
+ end
229
+
230
+ it "removes :assembly only by explicit contraction" do
231
+ collection.as_detail(:assembly)
232
+ contracted = collection[-:assembly]
233
+ _(contracted.dimensions).wont_include :assembly
234
+ end
235
+ end
236
+
237
+ describe "subclass with a default by:" do
238
+ let(:car) do
239
+ Car.new.tap{|c| c << [powertrain, chassis, body]}
240
+ end
241
+
242
+ it "uses the subclass default :assembly for summary" do
243
+ _(car.summary(:weight).values(:assembly)).must_equal [:powertrain, :chassis, :body]
244
+ end
245
+
246
+ it "uses the subclass default :assembly for detail" do
247
+ _(car.detail.values(:assembly)).must_equal [:powertrain, :powertrain, :chassis, :body]
248
+ end
249
+ end
250
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: namo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.0
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran
@@ -64,6 +64,7 @@ files:
64
64
  - LICENSE
65
65
  - README.md
66
66
  - Rakefile
67
+ - lib/Namo/Collection.rb
67
68
  - lib/Namo/Enumerable.rb
68
69
  - lib/Namo/NegatedDimension.rb
69
70
  - lib/Namo/Row.rb
@@ -71,6 +72,7 @@ files:
71
72
  - lib/Symbol.rb
72
73
  - lib/namo.rb
73
74
  - namo.gemspec
75
+ - test/Namo/Collection_test.rb
74
76
  - test/Namo/NegatedDimension_test.rb
75
77
  - test/Namo/Row_test.rb
76
78
  - test/Symbol_test.rb