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 +8 -8
- data/lib/resque/reports/base_report.rb +19 -2
- data/lib/resque/reports/common/batched_report.rb +119 -0
- data/lib/resque/reports/extensions/table_building.rb +11 -7
- data/lib/resque/reports/version.rb +1 -1
- data/spec/resque/reports/base_report_spec.rb +4 -8
- data/spec/resque/reports/csv_report_spec.rb +11 -7
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NTJmMWQwMTcwNDczZTgwMzkxNDkzOTlhZjA2NjI3MTkzMGEyYTgxMg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjVhNTg1NjllZmM4Y2Y4YzI5OTJlMTQyNjdhOTZlMjM2NDJlZjcyNg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MDE3OWE2ZGFjODZhZDgxYzRlOGFiOGU3MzMwZTdiNjZlYmEwOGI3ZTc2MjFl
|
10
|
+
MDhhNDYwZGRkODY5MTY1ZDQ1MTdlNGU2Mzc0MWI3ZWIwODhlMTNiN2EzMGVi
|
11
|
+
Nzg1Mzc1Y2U5Y2EwMmM0OTVhYjJjODg1NGFlZDNlYTMwNzUwNDA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NGE1MDM5OTA1M2ExNzgxMzE1MGNkMmI4NTlhZmY3NzQzYzJhMTQwMmIxYTAw
|
14
|
+
N2JlZjVhY2E3YTVlYzkzNDMwNzc4N2FjMGVhM2Q3NGFhY2QyMGJhYjU0MmU4
|
15
|
+
YmJmNjNjMWZmYmQwNTUxNzQzNWY2MWI3YWFlOTExYzI4NTdlOGM=
|
@@ -18,9 +18,13 @@ module Resque
|
|
18
18
|
# end
|
19
19
|
# end
|
20
20
|
#
|
21
|
-
# BaseReport provides
|
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
|
-
|
48
|
-
|
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
|
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
|
@@ -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', :
|
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(:
|
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
|
-
|
166
|
-
|
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', :
|
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
|
-
[
|
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,
|
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.
|
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-
|
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
|