jzimmek-reportme 0.2.2 → 0.4.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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.2
1
+ 0.4.0
@@ -2,5 +2,31 @@ require 'rubygems'
2
2
  require 'activerecord'
3
3
  require "actionmailer"
4
4
 
5
- require 'reportme/report'
6
- require 'reportme/report_factory'
5
+ class Object
6
+
7
+ def self.dsl_attr(name, opts={})
8
+
9
+ self.class.send(:define_method, name) do |value|
10
+ key = "@@#{name}".to_sym
11
+ class_variable_set(key, value)
12
+ end
13
+
14
+
15
+ self.class.send(:define_method, "#{name}_value".to_sym) do
16
+ class_variable_get("@@#{name}".to_sym)
17
+ end
18
+
19
+ self.class.send(:define_method, "#{name}_reset".to_sym) do
20
+ default = opts[:default].try(:call)
21
+ class_variable_set("@@#{name}".to_sym, default)
22
+ end
23
+
24
+ default = opts[:default].try(:call)
25
+ send(name, default)
26
+
27
+ end
28
+
29
+ end
30
+
31
+ require 'reportme/report_factory'
32
+
@@ -2,17 +2,31 @@ module Reportme
2
2
  class Report
3
3
 
4
4
  attr_reader :name
5
-
6
- def initialize(report_factory, name)
5
+
6
+ def initialize(report_factory, name, temporary=false)
7
7
  @report_factory = report_factory
8
8
  @name = name
9
9
  @periods = [:today, :day, :week, :calendar_week, :month, :calendar_month]
10
+ @depends_on = []
11
+ @temporary = temporary
10
12
  end
11
13
 
14
+ def temporary?
15
+ @temporary
16
+ end
17
+
12
18
  def source(&block)
13
19
  @source = block
14
20
  end
15
21
 
22
+ def depends_on(dependencies=[])
23
+ @depends_on += dependencies
24
+ end
25
+
26
+ def dependencies
27
+ @depends_on
28
+ end
29
+
16
30
  def periods(wanted_periods=[])
17
31
  unless wanted_periods.blank?
18
32
  @periods.clear
@@ -31,7 +45,8 @@ module Reportme
31
45
  end
32
46
 
33
47
  def table_name(period)
34
- "#{name}_#{period}"
48
+ prefix = temporary? ? "tmp_" : ""
49
+ "#{prefix}#{name}_#{period}"
35
50
  end
36
51
 
37
52
  def table_exist?(period)
@@ -3,51 +3,42 @@ require 'reportme/mailer'
3
3
 
4
4
  module Reportme
5
5
  class ReportFactory
6
-
7
- def self.create(&block)
8
- rme = ReportFactory.new
9
- rme.instance_eval(&block)
10
- rme
11
- end
6
+
7
+ dsl_attr :reports, :default => lambda{ [] }
8
+ dsl_attr :subscribtions, :default => lambda{ {} }
9
+ dsl_attr :properties, :default => lambda{ {} }
10
+ dsl_attr :init
12
11
 
13
12
  def initialize
14
- @reports = []
15
- @subscribtions = {}
16
13
  @report_exists_cache = []
17
14
  end
18
15
 
19
- def connection(properties)
20
- @properties = properties
21
- ActiveRecord::Base.establish_connection(@properties)
16
+ def self.connection(properties)
17
+ ActiveRecord::Base.establish_connection(@@properties = properties)
22
18
  end
23
19
 
24
- def smtp(settings)
20
+ def self.smtp(settings)
25
21
  ActionMailer::Base.smtp_settings = settings
26
22
  end
27
23
 
28
- def mail(from, recipients, subject, body, attachments=[])
24
+ def self.mail(from, recipients, subject, body, attachments=[])
29
25
  Mailer.deliver_message(from, recipients, subject, body, attachments)
30
26
  end
31
27
 
32
- def since(since)
33
- raise "since has already been set to '#{@since}' and cannot be changed to: '#{since}'" if @since
34
- raise "since cannot be in the future" if since.future?
35
-
36
- @since = since.to_date
37
- end
38
-
39
- def init(&block)
40
- raise "only one init block allowed" if @init
41
- @init = block;
28
+ def self.init(&block)
29
+ raise "only one init block allowed" if @@init
30
+ @@init = block;
42
31
  end
