resque-reports 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTQ0NGJiYjk0OGMyYWVhYmU4OGVmNzhiNWNhZmI0ZWFlYzI4OTMyOQ==
4
+ NTJmMWQwMTcwNDczZTgwMzkxNDkzOTlhZjA2NjI3MTkzMGEyYTgxMg==
5
5
  data.tar.gz: !binary |-
6
- MTJmMDEzZmQ0OGIxZTdkMmE4NjdlYmI0ZDIwNzc1YjNhOWNiYWZmOQ==
6
+ YjVhNTg1NjllZmM4Y2Y4YzI5OTJlMTQyNjdhOTZlMjM2NDJlZjcyNg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZTk3NWJhMzcyZjBjNTA4MDE3OWQ5NzkzZGVjZTRmYTY2MjdjM2Q1OTE2MTBm
10
- NGZiMTRkOGQ2OWE2NTY0YTk0ZDVjY2UwNzMxZDk3ODVjNTQ3NzA1NzgxNTgw
11
- ZTBlMjRkYWIwYTQ4ODBmNmQ0OWFiZDI3ZTJmODc0ZTIxODVjNzM=
9
+ MDE3OWE2ZGFjODZhZDgxYzRlOGFiOGU3MzMwZTdiNjZlYmEwOGI3ZTc2MjFl
10
+ MDhhNDYwZGRkODY5MTY1ZDQ1MTdlNGU2Mzc0MWI3ZWIwODhlMTNiN2EzMGVi
11
+ Nzg1Mzc1Y2U5Y2EwMmM0OTVhYjJjODg1NGFlZDNlYTMwNzUwNDA=
12
12
  data.tar.gz: !binary |-
13
- ZmJkYTIzYjZiYjZiYTgwYThhODMzZTc1NmRkMTYxYWRkMzU1ZWY2NjU5OWUx
14
- MTJkYTI3NWQ1NjRjODBiODdiYTVlN2VjZjA5MDU0OGUzODU1YzE5NWZhMWYy
15
- YzE2YTNlMTk3MTcwMjNkZDAxMDMxOWZjYzg4ZWJiNzI2MTViM2Y=
13
+ NGE1MDM5OTA1M2ExNzgxMzE1MGNkMmI4NTlhZmY3NzQzYzJhMTQwMmIxYTAw
14
+ N2JlZjVhY2E3YTVlYzkzNDMwNzc4N2FjMGVhM2Q3NGFhY2QyMGJhYjU0MmU4
15
+ YmJmNjNjMWZmYmQwNTUxNzQzNWY2MWI3YWFlOTExYzI4NTdlOGM=
@@ -18,9 +18,13 @@ module Resque
18
18
  # end
19
19
  # end
20
20
  #
21
- # BaseReport provides followed DSL, example:
21
+ # BaseReport provides following DSL, example:
22
22
  #
23
23
  # class CustomReport < CustomTypeReport
24
+ # # include Resque::Reports::Common::BatchedReport
25
+ # # overrides data retrieving to achieve batching
26
+ # # if included 'source :select_data' becomes needless
27
+ #
24
28
  # queue :custom_reports # Resque queue name
25
29
  # source :select_data # method called to retrieve report data
26
30
  # encoding UTF8 # file encoding
@@ -33,14 +37,21 @@ module Resque
33
37
  # column 'Column 1 Header', :decorate_one
34
38
  # column 'Column 2 Header', decorate_two(element[1])
35
39
  # column 'Column 3 Header', 'Column 3 Cell'
40
+ # column 'Column 4 Header', :formatted_four, formatter: :just_cute
36
41
  # end
37
42
  #
38
- # # Class initialize
43
+ # # Class initialize if needed
39
44
  # # NOTE: must be used instead of define 'initialize' method
45
+ # # Default behaviour is to receive in *args Hash with report attributes
46
+ # # like: CustomReport.new(main_param: 'value') => calls send(:main_param=, 'value')
40
47
  # create do |param|
41
48
  # @main_param = param
42
49
  # end
43
50
  #
51
+ # def self.just_cute_formatter(column_value)
52
+ # "I'm so cute #{column_value}"
53
+ # end
54
+ #
44
55
  # # decorate method, called by symbol-name
45
56
  # def decorate_one(element)
46
57
  # "decorate_one: #{element[0]}"
@@ -143,6 +154,12 @@ module Resque
143
154
  if create_block
144
155
  define_singleton_method(:create_dispatch, create_block)
145
156
  create_dispatch(*args)
157
+ else
158
+ if args && (attrs_hash = args.first) && attrs_hash.is_a?(Hash)
159
+ attrs_hash.each do |name, value|
160
+ send("#{name}=", value)
161
+ end
162
+ end
146
163
  end
147
164
 
148
165
  @args = args
