sbf-dm-aggregates 1.3.0.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,325 @@
1
+ shared_examples 'It Has Setup Resources' do
2
+ before :all do
3
+ @mysql = defined?(DataMapper::Adapters::MysqlAdapter) && @adapter.is_a?(DataMapper::Adapters::MysqlAdapter)
4
+ @postgres = defined?(DataMapper::Adapters::PostgresAdapter) && @adapter.is_a?(DataMapper::Adapters::PostgresAdapter)
5
+
6
+ @skip = (@mysql || @postgres) && ENV['TZ'].to_s.downcase != 'utc'
7
+ end
8
+
9
+ before :all do
10
+ DataMapper.auto_migrate!
11
+
12
+ @birth_at = DateTime.now
13
+ @birth_on = Date.parse(@birth_at.to_s)
14
+ @birth_time = Time.parse(@birth_at.to_s)
15
+
16
+ @chuck = Knight.create(name: 'Chuck')
17
+ @larry = Knight.create(name: 'Larry')
18
+
19
+ Dragon.create(name: 'George',
20
+ is_fire_breathing: false, toes_on_claw: 3, birth_at: @birth_at, birth_on: @birth_on, birth_time: @birth_time, knight: @chuck)
21
+ Dragon.create(name: 'Puff',
22
+ is_fire_breathing: true, toes_on_claw: 4, birth_at: @birth_at, birth_on: @birth_on, birth_time: @birth_time, knight: @larry)
23
+ Dragon.create(name: nil, is_fire_breathing: true, toes_on_claw: 5, birth_at: nil, birth_on: nil, birth_time: nil)
24
+
25
+ gold_kilo_price = 277_738.70
26
+ @gold_tonne_price = gold_kilo_price * 10_000
27
+
28
+ Country.create(
29
+ name: 'China',
30
+ population: 1_330_044_605,
31
+ birth_rate: 13.71,
32
+ gold_reserve_tonnes: 600.0,
33
+ gold_reserve_value: 600.0 * @gold_tonne_price # => 32150000
34
+ )
35
+
36
+ Country.create(
37
+ name: 'United States',
38
+ population: 303_824_646,
39
+ birth_rate: 14.18,
40
+ gold_reserve_tonnes: 8133.5,
41
+ gold_reserve_value: 8133.5 * @gold_tonne_price
42
+ )
43
+
44
+ Country.create(
45
+ name: 'Brazil',
46
+ population: 191_908_598,
47
+ birth_rate: 16.04,
48
+ gold_reserve_tonnes: nil # example of no stats available
49
+ )
50
+
51
+ Country.create(
52
+ name: 'Russia',
53
+ population: 140_702_094,
54
+ birth_rate: 11.03,
55
+ gold_reserve_tonnes: 438.2,
56
+ gold_reserve_value: 438.2 * @gold_tonne_price
57
+ )
58
+
59
+ Country.create(
60
+ name: 'Japan',
61
+ population: 127_288_419,
62
+ birth_rate: 7.87,
63
+ gold_reserve_tonnes: 765.2,
64
+ gold_reserve_value: 765.2 * @gold_tonne_price
65
+ )
66
+
67
+ Country.create(
68
+ name: 'Mexico',
69
+ population: 109_955_400,
70
+ birth_rate: 20.04,
71
+ gold_reserve_tonnes: nil # example of no stats available
72
+ )
73
+
74
+ Country.create(
75
+ name: 'Germany',
76
+ population: 82_369_548,
77
+ birth_rate: 8.18,
78
+ gold_reserve_tonnes: 3417.4,
79
+ gold_reserve_value: 3417.4 * @gold_tonne_price
80
+ )
81
+
82
+ @approx_by = 0.0001
83
+ end
84
+ end
85
+
86
+ shared_examples 'An Aggregatable Class' do
87
+ describe '#size' do
88
+ it_behaves_like 'count with no arguments'
89
+ end
90
+
91
+ describe '#count' do
92
+ it_behaves_like 'count with no arguments'
93
+
94
+ context 'with a property name' do
95
+ it 'counts the results' do
96
+ expect(dragons.count(:name)).to eq 2
97
+ end
98
+
99
+ it 'counts the results with conditions having operators' do
100
+ expect(dragons.count(:name, :toes_on_claw.gt => 3)).to eq 1
101
+ end
102
+
103
+ it 'counts the results with raw conditions' do
104
+ statement = 'is_fire_breathing = ?'
105
+ expect(dragons.count(:name, conditions: [statement, false])).to eq 1
106
+ expect(dragons.count(:name, conditions: [statement, true])).to eq 1
107
+ end
108
+ end
109
+ end
110
+
111
+ describe '#min' do
112
+ context 'with no arguments' do
113
+ it 'raises an error' do
114
+ expect { dragons.min }.to raise_error(ArgumentError)
115
+ end
116
+ end
117
+
118
+ context 'with a property name' do
119
+ it 'provides the lowest value of an Integer property' do
120
+ expect(dragons.min(:toes_on_claw)).to eq 3
121
+ expect(countries.min(:population)).to eq 82_369_548
122
+ end
123
+
124
+ it 'provides the lowest value of a Float property' do
125
+ expect(countries.min(:birth_rate)).to be_kind_of(Float)
126
+ expect(countries.min(:birth_rate)).to be >= 7.87 - @approx_by # approx match
127
+ expect(countries.min(:birth_rate)).to be <= 7.87 + @approx_by # approx match
128
+ end
129
+
130
+ it 'provides the lowest value of a BigDecimal property' do
131
+ expect(countries.min(:gold_reserve_value)).to be_kind_of(BigDecimal)
132
+ expect(countries.min(:gold_reserve_value)).to eq BigDecimal('1217050983400.0')
133
+ end
134
+
135
+ it 'provides the lowest value of a DateTime property' do
136
+ pending_if 'TODO: returns incorrect value until DO handles TZs properly', @skip do
137
+ expect(dragons.min(:birth_at)).to be_kind_of(DateTime)
138
+ expect(dragons.min(:birth_at).to_s).to eq @birth_at.to_s
139
+ end
140
+ end
141
+
142
+ it 'provides the lowest value of a Date property' do
143
+ expect(dragons.min(:birth_on)).to be_kind_of(Date)
144
+ expect(dragons.min(:birth_on)).to eq @birth_on
145
+ end
146
+
147
+ it 'provides the lowest value of a Time property' do
148
+ expect(dragons.min(:birth_time)).to be_kind_of(Time)
149
+ expect(dragons.min(:birth_time).to_s).to eq @birth_time.to_s
150
+ end
151
+
152
+ it 'provides the lowest value when conditions provided' do
153
+ expect(dragons.min(:toes_on_claw, is_fire_breathing: true)).to eq 4
154
+ expect(dragons.min(:toes_on_claw, is_fire_breathing: false)).to eq 3
155
+ end
156
+ end
157
+ end
158
+
159
+ describe '#max' do
160
+ context 'with no arguments' do
161
+ it 'raises an error' do
162
+ expect { dragons.max }.to raise_error(ArgumentError)
163
+ end
164
+ end
165
+
166
+ context 'with a property name' do
167
+ it 'provides the highest value of an Integer property' do
168
+ expect(dragons.max(:toes_on_claw)).to eq 5
169
+ expect(countries.max(:population)).to eq 1_330_044_605
170
+ end
171
+
172
+ it 'provides the highest value of a Float property' do
173
+ expect(countries.max(:birth_rate)).to be_kind_of(Float)
174
+ expect(countries.max(:birth_rate)).to be >= 20.04 - @approx_by # approx match
175
+ expect(countries.max(:birth_rate)).to be <= 20.04 + @approx_by # approx match
176
+ end
177
+
178
+ it 'provides the highest value of a BigDecimal property' do
179
+ expect(countries.max(:gold_reserve_value)).to eq BigDecimal('22589877164500.0')
180
+ end
181
+
182
+ it 'provides the highest value of a DateTime property' do
183
+ pending_if 'TODO: returns incorrect value until DO handles TZs properly', @skip do
184
+ expect(dragons.min(:birth_at)).to be_kind_of(DateTime)
185
+ expect(dragons.min(:birth_at).to_s).to eq @birth_at.to_s
186
+ end
187
+ end
188
+
189
+ it 'provides the highest value of a Date property' do
190
+ expect(dragons.min(:birth_on)).to be_kind_of(Date)
191
+ expect(dragons.min(:birth_on)).to eq @birth_on
192
+ end
193
+
194
+ it 'provides the highest value of a Time property' do
195
+ expect(dragons.min(:birth_time)).to be_kind_of(Time)
196
+ expect(dragons.min(:birth_time).to_s).to eq @birth_time.to_s
197
+ end
198
+
199
+ it 'provides the highest value when conditions provided' do
200
+ expect(dragons.max(:toes_on_claw, is_fire_breathing: true)).to eq 5
201
+ expect(dragons.max(:toes_on_claw, is_fire_breathing: false)).to eq 3
202
+ end
203
+ end
204
+ end
205
+
206
+ describe '#avg' do
207
+ context 'with no arguments' do
208
+ it 'raises an error' do
209
+ expect { dragons.avg }.to raise_error(ArgumentError)
210
+ end
211
+ end
212
+
213
+ context 'with a property name' do
214
+ it 'provides the average value of an Integer property' do
215
+ expect(dragons.avg(:toes_on_claw)).to be_kind_of(Float)
216
+ expect(dragons.avg(:toes_on_claw)).to eq 4.0
217
+ end
218
+
219
+ it 'provides the average value of a Float property' do
220
+ mean_birth_rate = (13.71 + 14.18 + 16.04 + 11.03 + 7.87 + 20.04 + 8.18) / 7
221
+ expect(countries.avg(:birth_rate)).to be_kind_of(Float)
222
+ expect(countries.avg(:birth_rate)).to be >= mean_birth_rate - @approx_by # approx match
223
+ expect(countries.avg(:birth_rate)).to be <= mean_birth_rate + @approx_by # approx match
224
+ end
225
+
226
+ it 'provides the average value of a BigDecimal property' do
227
+ mean_gold_reserve_value = ((600.0 + 8133.50 + 438.20 + 765.20 + 3417.40) * @gold_tonne_price) / 5
228
+ expect(countries.avg(:gold_reserve_value)).to be_kind_of(BigDecimal)
229
+ expect(countries.avg(:gold_reserve_value)).to eq BigDecimal(mean_gold_reserve_value.to_s)
230
+ end
231
+
232
+ it 'provides the average value when conditions provided' do
233
+ expect(dragons.avg(:toes_on_claw, is_fire_breathing: true)).to eq 4.5
234
+ expect(dragons.avg(:toes_on_claw, is_fire_breathing: false)).to eq 3
235
+ end
236
+ end
237
+ end
238
+
239
+ describe '#sum' do
240
+ context 'with no arguments' do
241
+ it 'raises an error' do
242
+ expect { dragons.sum }.to raise_error(ArgumentError)
243
+ end
244
+ end
245
+
246
+ context 'with a property name' do
247
+ it 'provides the sum of values for an Integer property' do
248
+ expect(dragons.sum(:toes_on_claw)).to eq 12
249
+
250
+ total_population = 1_330_044_605 + 303_824_646 + 191_908_598 + 140_702_094 +
251
+ 127_288_419 + 109_955_400 + 82_369_548
252
+ expect(countries.sum(:population)).to eq total_population
253
+ end
254
+
255
+ it 'provides the sum of values for a Float property' do
256
+ total_tonnes = 600.0 + 8133.5 + 438.2 + 765.2 + 3417.4
257
+ expect(countries.sum(:gold_reserve_tonnes)).to be_kind_of(Float)
258
+ expect(countries.sum(:gold_reserve_tonnes)).to be >= total_tonnes - @approx_by # approx match
259
+ expect(countries.sum(:gold_reserve_tonnes)).to be <= total_tonnes + @approx_by # approx match
260
+ end
261
+
262
+ it 'provides the sum of values for a BigDecimal property' do
263
+ expect(countries.sum(:gold_reserve_value)).to eq BigDecimal('37090059214100.0')
264
+ end
265
+
266
+ it 'provides the average value when conditions provided' do
267
+ expect(dragons.sum(:toes_on_claw, is_fire_breathing: true)).to eq 9
268
+ expect(dragons.sum(:toes_on_claw, is_fire_breathing: false)).to eq 3
269
+ end
270
+ end
271
+ end
272
+
273
+ describe '#aggregate' do
274
+ context 'with no arguments' do
275
+ it 'raises an error' do
276
+ expect { dragons.aggregate }.to raise_error(ArgumentError)
277
+ end
278
+ end
279
+
280
+ context 'with only aggregate fields specified' do
281
+ it 'provides aggregate results' do
282
+ results = dragons.aggregate(:all.count, :name.count, :toes_on_claw.min, :toes_on_claw.max, :toes_on_claw.avg, :toes_on_claw.sum)
283
+ expect(results).to eq [3, 2, 3, 5, 4.0, 12]
284
+ end
285
+ end
286
+
287
+ context 'with aggregate fields and a property to group by' do
288
+ it 'provides aggregate results' do
289
+ results = dragons.aggregate(:all.count, :name.count,
290
+ :toes_on_claw.min, :toes_on_claw.max, :toes_on_claw.avg, :toes_on_claw.sum, :is_fire_breathing)
291
+ expect(results).to eq [[1, 1, 3, 3, 3.0, 3, false], [2, 1, 4, 5, 4.5, 9, true]]
292
+ end
293
+ end
294
+ end
295
+
296
+ describe 'query path issue' do
297
+ it 'does not break when a query path is specified' do
298
+ dragon = dragons.first(Dragon.knight.name => 'Chuck')
299
+ expect(dragon.name).to eq 'George'
300
+ end
301
+ end
302
+ end
303
+
304
+ shared_examples 'count with no arguments' do
305
+ it 'counts the results' do
306
+ expect(dragons.count).to eq 3
307
+
308
+ expect(countries.count).to eq 7
309
+ end
310
+
311
+ it 'counts the results with conditions having operators' do
312
+ expect(dragons.count(:toes_on_claw.gt => 3)).to eq 2
313
+
314
+ expect(countries.count(:birth_rate.lt => 12)).to eq 3
315
+ expect(countries.count(:population.gt => 1_000_000_000)).to eq 1
316
+ expect(countries.count(:population.gt => 2_000_000_000)).to eq 0
317
+ expect(countries.count(:population.lt => 10)).to eq 0
318
+ end
319
+
320
+ it 'counts the results with raw conditions' do
321
+ dragon_statement = 'is_fire_breathing = ?'
322
+ expect(dragons.count(conditions: [dragon_statement, false])).to eq 1
323
+ expect(dragons.count(conditions: [dragon_statement, true])).to eq 2
324
+ end
325
+ end
data/spec/rcov.opts ADDED
@@ -0,0 +1,6 @@
1
+ --exclude "spec,^/"
2
+ --sort coverage
3
+ --callsites
4
+ --xrefs
5
+ --profile
6
+ --text-summary
@@ -0,0 +1,55 @@
1
+
2
+ require 'dm-core/spec/setup'
3
+ require 'dm-core/spec/lib/adapter_helpers'
4
+ require 'dm-core/spec/lib/pending_helpers'
5
+
6
+ require 'dm-aggregates'
7
+ require 'dm-migrations'
8
+
9
+ require 'public/shared/aggregate_shared_spec'
10
+
11
+ DataMapper::Spec.setup
12
+
13
+ RSpec.configure do |config|
14
+ config.extend(DataMapper::Spec::Adapters::Helpers)
15
+ config.include(DataMapper::Spec::PendingHelpers)
16
+
17
+ config.before(:all) do
18
+ # A simplistic example, using with an Integer property
19
+ class ::Knight
20
+ include DataMapper::Resource
21
+
22
+ property :id, Serial
23
+ property :name, String
24
+ end
25
+
26
+ class ::Dragon
27
+ include DataMapper::Resource
28
+
29
+ property :id, Serial
30
+ property :name, String
31
+ property :is_fire_breathing, Boolean
32
+ property :toes_on_claw, Integer
33
+ property :birth_at, DateTime
34
+ property :birth_on, Date
35
+ property :birth_time, Time
36
+
37
+ belongs_to :knight, required: false
38
+ end
39
+
40
+ # A more complex example, with BigDecimal and Float properties
41
+ # Statistics taken from CIA World Factbook:
42
+ # https://www.cia.gov/library/publications/the-world-factbook/
43
+ class ::Country
44
+ include DataMapper::Resource
45
+
46
+ property :id, Serial
47
+ property :name, String, required: true
48
+ property :population, Integer
49
+ property :birth_rate, Float, precision: 4, scale: 2
50
+ property :gold_reserve_tonnes, Float, precision: 6, scale: 2
51
+ property :gold_reserve_value, Decimal, precision: 15, scale: 1 # approx. value in USD
52
+ end
53
+ DataMapper.finalize
54
+ end
55
+ end
@@ -0,0 +1,6 @@
1
+ desc 'Release all gems (native, binaries for JRuby and Windows)'
2
+ task :release do
3
+ command = "gem push sbf-dm-aggregates-1.3.0.beta.gem"
4
+ puts "Executing #{command.inspect}:"
5
+ # sh command
6
+ end
data/tasks/spec.rake ADDED
@@ -0,0 +1,21 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ begin
4
+ task(:default).clear
5
+ task(:spec).clear
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do |spec|
8
+ spec.pattern = 'spec/**/*_spec.rb'
9
+
10
+ require 'simplecov'
11
+ SimpleCov.start do
12
+ minimum_coverage 100
13
+ end
14
+ end
15
+ rescue LoadError
16
+ task :spec do
17
+ abort 'rspec is not available. In order to run spec, you must: gem install rspec'
18
+ end
19
+ end
20
+
21
+ task default: :spec
data/tasks/yard.rake ADDED
@@ -0,0 +1,9 @@
1
+ begin
2
+ require 'yard'
3
+
4
+ YARD::Rake::YardocTask.new
5
+ rescue LoadError
6
+ task :yard do
7
+ abort 'YARD is not available. In order to run yard, you must: gem install yard'
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ begin
2
+ require 'pathname'
3
+ require 'yardstick/rake/measurement'
4
+ require 'yardstick/rake/verify'
5
+
6
+ # yardstick_measure task
7
+ Yardstick::Rake::Measurement.new
8
+
9
+ # verify_measurements task
10
+ Yardstick::Rake::Verify.new do |verify|
11
+ verify.threshold = 100
12
+ end
13
+ rescue LoadError
14
+ %w(yardstick_measure verify_measurements).each do |name|
15
+ task name.to_s do
16
+ abort "Yardstick is not available. In order to run #{name}, you must: gem install yardstick"
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sbf-dm-aggregates
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0.beta
5
+ platform: ruby
6
+ authors:
7
+ - Emmanuel Gomez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-10-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sbf-dm-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.3.0.beta
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.3.0.beta
27
+ description: DataMapper plugin providing support for aggregates, functions on collections
28
+ and datasets.
29
+ email:
30
+ - emmanuel.gomez@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files:
34
+ - LICENSE
35
+ - README.rdoc
36
+ files:
37
+ - ".gitignore"
38
+ - ".rspec"
39
+ - ".rubocop.yml"
40
+ - Gemfile
41
+ - LICENSE
42
+ - README.rdoc
43
+ - Rakefile
44
+ - VERSION
45
+ - dm-aggregates.gemspec
46
+ - lib/dm-aggregates.rb
47
+ - lib/dm-aggregates/adapters/dm-do-adapter.rb
48
+ - lib/dm-aggregates/aggregate_functions.rb
49
+ - lib/dm-aggregates/collection.rb
50
+ - lib/dm-aggregates/core_ext/symbol.rb
51
+ - lib/dm-aggregates/functions.rb
52
+ - lib/dm-aggregates/model.rb
53
+ - lib/dm-aggregates/query.rb
54
+ - lib/dm-aggregates/repository.rb
55
+ - lib/dm-aggregates/symbol_operators.rb
56
+ - lib/dm-aggregates/version.rb
57
+ - spec/isolated/require_after_setup_spec.rb
58
+ - spec/isolated/require_before_setup_spec.rb
59
+ - spec/isolated/require_spec.rb
60
+ - spec/public/collection_spec.rb
61
+ - spec/public/model_spec.rb
62
+ - spec/public/shared/aggregate_shared_spec.rb
63
+ - spec/rcov.opts
64
+ - spec/spec_helper.rb
65
+ - tasks/release.rake
66
+ - tasks/spec.rake
67
+ - tasks/yard.rake
68
+ - tasks/yardstick.rake
69
+ homepage: https://datamapper.org
70
+ licenses:
71
+ - Nonstandard
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 2.7.8
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">"
85
+ - !ruby/object:Gem::Version
86
+ version: 1.3.1
87
+ requirements: []
88
+ rubygems_version: 3.4.10
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: DataMapper plugin providing support for aggregates on collections
92
+ test_files: []