43
32
 
44
33
  def self.periods(today)
45
34
 
35
+ today = today.to_date
36
+
46
37
  r = []
47
38
  p = []
48
39
 
49
40
  # period "today" will never be generated for previous days
50
- p << :today if today.to_date == Date.today
41
+ p << :today if today == Date.today
51
42
  p += [:day, :week, :calendar_week, :month, :calendar_month]
52
43
 
53
44
  p.each do |period|
@@ -100,13 +91,17 @@ module Reportme
100
91
  exists
101
92
  end
102
93
 
103
- def schema_name
104
- schema = @properties[:database]
94
+ def self.schema_name
95
+ schema = @@properties[:database]
105
96
  raise "missing :database in connection properties" unless schema
106
97
  schema
107
98
  end
108
-
99
+
109
100
  def columns(table_name)
101
+ self.class.columns(table_name)
102
+ end
103
+
104
+ def self.columns(table_name)
110
105
  sql = <<-SQL
111
106
  select
112
107
  column_name
@@ -120,23 +115,17 @@ module Reportme
120
115
  select_values(sql)
121
116
  end
122
117
 
123
- def reset
124
- @report_exists_cache.clear
125
- exec("drop table if exists #{report_information_table_name};")
118
+ def ensure_report_tables_exist(report)
126
119
 
127
- ReportFactory.periods(@since).each do |period|
128
- @reports.each do |r|
129
- exec("drop table if exists #{r.table_name(period[:name])};")
130
- end
131
- end
132
- end
133
-
134
- def ensure_report_table_exist(report, period)
135
- unless report.table_exist?(period)
136
- table_name = report.table_name(period)
137
- sql = report.sql('0000-00-00 00:00:00', '0000-00-00 00:00:00', period)
120
+ [:today, :day, :week, :calendar_week, :month, :calendar_month].each do |period|
121
+ next unless report.wants_period?(period)
122
+
123
+ unless report.table_exist?(period)
124
+ table_name = report.table_name(period)
125
+ sql = report.sql('0000-00-00 00:00:00', '0000-00-00 00:00:00', period)
138
126
 
139
- exec("create table #{table_name} ENGINE=InnoDB default CHARSET=utf8 as #{sql} limit 0;")
127
+ exec("create table #{table_name} ENGINE=InnoDB default CHARSET=utf8 as #{sql} limit 0;")
128
+ end
140
129
  end
141
130
  end
142
131
 
@@ -186,46 +175,152 @@ module Reportme
186
175
  exec("insert into #{report_information_table_name} values ('#{table_name}', '#{von}', '#{bis}', now());")
187
176
  exec("insert into #{table_name} select #{column_names.join(',')} from #{report.table_name(:day)} as d where d.von between '#{von}' and '#{(_von + num_days.days).strftime("%Y-%m-%d 00:00:00")}';")
188
177
 
189
- notify_subscriber(report.name, period_name, _von)
178
+ self.class.notify_subscriber(report.name, period_name, _von)
190
179
  end
191
180
  end
192
181
  end
193
182
 
194
- def run
195
-
196
- @init.call if @init
197
- @since = Date.today unless @since
183
+ def validate_dependencies
184
+ @@reports.each do |r|
185
+ r.dependencies.each do |d|
186
+ raise "report #{r.name} depends on non existing report #{d}" unless self.class.has_report?(d)
187
+ end
188
+ end
189
+ end
198
190
 
199
- ensure_report_informations_table_exist
200
-
191
+ def __fill_periods_queue(since)
201
192
  periods_queue = []
202
193
 
203
- while !@since.future?
204
- ReportFactory.periods(@since).each do |period|
194
+ loop do
195
+ ReportFactory.periods(since).each do |period|
205
196
  periods_queue << period
206
197
  end