@@ -0,0 +1,119 @@
1
+ # coding: utf-8
2
+ module Resque::Reports::Common::BatchedReport
3
+ extend ActiveSupport::Concern
4
+
5
+ BATCH_SIZE = 10_000
6
+
7
+ module InstanceMethods
8
+ # Internal: Выполняет запрос отчета пачками и выполняет block для каждой пачки
9
+ # Переопредленный метод из Resque::Reports
10
+ #
11
+ # Returns Nothing
12
+ def data_each(force = false)
13
+ 0.step(data_size, batch_size) do |batch_offset|
14
+ ActiveRecord::Base.connection.execute(batched_query(batch_offset)).each do |element|
15
+ yield element
16
+ end
17
+ end
18
+ end
19
+
20
+ # Internal: Возвращает общее кол-во строк в отчете
21
+ # Переопредленный метод из Resque::Reports
22
+ #
23
+ # Returns Fixnum
24
+ def data_size
25
+ @data_size ||= ActiveRecord::Base.connection.execute(count_query)[0]['count'].to_i
26
+ end
27
+
28
+ protected
29
+
30
+ # Internal: Возвращает отфильтрованный запрос отчета
31
+ #
32
+ # Returns Arel::SelectManager
33
+ def query
34
+ filter base_query
35
+ end
36
+
37
+ # Internal: Полезный метод для хранения Arel::Table объектов для запроса отчета
38
+ #
39
+ # Returns Hash, {:table_name => #<Arel::Table @name="table_name">, ...}
40
+ def tables
41
+ return @tables if defined? @tables
42
+
43
+ tables = models.map(&:arel_table)
44
+
45
+ @tables = tables.inject({}) { |a, e| a.store(e.name, e) && a }.with_indifferent_access
46
+ end
47
+
48
+ # Internal: Полезный метод для join'а необходимых таблиц через Arel
49
+ #
50
+ # Returns Arel
51
+ def join_tables(source_table, joins)
52
+ joins.inject(source_table) do |query, joined|
53
+ query.join(joined[:table]).on(joined[:on])
54
+ end
55
+ end
56
+
57
+ # Internal: Размер пачки отчета
58
+ #
59
+ # Returns Fixnum
60
+ def batch_size
61
+ BATCH_SIZE
62
+ end
63
+
64
+ # Internal: Модели используемые в отчете
65
+ #
66
+ # Returns Array of Arel::Table
67
+ def models
68
+ fail NotImplementedError
69
+ end
70
+
71
+ # Internal: Основной запрос отчета (Arel)
72
+ #
73
+ # Returns Arel::SelectManager
74
+ def base_query
75
+ fail NotImplementedError
76
+ end
77
+
78
+ # Internal: Поля запрашиваемые отчетом
79
+ #
80
+ # Returns String (SQL)
81
+ def select
82
+ fail NotImplementedError
83
+ end
84
+
85
+ # Internal: Порядок строк отчета
86
+ #
87
+ # Returns String (SQL)
88
+ def order
89
+ nil
90
+ end
91
+
92
+ # Internal: Фильтры отчета
93
+ #
94
+ # Returns Arel::SelectManager
95
+ def filter(query)
96
+ query
97
+ end
98
+
99
+ # Internal: Запрос количества строк в отчете
100
+ #
101
+ # Returns String (SQL)
102
+ def count_query
103
+ query.project(Arel.sql('COUNT(*) as count')).to_sql
104
+ end
105
+
106
+ # Internal: Запрос пачки строк отчета
107
+ #
108
+ # offset - Numeric, число строк на которое сдвигается запрос
109
+ #
110
+ # Returns String (SQL)
111
+ def batched_query(offset)
112
+ query.project(Arel.sql(select))
113
+ .take(batch_size)
114
+ .skip(offset)
115
+ .order(order)
116
+ .to_sql
117
+ end
118
+ end
119
+ end
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
1
2
  # coding: utf-8
2
3
  # Resque namespace
3
4
  module Resque
@@ -19,7 +20,6 @@ module Resque
19
20
  # - data_each
20
21
  # - data_size
21
22
  module ClassMethods
22
-
23
23
  attr_accessor :source_method
24
24
  alias_method :source, :source_method=
25
25
 
@@ -27,8 +27,8 @@ module Resque
27
27
  @table_block = block
28
28
  end
29
29
 
30
- def column(name, value)
31
- add_column_header(name) || add_column_cell(value)
30
+ def column(name, value, options = {})
31
+ add_column_header(name) || add_column_cell(value, options)
32
32
  end
33
33
 
34
34
  def init_table
@@ -40,12 +40,15 @@ module Resque
40
40
  @table_header << encoded_string(column_name) if @header_collecting
41
41
  end
42
42
 
43
- def add_column_cell(column_value)
43
+ def add_column_cell(column_value, options = {})
44
44
  return if @header_collecting
45
45
 
46
46
  if column_value.is_a? Symbol
