resque-reports 0.0.2 → 0.3.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.
@@ -1,2 +1,3 @@
1
1
  # coding: utf-8
2
2
  require 'resque/reports'
3
+ require_relative '../app/jobs/resque/reports/report_job'
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
8
8
  gem.version = Resque::Reports::VERSION
9
9
  gem.authors = ['Sergey D.']
10
10
  gem.email = ['sclinede@gmail.com']
11
- gem.description = 'Make your custom reports to CSV with resque by simple DSL'
12
- gem.summary = 'resque-reports 0.0.1'
11
+ gem.description = 'Make your custom reports to CSV in background using Resque with simple DSL'
12
+ gem.summary = 'resque-reports'
13
13
  gem.homepage = 'https://github.com/sclinede/resque-reports'
14
14
  gem.license = "MIT"
15
15
 
@@ -18,10 +18,12 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ['lib']
20
20
 
21
- gem.add_dependency 'resque-integration', '~> 0.2.9'
22
- gem.add_dependency 'facets', '>= 2.9.3'
21
+ gem.add_dependency 'resque-integration', '>= 0.2.9'
22
+ gem.add_dependency 'activesupport' # избавиться от зависимости
23
23
 
24
24
  gem.add_development_dependency "bundler", "~> 1.3"
25
25
  gem.add_development_dependency "rake"
26
- gem.add_development_dependency 'rspec'
26
+ gem.add_development_dependency 'rspec', '>= 2.14.0'
27
+ gem.add_development_dependency 'rspec-given', '~> 3.0'
28
+ gem.add_development_dependency 'simplecov'
27
29
  end