207
- @since += 1.day
198
+ since += 1.day
199
+ break if since.future?
200
+ end
201
+
202
+ periods_queue
203
+ end
204
+
205
+ def run(since=Date.today)
206
+
207
+ raise "since cannot be in the future" if since.future?
208
+
209
+ begin
210
+ @@init.call if @@init
211
+
212
+ ensure_report_informations_table_exist
213
+
214
+ validate_dependencies
215
+
216
+ run_dependency_aware(@@reports) do |report|
217
+ ensure_report_tables_exist(report)
218
+ end
219
+
220
+ periods_queue = __fill_periods_queue(since)
221
+
222
+ # we will generate all daily reports first.
223
+ # this will speed up generation of weekly and monthly reports.
224
+
225
+ self.class.__sort_periods(periods_queue).each do |period|
226
+
227
+ run_dependency_aware(@@reports) do |report|
228
+ __report_period(report, period)
229
+ end
230
+
231
+ end
232
+ ensure
233
+ @@reports.each do |report|
234
+
235
+ if report.temporary?
236
+ [:today, :day, :week, :calendar_week, :month, :calendar_month].each do |period|
237
+
238
+ table_name = report.table_name(period)
239
+
240
+ exec("delete from #{report_information_table_name} where report = '#{table_name}';")
241
+ exec("drop table if exists #{table_name};")
242
+
243
+ end
244
+ end
245
+
246
+ end
208
247
  end
209
248
 
210
- # we will generate all daily reports first.
211
- # this will speed up generation of weekly and monthly reports.
249
+ end
250
+
251
+ def run_dependency_aware(reports, &block)
252
+
253
+ dependencies = __dependency_hash
254
+ reports = reports.dup
212
255
 
213
- periods_queue.reject{|p| p[:name] != :day}.each do |period|
214
- report_period(period)
256
+ while true
257
+
258
+ break if reports.blank?
259
+
260
+ num_run = 0
261
+
262
+ reports.each do |r|
263
+
264
+ unless dependencies[r.name].blank?
265
+ puts "report ['#{r.name}'] waits on dependencies: #{dependencies[r.name].collect{|d|d.name}.join(',')}"
266
+ next
267
+ end
268
+
269
+ block.call(r)
270
+
271
+ dependencies.each_pair do |key, values|
272
+
273
+ if values.include?(r)
274
+ # puts "remove '#{r[:name]}' from '#{key}' list of dependencies"
275
+ values.delete(r)
276
+ end
277
+ end
278
+
279
+ num_run += 1
280
+ reports.delete(r)
281
+
282
+ end
283
+
284
+ raise "deadlock" if num_run == 0
285
+
215
286
  end
287
+
288
+ end
289
+
290
+ def self.__sort_periods(periods)
291
+
292
+ sorting = {
293
+ :today => 99,
294
+ :day => 1,
295
+ :week => 2,
296
+ :calendar_week => 3,
297
+ :month => 4,
298
+ :calendar_month => 5
299
+ }
300
+
301
+ periods.sort{|a, b| sorting[a[:name]] <=> sorting[b[:name]]}
302
+
303
+ end
216
304
 
217
- periods_queue.reject{|p| p[:name] == :day}.each do |period|
218
- report_period(period)
305
+ def __dependency_hash
306
+ dependencies = {}
307
+ @@reports.each do |r|
308
+
309
+ dependencies[r.name] = []
310
+
311
+ r.dependencies.each do |d|
312
+ dependencies[r.name] << self.class.report_by_name(d)
313
+ end
219
314
  end
220
315
 
316
+ dependencies
221
317
  end
222
318
 
223
- def report_period(period)
224
- @reports.each do |r|
319
+ def __report_period(r, period)
225
320
 
226
- period_name = period[:name]
321
+ period_name = period[:name]
227
322
 
228
- next unless r.wants_period?(period_name)
323
+ if r.wants_period?(period_name)
229
324
 
230
325
  _von = period[:von]
231
326
  _bis = period[:bis]
@@ -239,8 +334,6 @@ module Reportme
239
334
  sql = r.sql(von, bis, period_name)
240
335
 
241
336
  puts "report: #{r.table_name(period_name)} von: #{von}, bis: #{bis}"
242
-
243
- ensure_report_table_exist(r, period_name)
244
337
 