47
- # Smells bad... changes input variable
48
- column_value = @instance.send(column_value, @row_object)
47
+ column_value = @row_object[column_value]
48
+ end
49
+
50
+ if (formatter_name = options[:formatter])
51
+ column_value = send("#{formatter_name}_formatter".to_sym, column_value)
49
52
  end
50
53
 
51
54
  @table_row << encoded_string(column_value)
@@ -54,7 +57,8 @@ module Resque
54
57
  def build_table_row(row_object)
55
58
  @header_collecting = false
56
59
 
57
- @row_object = row_object # for instance decorate methods calls
60
+ @row_object = row_object.is_a?(Hash) ? row_object.with_indifferent_access : row_object
61
+
58
62
  row = @table_block.call(@row_object)
59
63
 
60
64
  finish_row
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  module Resque
3
3
  module Reports
4
- VERSION = '0.3.2'
4
+ VERSION = '0.3.3'
5
5
  end
6
6
  end
7
7
 
@@ -28,7 +28,7 @@ class MyReport < MyTypeReport
28
28
  directory File.join(Dir.tmpdir, 'resque-reports')
29
29
 
30
30
  table do |element|
31
- column 'First one', :decorate_first
31
+ column 'First one', :one
32
32
  column 'Second', decorate_second(element[:two])
33
33
  end
34
34
 
@@ -36,10 +36,6 @@ class MyReport < MyTypeReport
36
36
  @main_param = param
37
37
  end
38
38
 
39
- def decorate_first(element)
40
- "decorated: #{element[:one]}"
41
- end
42
-
43
39
  def decorate_second(text)
44
40
  "#{text} - is second"
45
41
  end
@@ -148,7 +144,7 @@ describe 'Resque::Reports::BaseReport successor' do
148
144
  describe '#build' do
149
145
  subject { MyReport.new('#build test') }
150
146
 
151
- it { subject.should_receive(:decorate_first).twice }
147
+ it { subject.should_receive(:decorate_second).exactly(3).times }
152
148
 
153
149
  after { subject.build true }
154
150
 
@@ -162,8 +158,8 @@ describe 'Resque::Reports::BaseReport successor' do
162
158
  File.read(subject.filename)
163
159
  .should eq <<-REPORT.gsub(/^ {12}/, '')
164
160
  First one|Second\r
165
- decorated: one|one - is second\r
166
- decorated: was built test|was built test - is second\r
161
+ one|one - is second\r
162
+ was built test|was built test - is second\r
167
163
  REPORT
168
164
  end
169
165
  end
@@ -14,20 +14,25 @@ class MyCsvReport < Resque::Reports::CsvReport
14
14
  directory File.join(Dir.home, '.resque-reports')
15
15
 
16
16
  table do |element|
17
- column 'First one', :decorate_first
18
- column 'Second', "#{element} - is second"
17
+ column 'First one', decorate_first(element[:first])
18
+ column 'Second', "#{element[:second]} - is second"
19
+ column 'Third', :third, formatter: :cute_third
19
20
  end
20
21
 
21
22
  create do |param|
22
23
  @main_param = param
23
24
  end
24
25
 
26
+ def self.cute_third_formatter(column_value)
27
+ "3'rd row element is: #{column_value}"
28
+ end
29
+
25
30
  def decorate_first(element)
26
31
  "decorated: #{element}"
27
32
  end
28
33
 
29
34
  def select_data
30
- [:one, @main_param]
35
+ [{:first => :one, :second => @main_param, :third => 3}]
31
36
  end
32
37
  end
33
38
 
@@ -42,7 +47,7 @@ class MyCsvDefaultsReport < Resque::Reports::CsvReport
42
47
  end
43
48
 
44
49
  def select_data
45
- [:one, @main_param]
50
+ []
46
51
  end
47
52
  end
48
53
 
@@ -79,9 +84,8 @@ describe 'Resque::Reports::CsvReport successor' do
79
84
  it do
80
85
  File.read(subject.filename)
81
86
  .should eq <<-CSV.gsub(/^ {12}/, "")
82
- First one,Second
83
- decorated: one,one - is second
84
- decorated: was built test,was built test - is second
87
+ First one,Second,Third
88
+ decorated: one,was built test - is second,3'rd row element is: 3
85
89
  CSV
86
90
  end
87
91
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-reports
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey D.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-07 00:00:00.000000000 Z
11
+ date: 2014-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: resque-integration
@@ -128,6 +128,7 @@ files:
128
128
  - lib/resque/reports.rb
129
129
  - lib/resque/reports/base_report.rb
130
130
  - lib/resque/reports/cache_file.rb
131
+ - lib/resque/reports/common/batched_report.rb
131
132
  - lib/resque/reports/csv_report.rb
132
133
  - lib/resque/reports/extensions.rb
133
134
  - lib/resque/reports/extensions/const.rb