mdquery 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "mdquery"
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["mccraigmccraig"]
12
+ s.date = "2012-03-27"
13
+ s.description = "provides a DSL for simply specifying and executing segmented multi-dimensional queries on your active-record-3 models"
14
+ s.email = "mccraigmccraig@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ ".travis.yml",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "lib/mdquery.rb",
30
+ "lib/mdquery/dataset.rb",
31
+ "lib/mdquery/dsl.rb",
32
+ "lib/mdquery/model.rb",
33
+ "lib/mdquery/util.rb",
34
+ "mdquery.gemspec",
35
+ "spec/mdquery/dataset_spec.rb",
36
+ "spec/mdquery/dsl_spec.rb",
37
+ "spec/mdquery/model_spec.rb",
38
+ "spec/mdquery/util_spec.rb",
39
+ "spec/mdquery_spec.rb",
40
+ "spec/spec_helper.rb"
41
+ ]
42
+ s.homepage = "http://github.com/mccraigmccraig/mdquery"
43
+ s.licenses = ["MIT"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = "1.8.10"
46
+ s.summary = "simple multi-dimensional queries on top of active-record-3"
47
+
48
+ if s.respond_to? :specification_version then
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
52
+ s.add_runtime_dependency(%q<activerecord>, [">= 3.1.0"])
53
+ s.add_development_dependency(%q<rake>, ["~> 0.9.2"])
54
+ s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
55
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
56
+ s.add_development_dependency(%q<bundler>, ["~> 1.1.0"])
57
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
58
+ s.add_development_dependency(%q<rr>, [">= 1.0.4"])
59
+ else
60
+ s.add_dependency(%q<activerecord>, [">= 3.1.0"])
61
+ s.add_dependency(%q<rake>, ["~> 0.9.2"])
62
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
63
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
64
+ s.add_dependency(%q<bundler>, ["~> 1.1.0"])
65
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
66
+ s.add_dependency(%q<rr>, [">= 1.0.4"])
67
+ end
68
+ else
69
+ s.add_dependency(%q<activerecord>, [">= 3.1.0"])
70
+ s.add_dependency(%q<rake>, ["~> 0.9.2"])
71
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
72
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
73
+ s.add_dependency(%q<bundler>, ["~> 1.1.0"])
74
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
75
+ s.add_dependency(%q<rr>, [">= 1.0.4"])
76
+ end
77
+ end
78
+
@@ -0,0 +1,232 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'mdquery/dataset'
3
+
4
+ module MDQuery::Dataset
5
+
6
+ describe DimensionSegment do
7
+
8
+ def build
9
+ @model = Object.new
10
+ mock(@model).key{:foo_segment}
11
+
12
+ @dimension = Object.new
13
+ @source = Object.new
14
+ mock(@dimension).dataset.mock!.model.mock!.source{@source}
15
+
16
+ @values = [:foo, :bar, :baz]
17
+ mock(@model).get_values(@source){@values}
18
+ @labels = {:foo=>"foo", :bar=>"BAR", :baz=>"blah"}
19
+ mock(@model).labels(@values){@labels}
20
+
21
+ DimensionSegment.new(@model, @dimension)
22
+ end
23
+
24
+ it "should constract a DimensionSegment from a DimensionSegmentModel" do
25
+ ds = build
26
+ ds.dimension.should == @dimension
27
+ ds.key.should == :foo_segment
28
+ ds.dimension_values.map(&:dimension_segment).should == [ds, ds, ds]
29
+ ds.dimension_values.map(&:value).should == [:foo, :bar, :baz]
30
+ ds.dimension_values.map(&:label).should == ["foo", "BAR", "blah"]
31
+ ds.values.should == [:foo, :bar, :baz]
32
+ end
33
+
34
+ describe "dimension_value_for" do
35
+ it "should retrieve a DimensionValue from the segment by value" do
36
+ ds = build
37
+ dv0 = ds.dimension_value_for(:foo)
38
+ dv0.dimension_segment.should == ds
39
+ dv0.value.should == :foo
40
+ dv0.label.should == "foo"
41
+ end
42
+
43
+ it "should retrieve a DimensionValue from the segment with the deref operator" do
44
+ ds = build
45
+ dv0 = ds[:foo]
46
+ dv0.dimension_segment.should == ds
47
+ dv0.value.should == :foo
48
+ dv0.label.should == "foo"
49
+ end
50
+ end
51
+
52
+ describe "label_for" do
53
+ it "should get a label for a value from the segment" do
54
+ ds = build
55
+ ds.label_for(:foo).should == "foo"
56
+ ds.label_for(:bar).should == "BAR"
57
+ end
58
+ end
59
+ end
60
+
61
+ describe Dimension do
62
+
63
+ def build
64
+ @model = Object.new
65
+ mock(@model).key{:foodim}
66
+ mock(@model).label{"Dimension Foo"}
67
+
68
+ @sm0 = Object.new
69
+ @segment0 = Object.new
70
+ stub(@segment0).key{:segment0_key}
71
+ stub(@segment0).values{[:foo, :bar]}
72
+ @s0v0 = Object.new
73
+ stub(@s0v0).label{"Foo"}
74
+ stub(@s0v0).value{:foo}
75
+ @s0v1 = Object.new
76
+ mock(@s0v1).value{:bar}
77
+ stub(@segment0).dimension_values{[@s0v0, @s0v1]}
78
+
79
+ mock(DimensionSegment).new(@sm0, anything){@segment0}
80
+
81
+ @sm1 = Object.new
82
+ @segment1 = Object.new
83
+ stub(@segment1).key{:segment1_key}
84
+ stub(@segment1).values{[:baz, :waz]}
85
+ @s1v0 = Object.new
86
+ stub(@s1v0).value{:baz}
87
+ @s1v1 = Object.new
88
+ stub(@s1v1).value{:waz}
89
+ stub(@s1v1).label{"WAZ"}
90
+ stub(@segment1).dimension_values{[@s1v0, @s1v1]}
91
+
92
+ mock(DimensionSegment).new(@sm1, anything){@segment1}
93
+
94
+ mock(@model).segment_models{[@sm0,@sm1]}
95
+
96
+ @dataset = Object.new
97
+
98
+ Dimension.new(@model, @dataset)
99
+ end
100
+
101
+ it "should construct a Dimension from a DimensionModel" do
102
+ d = build
103
+ d.dataset.should == @dataset
104
+ d.key.should == :foodim
105
+ d.label.should == "Dimension Foo"
106
+ end
107
+
108
+ describe "segment" do
109
+ it "should retrieve a segment by key" do
110
+ d = build
111
+ d.segment(:segment0_key).should == @segment0
112
+ d.segment(:segment1_key).should == @segment1
113
+ end
114
+
115
+ it "should retrieve a segment by key with deref operator" do
116
+ d = build
117
+ d[:segment0_key].should == @segment0
118
+ d[:segment1_key].should == @segment1
119
+ end
120
+ end
121
+
122
+ describe "values_for_segments" do
123
+ it "values_for_segment should extract values belonging to a segment" do
124
+ d = build
125
+ d.values_for_segments([:segment1_key, :segment0_key]).should == [:baz, :waz, :foo, :bar]
126
+ end
127
+ end
128
+
129
+ describe "dimension_value_for" do
130
+ it "should retrieve the DimensionValue for a segment value" do
131
+ d = build
132
+ d.dimension_value_for(:foo).should == @s0v0
133
+ d.dimension_value_for(:waz).should == @s1v1
134
+ end
135
+ end
136
+
137
+ describe "dimension_values_for_segments" do
138
+ it "should extract DimensionValues belonging to a list of segments" do
139
+ d = build
140
+ d.dimension_values_for_segments(nil).should == [@s0v0, @s0v1, @s1v0, @s1v1]
141
+ d.dimension_values_for_segments([:segment1_key]).should == [@s1v0, @s1v1]
142
+ d.dimension_values_for_segments([:segment1_key, :segment0_key]).should == [@s1v0, @s1v1, @s0v0, @s0v1]
143
+ end
144
+ end
145
+
146
+ describe "dimension_values" do
147
+ it "should return a list of DimensionValues from all segments" do
148
+ d = build
149
+ d.dimension_values.should == [@s0v0, @s0v1, @s1v0, @s1v1]
150
+ end
151
+ end
152
+
153
+ describe "label_for" do
154
+ it "should retrieve the label for a segment value" do
155
+ d = build
156
+ d.label_for(:foo).should == "Foo"
157
+ d.label_for(:waz).should == "WAZ"
158
+ d.label_for(:blah).should == nil
159
+ end
160
+ end
161
+ end
162
+
163
+ describe Measure do
164
+ def build
165
+ @dataset = Object.new
166
+ @model = Object.new
167
+ mock(@model).key{:count}
168
+ mock(@model).definition{"count(*)"}
169
+
170
+ Measure.new(@model, @dataset)
171
+ end
172
+
173
+ it "should construct a Measure from a MeasureModel" do
174
+ m = build
175
+ m.dataset.should == @dataset
176
+ m.key.should == :count
177
+ m.definition.should == "count(*)"
178
+ end
179
+ end
180
+
181
+ describe Dataset do
182
+ def build
183
+ @data = [{:foo=>10, :bar=>10, :count=>100, :sum=>200}, {:foo=>5, :bar=>4, :count=>10, :sum=>20}]
184
+
185
+ @model = Object.new
186
+
187
+ @mm0 = Object.new
188
+ @mm1 = Object.new
189
+ stub(@model).measure_models{[@mm0, @mm1]}
190
+
191
+ @m0 = Object.new
192
+ stub(@m0).key{:count}
193
+ @m1 = Object.new
194
+ stub(@m1).key{:sum}
195
+ mock(Measure).new(@mm0, anything){@m0}
196
+ mock(Measure).new(@mm1, anything){@m1}
197
+
198
+ @dm0 = Object.new
199
+ @dm1 = Object.new
200
+ stub(@model).dimension_models{[@dm0, @dm1]}
201
+
202
+ @d0 = Object.new
203
+ stub(@d0).key{:foo}
204
+ @d1 = Object.new
205
+ stub(@d1).key{:bar}
206
+
207
+ mock(Dimension).new(@dm0, anything){@d0}
208
+ mock(Dimension).new(@dm1, anything){@d1}
209
+
210
+ Dataset.new(@model, @data)
211
+ end
212
+
213
+ it "should construct a Dataset from a DatasetModel" do
214
+ ds = build
215
+ ds.data.should == @data
216
+ ds.dimensions.should == {:foo=>@d0, :bar=>@d1}
217
+ ds.measures.should == {:count=>@m0, :sum=>@m1}
218
+ ds.indexed_data.should == {{:foo=>10, :bar=>10}=>{:count=>100, :sum=>200}, {:foo=>5, :bar=>4}=>{:count=>10, :sum=>20}}
219
+ end
220
+
221
+ it "should index the dataset" do
222
+ ds = build
223
+ ds.indexed_data.should == {{:foo=>10, :bar=>10}=>{:count=>100, :sum=>200}, {:foo=>5, :bar=>4}=>{:count=>10, :sum=>20}}
224
+ end
225
+
226
+ it "should retrieve datapoints from the index" do
227
+ ds = build
228
+ ds.datapoint({:foo=>10, :bar=>10}, :count).should == 100
229
+ ds.datapoint({:foo=>5, :bar=>4}, :sum).should == 20
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,144 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'mdquery/dsl'
3
+
4
+ module MDQuery::DSL
5
+ describe MDQuery::DSL do
6
+ describe DimensionSegmentDSL do
7
+
8
+ it "should build a DimensionSegmentModel for a fixed dimension-value" do
9
+ dimension_model = Object.new
10
+ narrow_proc = lambda{}
11
+ values_proc = lambda{}
12
+ label_proc = lambda{}
13
+ modify_count_proc = lambda{}
14
+
15
+ dsl = DimensionSegmentDSL.new(:foo) do
16
+ fix_dimension :blah
17
+ narrow(&narrow_proc)
18
+ values(&values_proc)
19
+ label(&label_proc)
20
+ cast :sym
21
+ modify :count, &modify_count_proc
22
+ end
23
+
24
+ dsm = dsl.send(:build, dimension_model)
25
+
26
+ dsm.dimension_model.should == dimension_model
27
+ dsm.key.should == :foo
28
+ dsm.fixed_dimension_value.should == :blah
29
+ dsm.narrow_proc.should == narrow_proc
30
+ dsm.values_proc.should == values_proc
31
+ dsm.label_proc.should == label_proc
32
+ dsm.measure_modifiers.should == {:count=>modify_count_proc}
33
+ end
34
+
35
+ it "should build a DimensionSegmentModel for an extracted dimension-value" do
36
+ dimension_model = Object.new
37
+
38
+ dsl = DimensionSegmentDSL.new(:foo) do
39
+ extract_dimension "bar"
40
+ end
41
+
42
+ dsm = dsl.send(:build, dimension_model)
43
+ dsm.dimension_model.should == dimension_model
44
+ dsm.key.should == :foo
45
+ dsm.extract_dimension_query.should == "bar"
46
+ end
47
+
48
+
49
+ end
50
+
51
+ describe DimensionDSL do
52
+
53
+ it "should build a DimensionModel with a single segment" do
54
+ dsl = DimensionDSL.new(:foo) do
55
+ label :blah
56
+
57
+ segment(:bar) do
58
+ fix_dimension :woot
59
+ end
60
+ end
61
+
62
+ dm = dsl.send(:build)
63
+ dm.key.should == :foo
64
+ dm.label.should == :blah
65
+ dm.segment_models.count.should == 1
66
+ dm.segment_models.first.key.should == :bar
67
+ end
68
+
69
+ it "should build a DimensionModel with a list of segments" do
70
+ dsl = DimensionDSL.new(:foo) do
71
+
72
+ segment(:bar) do
73
+ fix_dimension :woot
74
+ end
75
+
76
+ segment(:baz) do
77
+ fix_dimension :bloop
78
+ end
79
+ end
80
+
81
+ dm = dsl.send(:build)
82
+ dm.key.should == :foo
83
+ dm.segment_models.count.should == 2
84
+ dm.segment_models[0].key.should == :bar
85
+ dm.segment_models[1].key.should == :baz
86
+ end
87
+ end
88
+
89
+ describe MeasureDSL do
90
+
91
+ it "should build a MeasureModel without cast" do
92
+ dsl = MeasureDSL.new(:foo, "count(*)")
93
+ mm = dsl.send(:build)
94
+ mm.key.should == :foo
95
+ mm.definition.should == "count(*)"
96
+ end
97
+
98
+ it "should build a MeasureModel with cast" do
99
+ dsl = MeasureDSL.new(:foo, "count(*)", :sym)
100
+ mm = dsl.send(:build)
101
+ mm.key.should == :foo
102
+ mm.definition.should == "count(*)"
103
+ mm.cast.should == :sym
104
+ end
105
+
106
+ end
107
+
108
+ describe DatasetDSL do
109
+
110
+ it "should build a DatasetModel with multiple dimensions and measures" do
111
+ source = Object.new
112
+
113
+ dsl = DatasetDSL.new do
114
+ source source
115
+
116
+ dimension :foo do
117
+ segment :foo_a do
118
+ fix_dimension :foo_a_value
119
+ end
120
+ end
121
+
122
+ dimension :bar do
123
+ segment :bar_a do
124
+ fix_dimension :bar_a_value
125
+ end
126
+ end
127
+
128
+ measure :count, "count(*)"
129
+ measure :avg, "avg(foo)"
130
+ end
131
+
132
+ ds = dsl.send(:build)
133
+ ds.source.should == source
134
+ ds.dimension_models.count.should == 2
135
+ ds.dimension_models[0].key.should == :foo
136
+ ds.dimension_models[1].key.should == :bar
137
+ ds.measure_models.count.should == 2
138
+ ds.measure_models[0].key.should == :count
139
+ ds.measure_models[1].key.should == :avg
140
+ end
141
+
142
+ end
143
+ end
144
+ end