245
338
  report_exists = report_exists?(table_name, von, bis)
246
339
 
@@ -257,30 +350,35 @@ module Reportme
257
350
  if !report_exists || period_name == :today
258
351
  ActiveRecord::Base.transaction do
259
352
  exec("insert into #{report_information_table_name} values ('#{table_name}', '#{von}', '#{bis}', now());") unless report_exists
260
-
261
- if period_name == :today
262
- exec("truncate #{table_name};")
263
- end
353
+
354
+ exec("truncate #{table_name};") if period_name == :today
264
355
 
265
356
  exec("insert into #{table_name} #{sql};")
266
357
 
267
- notify_subscriber(r.name, period_name, _von)
358
+ self.class.notify_subscriber(r.name, period_name, _von)
268
359
  end
269
360
  end
270
361
 
271
-
272
362
  end
273
363
 
274
364
  end
275
-
365
+
276
366
  def exec(sql)
367
+ self.class.exec(sql)
368
+ end
369
+
370
+ def self.exec(sql)
277
371
  puts "// ------------------------"
278
372
  puts "exec: #{sql}"
279
373
  puts "------------------------ //"
280
374
  ActiveRecord::Base.connection.execute(sql)
281
375
  end
282
-
376
+
283
377
  def select_value(sql)
378
+ self.class.select_value(sql)
379
+ end
380
+
381
+ def self.select_value(sql)
284
382
  puts "// ------------------------"
285
383
  puts "select_value: #{sql}"
286
384
  puts "------------------------ //"
@@ -288,6 +386,10 @@ module Reportme
288
386
  end
289
387
 
290
388
  def select_one(sql)
389
+ self.class.select_one(sql)
390
+ end
391
+
392
+ def self.select_one(sql)
291
393
  puts "// ------------------------"
292
394
  puts "select_one: #{sql}"
293
395
  puts "------------------------ //"
@@ -295,6 +397,10 @@ module Reportme
295
397
  end
296
398
 
297
399
  def select_all(sql)
400
+ self.class.select_all(sql)
401
+ end
402
+
403
+ def self.select_all(sql)
298
404
  puts "// ------------------------"
299
405
  puts "select_all: #{sql}"
300
406
  puts "------------------------ //"
@@ -302,6 +408,10 @@ module Reportme
302
408
  end
303
409
 
304
410
  def select_rows(sql)
411
+ self.class.select_rows(sql)
412
+ end
413
+
414
+ def self.select_rows(sql)
305
415
  puts "// ------------------------"
306
416
  puts "select_rows: #{sql}"
307
417
  puts "------------------------ //"
@@ -309,32 +419,40 @@ module Reportme
309
419
  end
310
420
 
311
421
  def select_values(sql)
422
+ self.class.select_values(sql)
423
+ end
424
+
425
+ def self.select_values(sql)
312
426
  puts "// ------------------------"
313
427
  puts "select_values: #{sql}"
314
428
  puts "------------------------ //"
315
429
  ActiveRecord::Base.connection.select_values(sql)
316
430
  end
317
431
 
318
- def has_subscribtion?(report_name)
319
- !@subscribtions[report_name].blank?
432
+ def self.has_subscribtion?(report_name)
433
+ !@@subscribtions[report_name].blank?
434
+ end
435
+
436
+ def self.report_by_name(report_name)
437
+ @@reports.find{|r|r.name == report_name}
320
438
  end
321
439
 
322
- def has_report?(report_name)
323
- !@reports.find{|r|r.name == report_name}.blank?
440
+ def self.has_report?(report_name)
441
+ !@@reports.find{|r|r.name == report_name}.blank?
324
442
  end
325
443
 
326
- def subscribe(report_name, &block)
444
+ def self.subscribe(report_name, &block)
327
445
  report_name = report_name.to_sym
328
446
 
329
447
  raise "report: #{report_name} does not exist" unless has_report?(report_name)
330
448
 
331
- existing = @subscribtions[report_name] || (@subscribtions[report_name] = [])
449
+ existing = @@subscribtions[report_name] || (@@subscribtions[report_name] = [])
332
450
  existing << block
333
451
  end
