namo 0.0.1 → 0.1.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 +4 -4
- data/CHANGELOG +16 -3
- data/README.md +126 -21
- data/lib/Namo/VERSION.rb +1 -1
- data/lib/namo.rb +55 -14
- data/namo.gemspec +1 -1
- data/test/namo_test.rb +125 -41
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1bc0f90c2517b809ed9929ffcdf2fbc1e538974bc30320a3b603d93582f5200
|
|
4
|
+
data.tar.gz: 70247c7e8fa7b48ac0192400016351c323f5abcd9ee1095653cc58d6edc524e4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b7162863da3b04b989dd6206234094b160b0f32d36fee58f948608b85a3cd1c2dc09691c6e2c2c74d598942a0c15e44760f6b498a7bddbb1cc4bb2366b2996ac
|
|
7
|
+
data.tar.gz: 5dd204de47dc6979753a0b7113765093222f5fab870bacd4034aada2f93288af6e872781f357704951b75d1e17ea9c6aefc8a625530ca2e6f253f695c5456fc2
|
data/CHANGELOG
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
CHANGELOG
|
|
2
|
-
|
|
2
|
+
_________
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
20260328
|
|
5
|
+
0.1.0: Add formulae via []=. Formulae are procs which receive a Row object, compose via named references, and carry through selection.
|
|
6
|
+
|
|
7
|
+
1. ~ Namo#[]: Refactor to accept *names for projection alongside **selections. Always returns a Namo instance.
|
|
8
|
+
2. + Namo#[]=: Define named formulae as procs.
|
|
9
|
+
3. + Namo#to_a: Extract data as an array of hashes.
|
|
10
|
+
4. + Namo::Row: Resolve names against row data or formulae.
|
|
11
|
+
5. ~ Namo#initialize: Accept optional formulae: keyword argument.
|
|
12
|
+
6. + attr_accessor :data, :formulae
|
|
13
|
+
7. ~ README.md: + Projection section, + Formulae section, + Extracting data section
|
|
14
|
+
8. ~ namo_test.rb: Add tests for projection, formulae, composition, chained and single-call selection with projection.
|
|
15
|
+
9. ~ Namo::VERSION: /0.0.1/0.1.0/
|
|
16
|
+
|
|
17
|
+
20260315
|
|
5
18
|
0.0.1: Fix README.md and CHANGELOG.
|
|
6
19
|
|
|
7
20
|
1. ~ README.md: Fix Contributing secton.
|
|
8
21
|
2. ~ CHANGELOG: Use the same description in the summary as in the commit message.
|
|
9
22
|
3. ~ Namo::VERSION: /0.0.0/0.0.1/
|
|
10
23
|
|
|
11
|
-
|
|
24
|
+
20260315
|
|
12
25
|
0.0.0: The initial release has instantiation from hashes, automatic inferral of dimension names and co-ordinates, and selection of data by a single value, an array, or a range.
|
|
13
26
|
|
|
14
27
|
1. + Namo
|
data/README.md
CHANGED
|
@@ -18,60 +18,165 @@ gem 'namo'
|
|
|
18
18
|
|
|
19
19
|
## Usage
|
|
20
20
|
|
|
21
|
-
Create a Namo from an array of hashes:
|
|
21
|
+
Create a Namo instance from an array of hashes:
|
|
22
22
|
|
|
23
23
|
```ruby
|
|
24
24
|
require 'namo'
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
{
|
|
28
|
-
{
|
|
29
|
-
{
|
|
30
|
-
{
|
|
26
|
+
sales = Namo.new([
|
|
27
|
+
{product: 'Widget', quarter: 'Q1', price: 10.0, quantity: 100},
|
|
28
|
+
{product: 'Widget', quarter: 'Q2', price: 10.0, quantity: 150},
|
|
29
|
+
{product: 'Gadget', quarter: 'Q1', price: 25.0, quantity: 40},
|
|
30
|
+
{product: 'Gadget', quarter: 'Q2', price: 25.0, quantity: 60}
|
|
31
31
|
])
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
Dimensions and coordinates are inferred:
|
|
35
35
|
|
|
36
36
|
```ruby
|
|
37
|
-
|
|
38
|
-
# => [:
|
|
37
|
+
sales.dimensions
|
|
38
|
+
# => [:product, :quarter, :price, :quantity]
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
# => ['
|
|
40
|
+
sales.coordinates[:product]
|
|
41
|
+
# => ['Widget', 'Gadget']
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
# => ['
|
|
43
|
+
sales.coordinates[:quarter]
|
|
44
|
+
# => ['Q1', 'Q2']
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
### Selection
|
|
48
|
+
|
|
47
49
|
Select by named dimension using keyword arguments:
|
|
48
50
|
|
|
49
51
|
```ruby
|
|
50
52
|
# Single value
|
|
51
|
-
|
|
53
|
+
sales[product: 'Widget']
|
|
54
|
+
# => #<Namo [
|
|
55
|
+
# {product: 'Widget', quarter: 'Q1', price: 10.0, quantity: 100},
|
|
56
|
+
# {product: 'Widget', quarter: 'Q2', price: 10.0, quantity: 150}
|
|
57
|
+
# ]>
|
|
52
58
|
|
|
53
59
|
# Multiple dimensions
|
|
54
|
-
|
|
60
|
+
sales[product: 'Widget', quarter: 'Q1']
|
|
61
|
+
# => #<Namo [
|
|
62
|
+
# {product: 'Widget', quarter: 'Q1', price: 10.0, quantity: 100}
|
|
63
|
+
# ]>
|
|
55
64
|
|
|
56
65
|
# Range
|
|
57
|
-
|
|
66
|
+
sales[price: 10.0..20.0]
|
|
67
|
+
# => #<Namo [
|
|
68
|
+
# {product: 'Widget', quarter: 'Q1', price: 10.0, quantity: 100},
|
|
69
|
+
# {product: 'Widget', quarter: 'Q2', price: 10.0, quantity: 150}
|
|
70
|
+
# ]>
|
|
58
71
|
|
|
59
72
|
# Array of values
|
|
60
|
-
|
|
73
|
+
sales[quarter: ['Q1']]
|
|
74
|
+
# => #<Namo [
|
|
75
|
+
# {product: 'Widget', quarter: 'Q1', price: 10.0, quantity: 100},
|
|
76
|
+
# {product: 'Gadget', quarter: 'Q1', price: 25.0, quantity: 40}
|
|
77
|
+
# ]>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Projection
|
|
81
|
+
|
|
82
|
+
Project to specific dimensions:
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
sales[:product, :price]
|
|
86
|
+
# => #<Namo [
|
|
87
|
+
# {product: 'Widget', price: 10.0},
|
|
88
|
+
# {product: 'Widget', price: 10.0},
|
|
89
|
+
# {product: 'Gadget', price: 25.0},
|
|
90
|
+
# {product: 'Gadget', price: 25.0}
|
|
91
|
+
# ]>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Selection and projection can be chained:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
sales[product: 'Widget'][:quarter, :price]
|
|
98
|
+
# => #<Namo [
|
|
99
|
+
# {quarter: 'Q1', price: 10.0},
|
|
100
|
+
# {quarter: 'Q2', price: 10.0}
|
|
101
|
+
# ]>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Or combined in a single call (names before selectors):
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
sales[:quarter, :price, product: 'Widget']
|
|
108
|
+
# => #<Namo [
|
|
109
|
+
# {quarter: 'Q1', price: 10.0},
|
|
110
|
+
# {quarter: 'Q2', price: 10.0}
|
|
111
|
+
# ]>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Selection and projection always return a new Namo instance, so everything chains.
|
|
61
115
|
|
|
62
|
-
|
|
63
|
-
|
|
116
|
+
### Formulae
|
|
117
|
+
|
|
118
|
+
Define computed dimensions using `[]=`:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
sales[:revenue] = proc{|row| row[:price] * row[:quantity]}
|
|
122
|
+
|
|
123
|
+
sales[:product, :quarter, :revenue]
|
|
124
|
+
# => #<Namo [
|
|
125
|
+
# {product: 'Widget', quarter: 'Q1', revenue: 1000.0},
|
|
126
|
+
# {product: 'Widget', quarter: 'Q2', revenue: 1500.0},
|
|
127
|
+
# {product: 'Gadget', quarter: 'Q1', revenue: 1000.0},
|
|
128
|
+
# {product: 'Gadget', quarter: 'Q2', revenue: 1500.0}
|
|
129
|
+
# ]>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Formulae compose:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
sales[:cost] = proc{|row| row[:quantity] * 4.0}
|
|
136
|
+
sales[:profit] = proc{|row| row[:revenue] - row[:cost]}
|
|
137
|
+
|
|
138
|
+
sales[:product, :quarter, :profit]
|
|
139
|
+
# => #<Namo [
|
|
140
|
+
# {product: 'Widget', quarter: 'Q1', profit: 600.0},
|
|
141
|
+
# {product: 'Widget', quarter: 'Q2', profit: 900.0},
|
|
142
|
+
# {product: 'Gadget', quarter: 'Q1', profit: 840.0},
|
|
143
|
+
# {product: 'Gadget', quarter: 'Q2', profit: 1260.0}
|
|
144
|
+
# ]>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Formulae work with selection and projection:
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
sales[product: 'Widget'][:revenue, :quarter]
|
|
151
|
+
# => #<Namo [
|
|
152
|
+
# {revenue: 1000.0, quarter: 'Q1'},
|
|
153
|
+
# {revenue: 1500.0, quarter: 'Q2'}
|
|
154
|
+
# ]>
|
|
64
155
|
```
|
|
65
156
|
|
|
66
|
-
|
|
157
|
+
Formulae carry through selection — a filtered Namo instance remembers its formulae.
|
|
158
|
+
|
|
159
|
+
### Extracting data
|
|
160
|
+
|
|
161
|
+
`to_a` returns an array of hashes:
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
sales[:product, :quarter, :revenue].to_a
|
|
165
|
+
# => [
|
|
166
|
+
# {product: 'Widget', quarter: 'Q1', revenue: 1000.0},
|
|
167
|
+
# {product: 'Widget', quarter: 'Q2', revenue: 1500.0},
|
|
168
|
+
# {product: 'Gadget', quarter: 'Q1', revenue: 1000.0},
|
|
169
|
+
# {product: 'Gadget', quarter: 'Q2', revenue: 1500.0}
|
|
170
|
+
# ]
|
|
171
|
+
```
|
|
67
172
|
|
|
68
173
|
## Why?
|
|
69
174
|
|
|
70
|
-
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 already comes in.
|
|
175
|
+
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.
|
|
71
176
|
|
|
72
177
|
## Name
|
|
73
178
|
|
|
74
|
-
Namo:
|
|
179
|
+
Namo: nam(ed) (dimensi)o(ns). A companion to Numo (numeric arrays for Ruby). And in Aussie culture 'o' gets added to the end of names.
|
|
75
180
|
|
|
76
181
|
## Contributing
|
|
77
182
|
|
data/lib/Namo/VERSION.rb
CHANGED
data/lib/namo.rb
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
# Namo
|
|
3
3
|
|
|
4
4
|
class Namo
|
|
5
|
+
class Row
|
|
6
|
+
def [](name)
|
|
7
|
+
if @formulae.key?(name)
|
|
8
|
+
@formulae[name].call(self)
|
|
9
|
+
else
|
|
10
|
+
@row[name]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def initialize(row, formulae)
|
|
17
|
+
@row = row
|
|
18
|
+
@formulae = formulae
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_accessor :data
|
|
23
|
+
attr_accessor :formulae
|
|
24
|
+
|
|
5
25
|
def dimensions
|
|
6
26
|
@dimensions ||= @data.first.keys
|
|
7
27
|
end
|
|
@@ -14,25 +34,46 @@ class Namo
|
|
|
14
34
|
)
|
|
15
35
|
end
|
|
16
36
|
|
|
17
|
-
def [](**selections)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
coordinate.include?(row[dimension])
|
|
24
|
-
else
|
|
25
|
-
coordinate == row[dimension]
|
|
26
|
-
end
|
|
27
|
-
end
|
|
37
|
+
def [](*names, **selections)
|
|
38
|
+
data = selections.any? ? select_rows(selections) : @data
|
|
39
|
+
if names.any?
|
|
40
|
+
data = data.map do |row_data|
|
|
41
|
+
row = Row.new(row_data, @formulae)
|
|
42
|
+
names.each_with_object({}){|name, hash| hash[name] = row[name]}
|
|
28
43
|
end
|
|
29
|
-
|
|
30
|
-
self.class.new(
|
|
44
|
+
end
|
|
45
|
+
self.class.new(data, formulae: @formulae.dup)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def []=(name, proc)
|
|
49
|
+
@formulae[name] = proc
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_a
|
|
53
|
+
@data.map do |row|
|
|
54
|
+
row.keys.each_with_object({}) do |key, hash|
|
|
55
|
+
hash[key] = row[key]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
31
58
|
end
|
|
32
59
|
|
|
33
60
|
private
|
|
34
61
|
|
|
35
|
-
def initialize(data)
|
|
62
|
+
def initialize(data = nil, formulae: {})
|
|
36
63
|
@data = data
|
|
64
|
+
@formulae = formulae
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def select_rows(selections)
|
|
68
|
+
@data.select do |row|
|
|
69
|
+
selections.all? do |dimension, coordinate|
|
|
70
|
+
case coordinate
|
|
71
|
+
when Array, Range
|
|
72
|
+
coordinate.include?(row[dimension])
|
|
73
|
+
else
|
|
74
|
+
row[dimension] == coordinate
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
37
78
|
end
|
|
38
79
|
end
|
data/namo.gemspec
CHANGED
|
@@ -2,7 +2,6 @@ require_relative './lib/Namo/VERSION'
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |spec|
|
|
4
4
|
spec.name = 'namo'
|
|
5
|
-
spec.date = '2026-03-15'
|
|
6
5
|
spec.version = Namo::VERSION
|
|
7
6
|
|
|
8
7
|
spec.summary = "Named dimensional data for Ruby."
|
|
@@ -28,5 +27,6 @@ Gem::Specification.new do |spec|
|
|
|
28
27
|
].flatten
|
|
29
28
|
|
|
30
29
|
spec.add_development_dependency 'minitest'
|
|
30
|
+
spec.add_development_dependency 'minitest-spec-context'
|
|
31
31
|
spec.add_development_dependency 'rake'
|
|
32
32
|
end
|
data/test/namo_test.rb
CHANGED
|
@@ -1,70 +1,154 @@
|
|
|
1
|
-
# namo_test.rb
|
|
2
|
-
|
|
3
|
-
# 20260314
|
|
4
|
-
# 0.0.0
|
|
5
|
-
|
|
6
1
|
require 'minitest/autorun'
|
|
2
|
+
require 'minitest-spec-context'
|
|
3
|
+
|
|
7
4
|
require_relative '../lib/namo'
|
|
8
5
|
|
|
9
6
|
describe Namo do
|
|
10
7
|
let(:sample_data) do
|
|
11
8
|
[
|
|
12
|
-
{
|
|
13
|
-
{
|
|
14
|
-
{
|
|
15
|
-
{
|
|
9
|
+
{product: 'Widget', quarter: 'Q1', price: 10.0, quantity: 100},
|
|
10
|
+
{product: 'Widget', quarter: 'Q2', price: 10.0, quantity: 150},
|
|
11
|
+
{product: 'Gadget', quarter: 'Q1', price: 25.0, quantity: 40},
|
|
12
|
+
{product: 'Gadget', quarter: 'Q2', price: 25.0, quantity: 60}
|
|
16
13
|
]
|
|
17
14
|
end
|
|
18
15
|
|
|
19
|
-
let(:
|
|
16
|
+
let(:sales) do
|
|
17
|
+
Namo.new(sample_data)
|
|
18
|
+
end
|
|
20
19
|
|
|
21
|
-
describe
|
|
22
|
-
it
|
|
23
|
-
_(
|
|
20
|
+
describe "#dimensions" do
|
|
21
|
+
it "infers dimensions from hash keys" do
|
|
22
|
+
_(sales.dimensions).must_equal [:product, :quarter, :price, :quantity]
|
|
24
23
|
end
|
|
25
24
|
end
|
|
26
25
|
|
|
27
|
-
describe
|
|
28
|
-
it
|
|
29
|
-
_(
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
describe "#coordinates" do
|
|
27
|
+
it "extracts unique values for each dimension" do
|
|
28
|
+
_(sales.coordinates).must_equal ({
|
|
29
|
+
product: ['Widget', 'Gadget'],
|
|
30
|
+
quarter: ['Q1', 'Q2'],
|
|
31
|
+
price: [10.0, 25.0],
|
|
32
|
+
quantity: [100, 150, 40, 60]
|
|
33
|
+
})
|
|
34
|
+
_(sales.coordinates[:product]).must_equal ['Widget', 'Gadget']
|
|
35
|
+
_(sales.coordinates[:quarter]).must_equal ['Q1', 'Q2']
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "#[]" do
|
|
40
|
+
context "selection" do
|
|
41
|
+
it "selects by single coordinate" do
|
|
42
|
+
result = sales[product: 'Widget']
|
|
43
|
+
_(result.coordinates[:product]).must_equal ['Widget']
|
|
44
|
+
_(result.to_a.count).must_equal 2
|
|
45
|
+
_(result.to_a.map{|row| row[:product]}).must_equal ['Widget', 'Widget']
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "selects by array of coordinates" do
|
|
49
|
+
result = sales[quarter: ['Q1']]
|
|
50
|
+
_(result.coordinates[:quarter]).must_equal ['Q1']
|
|
51
|
+
_(result.to_a.count).must_equal 2
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "selects by multiple dimensions" do
|
|
55
|
+
result = sales[product: 'Widget', quarter: 'Q1']
|
|
56
|
+
_(result.to_a.count).must_equal 1
|
|
57
|
+
_(result.to_a).must_equal [{product: 'Widget', quarter: 'Q1', price: 10.0, quantity: 100}]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "returns a Namo instance" do
|
|
61
|
+
result = sales[product: 'Widget']
|
|
62
|
+
_(result.coordinates[:product]).must_equal ['Widget']
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context "projection" do
|
|
67
|
+
it "projects to named dimensions" do
|
|
68
|
+
result = sales[:product, :price]
|
|
69
|
+
_(result.dimensions).must_equal [:product, :price]
|
|
70
|
+
_(result.coordinates[:product]).must_equal ['Widget', 'Gadget']
|
|
71
|
+
_(result.to_a.count).must_equal 4
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context "selection and projection" do
|
|
76
|
+
it "can use them together" do
|
|
77
|
+
result = sales[:price, product: 'Widget']
|
|
78
|
+
_(result.to_a.count).must_equal 2
|
|
79
|
+
_(result.to_a).must_equal [{price: 10.0}, {price: 10.0}]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "can chain them" do
|
|
83
|
+
result = sales[product: 'Widget'][:price]
|
|
84
|
+
_(result.to_a.count).must_equal 2
|
|
85
|
+
_(result.to_a).must_equal [{price: 10.0}, {price: 10.0}]
|
|
86
|
+
end
|
|
32
87
|
end
|
|
33
88
|
end
|
|
34
89
|
|
|
35
|
-
describe
|
|
36
|
-
it
|
|
37
|
-
|
|
38
|
-
_(
|
|
39
|
-
|
|
40
|
-
|
|
90
|
+
describe "#[]= formulae" do
|
|
91
|
+
it "defines a formula" do
|
|
92
|
+
sales[:revenue] = proc{|r| r[:price] * r[:quantity]}
|
|
93
|
+
_(sales[:product, :quarter, :revenue].to_a).must_equal [
|
|
94
|
+
{product: "Widget", quarter: "Q1", revenue: 1000.0},
|
|
95
|
+
{product: "Widget", quarter: "Q2", revenue: 1500.0},
|
|
96
|
+
{product: "Gadget", quarter: "Q1", revenue: 1000.0},
|
|
97
|
+
{product: "Gadget", quarter: "Q2", revenue: 1500.0}
|
|
98
|
+
]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "composes formulae" do
|
|
102
|
+
sales[:revenue] = proc{|r| r[:price] * r[:quantity]}
|
|
103
|
+
sales[:cost] = proc{|r| r[:quantity] * 4.0}
|
|
104
|
+
sales[:profit] = proc{|r| r[:revenue] - r[:cost]}
|
|
105
|
+
_(sales[:product, :quarter, :profit].to_a).must_equal [
|
|
106
|
+
{product: "Widget", quarter: "Q1", profit: 600.0},
|
|
107
|
+
{product: "Widget", quarter: "Q2", profit: 900.0},
|
|
108
|
+
{product: "Gadget", quarter: "Q1", profit: 840.0},
|
|
109
|
+
{product: "Gadget", quarter: "Q2", profit: 1260.0}
|
|
110
|
+
]
|
|
41
111
|
end
|
|
42
112
|
|
|
43
|
-
it
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
_(result.
|
|
47
|
-
|
|
113
|
+
it "works with chained selection and projection" do
|
|
114
|
+
sales[:revenue] = proc{|r| r[:price] * r[:quantity]}
|
|
115
|
+
result = sales[product: 'Widget'][:product, :quarter, :revenue]
|
|
116
|
+
_(result.to_a).must_equal [
|
|
117
|
+
{product: "Widget", quarter: "Q1", revenue: 1000.0},
|
|
118
|
+
{product: "Widget", quarter: "Q2", revenue: 1500.0}
|
|
119
|
+
]
|
|
48
120
|
end
|
|
49
121
|
|
|
50
|
-
it
|
|
51
|
-
|
|
52
|
-
|
|
122
|
+
it "works with single-call selection and projection" do
|
|
123
|
+
sales[:revenue] = proc{|r| r[:price] * r[:quantity]}
|
|
124
|
+
result = sales[:product, :revenue, product: 'Widget']
|
|
125
|
+
_(result.to_a).must_equal [
|
|
126
|
+
{product: "Widget", revenue: 1000.0},
|
|
127
|
+
{product: "Widget", revenue: 1500.0}
|
|
128
|
+
]
|
|
53
129
|
end
|
|
54
130
|
|
|
55
|
-
it
|
|
56
|
-
|
|
57
|
-
|
|
131
|
+
it "carries formulae through selection" do
|
|
132
|
+
sales[:revenue] = proc{|r| r[:price] * r[:quantity]}
|
|
133
|
+
widgets = sales[product: 'Widget']
|
|
134
|
+
_(widgets[:revenue].to_a).must_equal [{revenue: 1000.0}, {revenue: 1500.0}]
|
|
58
135
|
end
|
|
59
136
|
|
|
60
|
-
it
|
|
61
|
-
|
|
62
|
-
|
|
137
|
+
it "projects formula with context dimensions" do
|
|
138
|
+
sales[:revenue] = proc{|r| r[:price] * r[:quantity]}
|
|
139
|
+
result = sales[:product, :quarter, :revenue]
|
|
140
|
+
_(result.to_a).must_equal [
|
|
141
|
+
{product: 'Widget', quarter: 'Q1', revenue: 1000.0},
|
|
142
|
+
{product: 'Widget', quarter: 'Q2', revenue: 1500.0},
|
|
143
|
+
{product: 'Gadget', quarter: 'Q1', revenue: 1000.0},
|
|
144
|
+
{product: 'Gadget', quarter: 'Q2', revenue: 1500.0}
|
|
145
|
+
]
|
|
63
146
|
end
|
|
147
|
+
end
|
|
64
148
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
_(
|
|
149
|
+
describe "#to_a" do
|
|
150
|
+
it "returns the data as an array of hashes" do
|
|
151
|
+
_(sales.to_a).must_equal sample_data
|
|
68
152
|
end
|
|
69
153
|
end
|
|
70
154
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: namo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thoran
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: minitest
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: minitest-spec-context
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: rake
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -72,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
72
86
|
- !ruby/object:Gem::Version
|
|
73
87
|
version: '0'
|
|
74
88
|
requirements: []
|
|
75
|
-
rubygems_version: 4.0.
|
|
89
|
+
rubygems_version: 4.0.8
|
|
76
90
|
specification_version: 4
|
|
77
91
|
summary: Named dimensional data for Ruby.
|
|
78
92
|
test_files: []
|