@@ -0,0 +1,196 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'stringio'
4
+
5
+ require 'resque-reports'
6
+
7
+ class MyTypeReport < Resque::Reports::BaseReport
8
+ extension :type
9
+
10
+ def write(io, force = false)
11
+ write_line io, build_table_header
12
+
13
+ data_each(true) do |element|
14
+ write_line io, build_table_row(element)
15
+ end
16
+ end
17
+
18
+ def write_line(io, row)
19
+ io << "#{row.nil? ? 'nil' : row.join('|')}\r\n"
20
+ end
21
+ end
22
+
23
+ class MyReport < MyTypeReport
24
+ queue :my_type_reports
25
+ source :select_data
26
+ encoding UTF8
27
+
28
+ directory File.join(Dir.tmpdir, 'resque-reports')
29
+
30
+ table do |element|
31
+ column 'First one', :decorate_first
32
+ column 'Second', decorate_second(element[:two])
33
+ end
34
+
35
+ create do |param|
36
+ @main_param = param
37
+ end
38
+
39
+ def decorate_first(element)
40
+ "decorated: #{element[:one]}"
41
+ end
42
+
43
+ def decorate_second(text)
44
+ "#{text} - is second"
45
+ end
46
+
47
+ def select_data
48
+ [{one: 'one', two: 'one'}, {one: @main_param, two: @main_param}]
49
+ end
50
+ end
51
+
52
+ describe 'Resque::Reports::BaseReport successor' do
53
+ let(:io) { StringIO.new }
54
+ let(:my_report) { MyReport.new('test') }
55
+ let(:dummy) { Resque::Reports::Extensions::Dummy.new }
56
+
57
+ describe '#extension' do
58
+ before { File.stub(:exists? => true) }
59
+
60
+ it { File.extname(my_report.filename).should eq '.type' }
61
+ end
62
+
63
+ describe '#source' do
64
+ before { my_report.stub(select_data: [dummy]) }
65
+
66
+ it { my_report.should_receive(:select_data) }
67
+
68
+ after { my_report.build true }
69
+ end
70
+
71
+ describe '#directory' do
72
+ subject { my_report.instance_variable_get(:@cache_file) }
73
+ let(:tmpdir) { File.join(Dir.tmpdir, 'resque-reports') }
74
+
75
+ it { subject.instance_variable_get(:@dir).should eq tmpdir }
76
+ end
77
+
78
+ describe '#create' do
79
+ it { my_report.instance_variable_get(:@main_param).should eq 'test' }
80
+ end
81
+
82
+ describe '#encoding' do
83
+ subject { my_report.instance_variable_get(:@cache_file) }
84
+ let(:utf_coding) { Resque::Reports::Extensions::Encodings::UTF8 }
85
+
86
+ it { subject.instance_variable_get(:@coding).should eq utf_coding }
87
+ end
88
+
89
+ describe '#write' do
90
+ subject { MyReport.new('#write test') }
91
+
92
+ before do
93
+ subject.stub(:data_each) { |&block| block.call(dummy) }
94
+ subject.stub(build_table_row: ['row'])
95
+ subject.stub(build_table_header: ['header'])
96
+
97
+ subject.write(io)
98
+ end
99
+
100
+ it { io.string.should eq "header\r\nrow\r\n" }
101
+ end
102
+
103
+ describe '#bg_build' do
104
+ let(:job_class) { Resque::Reports::ReportJob }
105
+
106
+ context 'when report is building twice' do
107
+ subject { MyReport.new('#bg_build test') }
108
+
109
+ before { job_class.stub(enqueue: 'job_id') }
110
+
111
+ it do
112
+ job_class
113
+ .should_receive(:enqueue)
114
+ .with('MyReport', '["#bg_build test",true]')
115
+ .twice
116
+ end
117
+
118
+ after do
119
+ 2.times { subject.bg_build true }
120
+ end
121
+ end
122
+
123
+ context 'when report is building' do
124
+ subject { MyReport.new('#bg_build test') }
125
+
126
+ before { job_class.stub(enqueue: 'job_id') }
127
+
128
+ it do
129
+ job_class
130
+ .should_receive(:enqueue)
131
+ .with('MyReport', '["#bg_build test",true]')
132
+ end
133
+
134
+ after { subject.bg_build true }
135
+ end
136
+
137
+ context 'when report is not build yet' do
138
+ subject { MyReport.new('#bg_build test') }
139
+
140
+ before do
141
+ job_class.stub(enqueued?: double('Meta', meta_id: 'enqueued_job_id'))
142
+ end
143
+
144
+ it { subject.bg_build(true).should eq 'enqueued_job_id' }
145
+ end
146
+ end
147
+
148
+ describe '#build' do
149
+ subject { MyReport.new('#build test') }
150
+
151
+ it { subject.should_receive(:decorate_first).twice }
152
+
153
+ after { subject.build true }
154
+
155
+ context 'when report was built' do
156
+ subject { MyReport.new('was built test') }
157
+
158
+ before { subject.build true }
159
+
160
+ its(:exists?) { should be_true }
161
+ it do
162
+ File.read(subject.filename)
163
+ .should eq <<-REPORT.gsub(/^ {12}/, '')
164
+ First one|Second\r
165
+ decorated: one|one - is second\r
166
+ decorated: was built test|was built test - is second\r
167
+ REPORT
168
+ end
169
+ end
170
+ end
171
+
172
+ describe '#data_each' do
173
+ subject { MyReport.new('#data_each test') }
174
+
175
+ it { subject.should_receive(:data_each) }
176
+
177
+ after { subject.write(io) }
178
+ end
179
+
180
+ describe '#build_table_header' do
181
+ subject { MyReport.new('#build_table_header test') }
182
+
183
+ it { subject.should_receive(:build_table_header) }
184
+
185
+ after { subject.write(io) }
186
+ end
187
+
188
+ describe '#build_table_row' do
189
+ subject { MyReport.new('#build_table_row test') }
190
+
191
+ it { subject.should_receive(:build_table_row).twice }
192
+
193
+ after { subject.write(io) }
194
+ end
195
+
196
+ end
@@ -0,0 +1,89 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'stringio'
4
+
5
+ require 'resque/reports/csv_report'
6
+
7
+ class MyCsvReport < Resque::Reports::CsvReport
8
+ queue :csv_reports
9
+ source :select_data
10
+ encoding UTF8
11
+
12
+ csv_options col_sep: ',', row_sep: "\n"
13
+
14
+ directory File.join(Dir.home, '.resque-reports')
15
+
16
+ table do |element|
17
+ column 'First one', :decorate_first
18
+ column 'Second', "#{element} - is second"
19
+ end
20
+
21
+ create do |param|
22
+ @main_param = param
23
+ end
24
+
25
+ def decorate_first(element)
26
+ "decorated: #{element}"
27
+ end
28
+
29
+ def select_data
30
+ [:one, @main_param]
31
+ end
32
+ end
33
+
34
+ class MyCsvDefaultsReport < Resque::Reports::CsvReport
35
+ source :select_data
36
+ encoding UTF8
37
+
38
+ directory File.join(Dir.tmpdir, 'resque-reports')
39
+
40
+ table do |element|
41
+ column 'Uno', "#{element} - is value"
42
+ end
43
+
44
+ def select_data
45
+ [:one, @main_param]
46
+ end
47
+ end
48
+
49
+ describe 'Resque::Reports::CsvReport successor' do
50
+ describe '.csv_options' do
51
+ context 'when custom options not set' do
52
+ subject { MyCsvDefaultsReport.new }
53
+
54
+ it 'sets csv_options defaults' do
55
+ subject.options.should eq MyCsvReport::DEFAULT_CSV_OPTIONS
56
+ end
57
+ end
58
+
59
+ context 'when custom options are set' do
60
+ subject { MyCsvReport.new('csv_options test') }
61
+
62
+ let(:my_options) do
63
+ MyCsvReport::DEFAULT_CSV_OPTIONS.merge(col_sep: ',', row_sep: "\n")
64
+ end
65
+
66
+ it 'merges csv_options with defaults' do
67
+ subject.options.should eq my_options
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#build' do
73
+ context 'when report was built' do
74
+ subject { MyCsvReport.new('was built test') }
75
+
76
+ before { subject.build true }
77
+
78
+ its(:exists?) { should be_true }
79
+ it do
80
+ File.read(subject.filename)
81
+ .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
85
+ CSV
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,140 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'resque-reports'
4
+
5
+ module Reports
6
+ class MyCsvReport < Resque::Reports::CsvReport
7
+ queue :csv_reports
8
+ source :select_data
9
+ encoding UTF8
10
+
11
+ directory File.join(Dir.home, '.resque-reports')
12
+
13
+ table do |element|
14
+ column 'First', "#{element} - is first"
15
+ end
16
+
17
+ create do |param1, param2|
18
+ @main_param = param1
19
+ @secondary_param = param2
20
+ end
21
+
22
+ def select_data
23
+ [:one, @main_param]
24
+ end
25
+ end
26
+ end
27
+
28
+ describe Resque::Reports::ReportJob do
29
+ let(:my_report) { Reports::MyCsvReport.new('.execute test', 'test') }
30
+ let(:exec_params) do
31
+ ['Reports::MyCsvReport', '[".execute test", "test",true]']
32
+ end
33
+
34
+ describe '.execute' do
35
+ before { Reports::MyCsvReport.stub(new: my_report) }
36
+
37
+ context 'when building report' do
38
+ before { my_report.stub(build: nil) }
39
+
40
+ it do
41
+ expect(Reports::MyCsvReport)
42
+ .to receive(:new).with('.execute test', 'test')
43
+ end
44
+ it { expect(my_report).to receive(:build).with(true) }
45
+
46
+ after { described_class.execute(*exec_params) }
47
+
48
+ end
49
+ context 'when wrong class given' do
50
+ it 'sends invalid class name' do
51
+ expect { described_class.execute('MyWrongReport', '[true]') }
52
+ .to raise_error(NameError)
53
+ end
54
+
55
+ it 'sends class that is not BaseReport successor' do
56
+ expect { described_class.execute('Object', '[true]') }
57
+ .to raise_error(RuntimeError)
58
+ end
59
+ end
60
+
61
+ context 'when events are firing' do
62
+ before do
63
+ described_class.stub(get_meta: {})
64
+ described_class.get_meta.stub(save: true)
65
+ end
66
+
67
+ context 'when progress total is zero' do
68
+ before do
69
+ my_report.stub(select_data: [])
70
+ my_report.stub(data_size: 0)
71
+ end
72
+
73
+ it { described_class.should_not_receive(:at) }
74
+
75
+ after { described_class.execute(*exec_params) }
76
+ end
77
+
78
+ context 'when works default handlers' do
79
+ context 'when error occurs' do
80
+ before { my_report.stub(:build_table_row) { fail 'Custom error' } }
81
+
82
+ it do
83
+ expect { described_class.execute(*exec_params) }
84
+ .to raise_error('Custom error')
85
+ end
86
+ end
87
+
88
+ context 'when progress is changed' do
89
+ it { described_class.should_receive(:at).with(2, 2, nil) }
90
+
91
+ after { described_class.execute(*exec_params) }
92
+ end
93
+ end
94
+
95
+ context 'when works custom handlers' do
96
+ context 'when error occurs' do
97
+ before do
98
+ my_report.stub(:error_message) { |e| fail "Boom! #{e.message}" }
99
+ my_report.stub(:build_table_row) { fail 'Custom error' }
100
+ end
101
+
102
+ it do
103
+ expect { described_class.execute(*exec_params) }
104
+ .to raise_error('Boom! Custom error')
105
+ end
106
+ end
107
+
108
+ context 'when progress is changed' do
109
+ before do
110
+ my_report.stub(:progress_message) do |progress, total|
111
+ "my progress: #{progress} / #{total}"
112
+ end
113
+ end
114
+
115
+ it do
116
+ described_class
117
+ .should_receive(:at)
118
+ .with(2, 2, 'my progress: 2 / 2')
119
+ end
120
+
121
+ after { described_class.execute(*exec_params) }
122
+ end
123
+ end
124
+
125
+ context 'when task is performed by resque' do
126
+ context 'when error occurs' do
127
+ before do
128
+ my_report.stub(:error_message) { |e| fail "Boom! #{e.message}" }
129
+ my_report.stub(:build_table_row) { fail 'Custom error' }
130
+ end
131
+
132
+ it do
133
+ expect { described_class.execute(*exec_params) }
134
+ .to raise_error('Boom! Custom error')
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end