334
452
 
335
- def notify_subscriber(report_name, period, von)
453
+ def self.notify_subscriber(report_name, period, von)
336
454
 
337
- (@subscribtions[report_name] || []).each do |subscription|
455
+ (@@subscribtions[report_name] || []).each do |subscription|
338
456
  begin
339
457
  subscription.call(period, von)
340
458
  rescue Exception => e
@@ -344,14 +462,14 @@ module Reportme
344
462
 
345
463
  end
346
464
 
347
- def report(name, &block)
465
+ def self.report(name, temporary=false, &block)
348
466
 
349
467
  name = name.to_sym
350
468
 
351
- r = Report.new(self, name)
469
+ r = Report.new(self, name, temporary)
352
470
  r.instance_eval(&block)
353
471
 
354
- @reports << r
472
+ @@reports << r
355
473
  end
356
474
 
357
475
  end
@@ -16,55 +16,73 @@ class ReportmeTest < Test::Unit::TestCase
16
16
  )
17
17
  SQL
18
18
  end
19
+
20
+ class TestReport < Reportme::ReportFactory
21
+ end
19
22
 
20
23
  def create_visit_report_factory(opts={})
21
24
 
22
25
  defaults = {
23
26
  :periods => [],
24
- :since => DateTime.now
27
+ :init => lambda {}
25
28
  }
26
29
 
27
30
  opts = defaults.merge(opts)
28
31
 
29
- @rme = Reportme::ReportFactory.create do
30
-
31
- since opts[:since]
32
- connection :adapter => "mysql", :database => "report_me_test", :username => "root", :password => "root", :host => "localhost", :port => 3306
33
-
34
- report :visits do
35
- periods opts[:periods]
36
- source do |von, bis|
37
- <<-SQL
38
- select
39
- '#{von}' as von,
40
- date(created_at) as datum,
41
- channel,
42
- count(1) as cnt
43
- from
44
- visits
45
- where
46
- created_at between '#{von}' and '#{bis}'
47
- group by
48
- date(created_at),
49
- channel
50
- SQL
51
- end
32
+ TestReport.connection :adapter => "mysql", :database => "report_me_test", :username => "root", :password => "root", :host => "localhost", :port => 3306
33
+ TestReport.init do
34
+ opts[:init].call
35
+ end
36
+ TestReport.report :visits do
37
+ periods opts[:periods]
38
+ source do |von, bis|
39
+ <<-SQL
40
+ select
41
+ '#{von}' as von,
42
+ date(created_at) as datum,
43
+ channel,
44
+ count(1) as cnt
45
+ from
46
+ visits
47
+ where
48
+ created_at between '#{von}' and '#{bis}'
49
+ group by
50
+ date(created_at),
51
+ channel
52
+ SQL
52
53
  end
53
54
  end
54
- @rme
55
+
56
+ @rme = TestReport.new
57
+
55
58
  end
56
59
 
57
60
  def exec(sql)
61
+ puts "exec: #{sql}"
58
62
  ActiveRecord::Base.connection.execute(sql)
59
63
  end
60
64
 
61
65
  def one(sql)
66
+ puts "one: #{sql}"
62
67
  ActiveRecord::Base.connection.select_one(sql)
63
68
  end
64
69
 
65
70
  def teardown
66
71
  unless @debug
67
- @rme.reset if @rme
72
+ exec("drop table if exists report_informations;")
73
+
74
+ [:today, :day, :week, :calendar_week, :month, :calendar_month].each do |period|
75
+
76
+ TestReport.reports_value.each do |report|
77
+ exec("drop table if exists #{report.table_name(period)};")
78
+ end
79
+ end
80
+
81
+ TestReport.reports_reset
82
+ TestReport.init_reset
83
+ TestReport.subscribtions_reset
84
+ TestReport.properties_reset
85
+
68
86
  exec("truncate visits;");
69
87
  end
70
88
  end
@@ -82,6 +100,7 @@ class ReportmeTest < Test::Unit::TestCase
82
100
  assert_equal 2, one("select cnt from visits_today where channel = 'sem' and datum = curdate()")["cnt"].to_i
83
101
  end
