resque-reports 0.3.2 → 0.3.3

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 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