84
102
 
103
+
85
104
  should "create visitors in the today report for channel sem and seo" do
86
105
  exec("insert into visits values (null, 'sem', now())");
87
106
  exec("insert into visits values (null, 'sem', now())");
@@ -126,6 +145,7 @@ class ReportmeTest < Test::Unit::TestCase
126
145
 
127
146
  should "create a daily report for the previous 3 days" do
128
147
 
148
+
129
149
  #should be ignored
130
150
  exec("insert into visits values (null, 'sem', curdate());");
131
151
 
@@ -137,7 +157,7 @@ class ReportmeTest < Test::Unit::TestCase
137
157
  # should be ignored
138
158
  exec("insert into visits values (null, 'sem', date_sub(curdate(), interval 5 day));");
139
159
 
140
- create_visit_report_factory(:since => 3.days.ago,:periods => [:day]).run
160
+ create_visit_report_factory(:periods => [:day]).run(3.days.ago)
141
161
  assert_equal 4, one("select count(1) as cnt from visits_day where von between date_sub(curdate(), interval 4 day) and date_sub(curdate(), interval 1 day)")["cnt"].to_i
142
162
  end
143
163
 
@@ -159,10 +179,12 @@ class ReportmeTest < Test::Unit::TestCase
159
179
  # should be ignored in weekly
160
180
  exec("insert into visits values (null, 'sem', date_sub(curdate(), interval 9 day));");
161
181
 
162
- create_visit_report_factory(:since => 10.days.ago, :periods => [:day]).run
182
+ create_visit_report_factory(:periods => [:day]).run(10.days.ago)
163
183
 
164
184
  exec("truncate visits;")
165
185
 
186
+ Reportme::ReportFactory.init_reset
187
+
166
188
  create_visit_report_factory(:periods => [:week]).run
167
189
 
168
190
  assert_equal 7, one("select count(1) as cnt from visits_week where date(von) between date_sub(curdate(), interval 7 day) and date_sub(curdate(), interval 1 day)")["cnt"].to_i
@@ -320,7 +342,7 @@ class ReportmeTest < Test::Unit::TestCase
320
342
 
321
343
  assert_equal '2009-04-01 00:00:00'.to_datetime, periods[:calendar_month][:von]
322
344
  assert_equal '2009-04-30 23:59:59'.to_datetime, periods[:calendar_month][:bis]
323
-
345
+
324
346
  ##
325
347
  # today
326
348
  ##
@@ -333,7 +355,7 @@ class ReportmeTest < Test::Unit::TestCase
333
355
  assert_equal "#{today.strftime('%Y-%m-%d')} 23:59:59".to_datetime, periods[:today][:bis]
334
356
 
335
357
  end
336
-
358
+
337
359
  should "create the calendar_weekly report by using 7 daily reports" do
338
360
 
339
361
  today = '2009-06-24'
@@ -353,58 +375,84 @@ class ReportmeTest < Test::Unit::TestCase
353
375
  exec("insert into visits values (null, 'sem', date_sub('#{today}', interval 9 day));");
354
376
  # should be ignored in weekly
355
377
  exec("insert into visits values (null, 'sem', date_sub('#{today}', interval 10 day));");
378
+
379
+ create_visit_report_factory(:periods => [:day]).run(15.days.ago)
380
+
381
+ exec("truncate visits;")
382
+
383
+ Reportme::ReportFactory.init_reset
356
384
 
357
- create_visit_report_factory(:since => 15.days.ago, :periods => [:day]).run
385
+ d1 = Date.today
386
+ d2 = today.to_date
387
+
388
+ num_days = 0
358
389
 
359
- exec("truncate visits;")
390
+ while d2.past?
391
+ d2 += 1.day
392
+ num_days += 1
393
+ end
394
+
395
+ create_visit_report_factory(:periods => [:calendar_week]).run(num_days.days.ago)
360
396
 
361
- create_visit_report_factory(:periods => [:calendar_week]).run
397
+ day_lastweek = today.to_date - 7.days
398
+
399
+ monday = day_lastweek - (day_lastweek.cwday - 1).days
400
+
401
+ von, bis = [monday, monday + 6.days]
402
+
403
+ von = von.to_datetime
404
+ bis = bis.to_datetime + 23.hours + 59.minutes + 59.seconds
362
405
 
363
- assert_equal 7, one("select count(1) as cnt from visits_calendar_week where von between '2009-06-15 00:00:00' and '2009-06-21 00:00:00'")["cnt"].to_i
406
+ sql = "select count(1) as cnt from visits_calendar_week where von between '#{von.strftime('%Y-%m-%d 00:00:00')}' and '#{bis.strftime('%Y-%m-%d 23:59:59')}'"
407
+
408
+ # assert_equal 7, one("select count(1) as cnt from visits_calendar_week where von between '2009-06-15 00:00:00' and '2009-06-21 00:00:00'")["cnt"].to_i
409
+ assert_equal 7, one(sql)["cnt"].to_i
364
410
 
365
411
  end
366
-
412
+
367
413
  should "probe existing reports" do
368
414
  rme = create_visit_report_factory
369
- assert rme.has_report?(:visits)
370
- assert !rme.has_report?(:some_not_existing_report)
415
+ assert rme.class.has_report?(:visits)
416
+ assert !rme.class.has_report?(:some_not_existing_report)
371
417
  end
372
-
418
+
373
419
  should "subscribe to visits report" do
374
420
  rme = create_visit_report_factory
375
- rme.subscribe :visits do
421
+ rme.class.subscribe :visits do
376
422
  end
377
- assert rme.has_subscribtion?(:visits)
423
+ assert rme.class.has_subscribtion?(:visits)
378
424
  end
379
425
 
380
426
  should "fail on subscribtion to not existing report" do
381
427
  rme = create_visit_report_factory
382
428
  assert_raise RuntimeError do
383
- rme.subscribe :some_not_existing_report do
429
+ rme.class.subscribe :some_not_existing_report do
384
430
  end
385
431
  end
386
432
  end
387
433
 
388
434
  should "notify subscriptions" do
389
435
  notifed = false
390
-
436
+
391
437
  rme = create_visit_report_factory
392
- rme.subscribe :visits do
438
+ rme.class.subscribe :visits do
393
439
  notifed = true
394
440
  end
395
441
 
396
- rme.notify_subscriber(:visits, :day, '2009-01-01'.to_datetime)
442
+ rme.class.notify_subscriber(:visits, :day, '2009-01-01'.to_datetime)
397
443
 
398
444
  assert notifed
399
445
  end
400
446
 
401
447
  should "call initializer before running reports" do
402
448
  initialized = false
403
-
404
- rme = create_visit_report_factory
405
- rme.init do
406
- initialized = true
407
- end
449
+
450
+ rme = create_visit_report_factory({
451
+ :init => lambda {
452
+ initialized = true
453
+ }
454
+ })
455
+
408
456
  rme.run
409
457
 
410
458
  assert initialized
@@ -412,26 +460,115 @@ class ReportmeTest < Test::Unit::TestCase
412
460
  end
413
461
 
414
462
  should "fail when multiple init blocks are defined" do
415
-
463
+
416
464
  rme = create_visit_report_factory
417
- rme.init do
418
- end
419
465
 
420
466
  assert_raise RuntimeError do
421
- rme.init do
467
+ rme.class.init do
422
468
  end
423
469
  end
424
470
  end
425
471
 
426
- should "fail on multiple since calls" do
472
+ should "fail on non existing report dependencies" do
473
+
474
+ class ReportDependencyTestReport < Reportme::ReportFactory
475
+ connection :adapter => "mysql", :database => "report_me_test", :username => "root", :password => "root", :host => "localhost", :port => 3306
476
+ report :report1 do
477
+ depends_on [:report2]
478
+ end
479
+ end
427
480
 
428
- rme = create_visit_report_factory
429
- # 'since' will be implicitly called by ower testing factory method above
430
- # any further call should fails
431
-
432
481
  assert_raise RuntimeError do
433
- rme.since 20.days.ago
482
+ ReportDependencyTestReport.new.validate_dependencies
483
+ end
484
+ end
485
+
486
+ should "return a report by name" do
487
+ class ReportByNameTestReport < Reportme::ReportFactory
488
+ connection :adapter => "mysql", :database => "report_me_test", :username => "root", :password => "root", :host => "localhost", :port => 3306
489
+ report :report1 do
490
+ end
491
+ report :report2 do
492
+ end
434
493
  end
494
+
495
+ assert :report1, ReportByNameTestReport.report_by_name(:report1).name
496
+ assert :report2, ReportByNameTestReport.report_by_name(:report2).name
497
+
498
+ end
499
+
500
+ should "compute a dependency hash" do
501
+ class ReportDependencyHashTestReport < Reportme::ReportFactory
502
+ connection :adapter => "mysql", :database => "report_me_test", :username => "root", :password => "root", :host => "localhost", :port => 3306
503
+ report :report1 do
504
+ depends_on [:report2, :report3]
505
+ end
506
+ report :report2 do
507
+ depends_on [:report3]
508
+ end
509
+ report :report3 do
510
+ end
511
+ end
512
+
513
+ hash = ReportDependencyHashTestReport.new.__dependency_hash
514
+
515
+ assert [
516
+ ReportDependencyHashTestReport.report_by_name(:report2),
517
+ ReportDependencyHashTestReport.report_by_name(:report3)
518
+ ], hash[:report1]
519
+
520
+ assert [
521
+ ReportDependencyHashTestReport.report_by_name(:report3)
522
+ ], hash[:report2]
523
+
524
+ assert [], hash[:report3]
525
+ end
526
+
527
+ should "sort some periods" do
528
+ assert [:day, :today], Reportme::ReportFactory.__sort_periods([{:name => :today}, {:name => :day}])
529
+ assert [:day, :week, :today], Reportme::ReportFactory.__sort_periods([{:name => :today}, {:name => :day}, {:name => :week}])
530
+ assert [:day, :week], Reportme::ReportFactory.__sort_periods([{:name => :week}, {:name => :day}])
531
+ assert [:day, :week, :month, :calendar_month, :today], Reportme::ReportFactory.__sort_periods([{:name => :week}, {:name => :day}, {:name => :today}, {:name => :calendar_month}, {:name => :month}])
532
+
533
+ assert [:day, :week, :week, :today, :today], Reportme::ReportFactory.__sort_periods([{:name => :week}, {:name => :today}, {:name => :today}, {:name => :week}, {:name => :day}])
534
+ end
535
+
536
+ should "run reports in a dependency aware manner" do
537
+ class ReportDependencyAwareTestReport < Reportme::ReportFactory
538
+ connection :adapter => "mysql", :database => "report_me_test", :username => "root", :password => "root", :host => "localhost", :port => 3306
539
+ report :report1 do
540
+ depends_on [:report2, :report3]
541
+ end
542
+ report :report2 do
543
+ depends_on [:report3]
544
+ end
545
+ report :report3 do
546
+ end
547
+ end
548
+
549
+ runned = []
550
+
551
+ reports = []
552
+ reports << ReportDependencyAwareTestReport.report_by_name(:report1)
553
+ reports << ReportDependencyAwareTestReport.report_by_name(:report2)
554
+ reports << ReportDependencyAwareTestReport.report_by_name(:report3)
555
+
556
+ ReportDependencyAwareTestReport.new.run_dependency_aware(reports) do |report|
557
+ runned << report.name
558
+ end
559
+
560
+ assert [:report3, :report2, :report1], runned
561
+
562
+ end
563
+
564
+ should "prefix tablename with tmp_ for temporary reports" do
565
+ class ReportTemporaryTestReport < Reportme::ReportFactory
566
+ connection :adapter => "mysql", :database => "report_me_test", :username => "root", :password => "root", :host => "localhost", :port => 3306
567
+ report :report1, :temporary => true do
568
+ end
569
+ end
570
+
571
+ assert "tmp_report1_day", ReportTemporaryTestReport.report_by_name(:report1).table_name(:day)
435
572
  end
436
573
 
437
574
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jzimmek-reportme
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Zimmek
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-27 00:00:00 -07:00
12
+ date: 2009-06-29 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15