rails-excel-reporter 0.1.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.
@@ -0,0 +1,190 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RailsExcelReporter::Base do
4
+ let(:sample_data) do
5
+ [
6
+ OpenStruct.new(id: 1, name: 'John Doe', email: 'john@example.com', created_at: Time.parse('2024-01-01')),
7
+ OpenStruct.new(id: 2, name: 'Jane Smith', email: 'jane@example.com', created_at: Time.parse('2024-01-02')),
8
+ OpenStruct.new(id: 3, name: 'Bob Johnson', email: 'bob@example.com', created_at: Time.parse('2024-01-03'))
9
+ ]
10
+ end
11
+
12
+ let(:report_class) do
13
+ Class.new(RailsExcelReporter::Base) do
14
+ attributes :id, :name, :email
15
+
16
+ def name
17
+ "#{object.name} (Custom)"
18
+ end
19
+ end
20
+ end
21
+
22
+ let(:report) { report_class.new(sample_data) }
23
+
24
+ describe 'class methods' do
25
+ describe '.attributes' do
26
+ it 'defines attributes for the report' do
27
+ expect(report_class.attributes).to contain_exactly(
28
+ { name: :id, header: 'Id' },
29
+ { name: :name, header: 'Name' },
30
+ { name: :email, header: 'Email' }
31
+ )
32
+ end
33
+ end
34
+
35
+ describe '.attribute' do
36
+ it 'adds a single attribute with custom header' do
37
+ klass = Class.new(RailsExcelReporter::Base)
38
+ klass.attribute :custom_field, header: 'Custom Header'
39
+
40
+ expect(klass.attributes).to contain_exactly(
41
+ { name: :custom_field, header: 'Custom Header' }
42
+ )
43
+ end
44
+ end
45
+ end
46
+
47
+ describe 'instance methods' do
48
+ describe '#initialize' do
49
+ it 'initializes with collection' do
50
+ expect(report.collection).to eq(sample_data)
51
+ end
52
+
53
+ it 'accepts custom worksheet name' do
54
+ custom_report = report_class.new(sample_data, worksheet_name: 'Custom Sheet')
55
+ expect(custom_report.worksheet_name).to eq('Custom Sheet')
56
+ end
57
+
58
+ it 'accepts progress callback' do
59
+ callback = proc { |progress| }
60
+ custom_report = report_class.new(sample_data, &callback)
61
+ expect(custom_report.progress_callback).to eq(callback)
62
+ end
63
+ end
64
+
65
+ describe '#filename' do
66
+ it 'generates a filename with timestamp' do
67
+ allow(Time).to receive(:now).and_return(Time.parse('2024-01-15'))
68
+ expect(report.filename).to match(/report_report_2024_01_15\.xlsx/)
69
+ end
70
+ end
71
+
72
+ describe '#to_h' do
73
+ it 'returns hash representation' do
74
+ hash = report.to_h
75
+ expect(hash).to include(
76
+ :worksheet_name,
77
+ :attributes,
78
+ :data,
79
+ :collection_size,
80
+ :streaming
81
+ )
82
+ end
83
+ end
84
+
85
+ describe '#collection_size' do
86
+ it 'returns the size of the collection' do
87
+ expect(report.collection_size).to eq(3)
88
+ end
89
+ end
90
+
91
+ describe '#should_stream?' do
92
+ it 'returns false for small collections' do
93
+ expect(report.should_stream?).to be false
94
+ end
95
+
96
+ it 'returns true for large collections' do
97
+ large_data = (1..2000).map { |i| OpenStruct.new(id: i, name: "User #{i}") }
98
+ large_report = report_class.new(large_data)
99
+ expect(large_report.should_stream?).to be true
100
+ end
101
+ end
102
+
103
+ describe 'Excel generation' do
104
+ it 'generates Excel file' do
105
+ file = report.file
106
+ expect(file).to be_a(Tempfile)
107
+ expect(file.size).to be > 0
108
+ end
109
+
110
+ it 'generates Excel binary data' do
111
+ xlsx_data = report.to_xlsx
112
+ expect(xlsx_data).to be_a(String)
113
+ expect(xlsx_data.size).to be > 0
114
+ expect(xlsx_data[0, 4]).to eq("PK\x03\x04")
115
+ end
116
+
117
+ it 'saves to file' do
118
+ temp_path = '/tmp/test_report.xlsx'
119
+ report.save_to(temp_path)
120
+ expect(File.exist?(temp_path)).to be true
121
+ expect(File.size(temp_path)).to be > 0
122
+ File.delete(temp_path)
123
+ end
124
+ end
125
+
126
+ describe 'custom methods' do
127
+ it 'calls custom methods for attribute values' do
128
+ # Since we can't easily inspect binary xlsx data, test the underlying data
129
+ report_data = report.to_h[:data]
130
+ expect(report_data[0][1]).to eq('John Doe (Custom)')
131
+ expect(report_data[1][1]).to eq('Jane Smith (Custom)')
132
+ end
133
+ end
134
+ end
135
+
136
+ describe 'error handling' do
137
+ it 'raises error when no attributes are defined' do
138
+ empty_class = Class.new(RailsExcelReporter::Base)
139
+ empty_report = empty_class.new([])
140
+
141
+ expect { empty_report.to_xlsx }.to raise_error(RuntimeError, /No attributes defined/)
142
+ end
143
+ end
144
+
145
+ describe 'callbacks' do
146
+ let(:callback_class) do
147
+ Class.new(RailsExcelReporter::Base) do
148
+ attributes :id, :name
149
+
150
+ attr_reader :before_render_called, :after_render_called,
151
+ :before_row_calls, :after_row_calls
152
+
153
+ def initialize(*args)
154
+ super
155
+ @before_render_called = false
156
+ @after_render_called = false
157
+ @before_row_calls = []
158
+ @after_row_calls = []
159
+ end
160
+
161
+ def before_render
162
+ @before_render_called = true
163
+ end
164
+
165
+ def after_render
166
+ @after_render_called = true
167
+ end
168
+
169
+ def before_row(object)
170
+ @before_row_calls << object
171
+ end
172
+
173
+ def after_row(object)
174
+ @after_row_calls << object
175
+ end
176
+ end
177
+ end
178
+
179
+ let(:callback_report) { callback_class.new(sample_data) }
180
+
181
+ it 'calls all callbacks in correct order' do
182
+ callback_report.to_xlsx
183
+
184
+ expect(callback_report.before_render_called).to be true
185
+ expect(callback_report.after_render_called).to be true
186
+ expect(callback_report.before_row_calls).to eq(sample_data)
187
+ expect(callback_report.after_row_calls).to eq(sample_data)
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RailsExcelReporter::Configuration do
4
+ let(:config) { RailsExcelReporter::Configuration.new }
5
+
6
+ describe 'default values' do
7
+ it 'has default date format' do
8
+ expect(config.date_format).to eq('%Y-%m-%d')
9
+ end
10
+
11
+ it 'has default streaming threshold' do
12
+ expect(config.streaming_threshold).to eq(1000)
13
+ end
14
+
15
+ it 'has default styles' do
16
+ expect(config.default_styles).to be_a(Hash)
17
+ expect(config.default_styles[:header]).to include(:bg_color, :fg_color, :bold)
18
+ expect(config.default_styles[:cell]).to include(:border)
19
+ end
20
+
21
+ it 'has default temp directory' do
22
+ expect(config.temp_directory).to eq(Dir.tmpdir)
23
+ end
24
+ end
25
+
26
+ describe 'customization' do
27
+ it 'allows setting custom date format' do
28
+ config.date_format = '%d/%m/%Y'
29
+ expect(config.date_format).to eq('%d/%m/%Y')
30
+ end
31
+
32
+ it 'allows setting custom streaming threshold' do
33
+ config.streaming_threshold = 500
34
+ expect(config.streaming_threshold).to eq(500)
35
+ end
36
+
37
+ it 'allows setting custom temp directory' do
38
+ config.temp_directory = '/custom/temp'
39
+ expect(config.temp_directory).to eq('/custom/temp')
40
+ end
41
+
42
+ it 'allows setting custom default styles' do
43
+ custom_styles = {
44
+ header: { bg_color: 'FF0000' },
45
+ cell: { border: { style: :medium } }
46
+ }
47
+ config.default_styles = custom_styles
48
+ expect(config.default_styles).to eq(custom_styles)
49
+ end
50
+ end
51
+ end
52
+
53
+ RSpec.describe RailsExcelReporter do
54
+ describe 'configuration' do
55
+ it 'returns the same configuration instance' do
56
+ config1 = RailsExcelReporter.configuration
57
+ config2 = RailsExcelReporter.configuration
58
+ expect(config1).to be(config2)
59
+ end
60
+
61
+ it 'allows configuration via block' do
62
+ RailsExcelReporter.configure do |config|
63
+ config.date_format = '%d-%m-%Y'
64
+ config.streaming_threshold = 2000
65
+ end
66
+
67
+ expect(RailsExcelReporter.config.date_format).to eq('%d-%m-%Y')
68
+ expect(RailsExcelReporter.config.streaming_threshold).to eq(2000)
69
+ end
70
+
71
+ it 'provides config alias' do
72
+ expect(RailsExcelReporter.config).to be(RailsExcelReporter.configuration)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RailsExcelReporter::ControllerHelpers do
4
+ let(:controller_class) do
5
+ Class.new do
6
+ include RailsExcelReporter::ControllerHelpers
7
+
8
+ attr_accessor :response
9
+
10
+ def initialize
11
+ @response = MockResponse.new
12
+ end
13
+
14
+ def send_data(data, options = {})
15
+ @sent_data = data
16
+ @send_options = options
17
+ end
18
+
19
+ def response_body=(body)
20
+ @response.body = body
21
+ end
22
+
23
+ attr_reader :sent_data, :send_options
24
+ end
25
+ end
26
+
27
+ let(:mock_response) do
28
+ Class.new do
29
+ attr_accessor :headers, :body
30
+
31
+ def initialize
32
+ @headers = {}
33
+ end
34
+ end
35
+ end
36
+
37
+ let(:controller) { controller_class.new }
38
+
39
+ let(:report_class) do
40
+ Class.new(RailsExcelReporter::Base) do
41
+ attributes :id, :name
42
+ end
43
+ end
44
+
45
+ let(:sample_data) do
46
+ [
47
+ OpenStruct.new(id: 1, name: 'John'),
48
+ OpenStruct.new(id: 2, name: 'Jane')
49
+ ]
50
+ end
51
+
52
+ let(:report) { report_class.new(sample_data) }
53
+
54
+ before do
55
+ stub_const('MockResponse', mock_response)
56
+ end
57
+
58
+ describe '#send_excel_report' do
59
+ it 'sends Excel data with default filename' do
60
+ controller.send_excel_report(report)
61
+
62
+ expect(controller.sent_data).to be_a(String)
63
+ expect(controller.send_options[:filename]).to match(/\.xlsx$/)
64
+ expect(controller.send_options[:type]).to eq('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
65
+ expect(controller.send_options[:disposition]).to eq('attachment')
66
+ end
67
+
68
+ it 'sends Excel data with custom filename' do
69
+ controller.send_excel_report(report, filename: 'custom_report.xlsx')
70
+
71
+ expect(controller.send_options[:filename]).to eq('custom_report.xlsx')
72
+ end
73
+
74
+ it 'sends Excel data with custom disposition' do
75
+ controller.send_excel_report(report, disposition: 'inline')
76
+
77
+ expect(controller.send_options[:disposition]).to eq('inline')
78
+ end
79
+ end
80
+
81
+ describe '#stream_excel_report' do
82
+ it 'sets up streaming response headers' do
83
+ controller.stream_excel_report(report)
84
+
85
+ expect(controller.response.headers['Content-Type']).to eq('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
86
+ expect(controller.response.headers['Content-Disposition']).to match(/attachment; filename=/)
87
+ expect(controller.response.headers['Content-Transfer-Encoding']).to eq('binary')
88
+ expect(controller.response.headers['Last-Modified']).to be_present
89
+ expect(controller.response.body).to be_a(StringIO)
90
+ end
91
+
92
+ it 'uses custom filename in Content-Disposition' do
93
+ controller.stream_excel_report(report, filename: 'custom_stream.xlsx')
94
+
95
+ expect(controller.response.headers['Content-Disposition']).to include('custom_stream.xlsx')
96
+ end
97
+ end
98
+
99
+ describe '#excel_report_response' do
100
+ it 'uses send_excel_report for small reports' do
101
+ allow(report).to receive(:should_stream?).and_return(false)
102
+ expect(controller).to receive(:send_excel_report).with(report, {})
103
+
104
+ controller.excel_report_response(report)
105
+ end
106
+
107
+ it 'uses stream_excel_report for large reports' do
108
+ allow(report).to receive(:should_stream?).and_return(true)
109
+ expect(controller).to receive(:stream_excel_report).with(report, {})
110
+
111
+ controller.excel_report_response(report)
112
+ end
113
+
114
+ it 'passes options to the appropriate method' do
115
+ allow(report).to receive(:should_stream?).and_return(false)
116
+ expect(controller).to receive(:send_excel_report).with(report, { filename: 'test.xlsx' })
117
+
118
+ controller.excel_report_response(report, filename: 'test.xlsx')
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RailsExcelReporter::Streaming do
4
+ let(:small_data) do
5
+ (1..10).map { |i| OpenStruct.new(id: i, name: "User #{i}") }
6
+ end
7
+
8
+ let(:large_data) do
9
+ (1..2000).map { |i| OpenStruct.new(id: i, name: "User #{i}") }
10
+ end
11
+
12
+ let(:report_class) do
13
+ Class.new(RailsExcelReporter::Base) do
14
+ attributes :id, :name
15
+ end
16
+ end
17
+
18
+ describe 'streaming threshold' do
19
+ it 'has default streaming threshold' do
20
+ expect(report_class.streaming_threshold).to eq(RailsExcelReporter.config.streaming_threshold)
21
+ end
22
+
23
+ it 'can set custom streaming threshold' do
24
+ report_class.streaming_threshold = 500
25
+ expect(report_class.streaming_threshold).to eq(500)
26
+ end
27
+ end
28
+
29
+ describe '#should_stream?' do
30
+ it 'returns false for small collections' do
31
+ report = report_class.new(small_data)
32
+ expect(report.should_stream?).to be false
33
+ end
34
+
35
+ it 'returns true for large collections' do
36
+ report = report_class.new(large_data)
37
+ expect(report.should_stream?).to be true
38
+ end
39
+ end
40
+
41
+ describe '#collection_size' do
42
+ it 'calculates size for arrays' do
43
+ report = report_class.new(small_data)
44
+ expect(report.collection_size).to eq(10)
45
+ end
46
+
47
+ it 'uses count method when available' do
48
+ mock_collection = double('Collection')
49
+ allow(mock_collection).to receive(:count).and_return(100)
50
+
51
+ report = report_class.new(mock_collection)
52
+ expect(report.collection_size).to eq(100)
53
+ end
54
+
55
+ it 'falls back to size method' do
56
+ mock_collection = double('Collection')
57
+ allow(mock_collection).to receive(:respond_to?).with(:count).and_return(false)
58
+ allow(mock_collection).to receive(:respond_to?).with(:size).and_return(true)
59
+ allow(mock_collection).to receive(:size).and_return(50)
60
+
61
+ report = report_class.new(mock_collection)
62
+ expect(report.collection_size).to eq(50)
63
+ end
64
+ end
65
+
66
+ describe '#stream_data' do
67
+ it 'yields each item in small collections' do
68
+ report = report_class.new(small_data)
69
+ yielded_items = []
70
+
71
+ report.stream_data do |item|
72
+ yielded_items << item
73
+ end
74
+
75
+ expect(yielded_items).to eq(small_data)
76
+ end
77
+
78
+ it 'uses find_each for large ActiveRecord-like collections' do
79
+ mock_collection = double('ActiveRecord Collection')
80
+ allow(mock_collection).to receive(:respond_to?).with(:count).and_return(true)
81
+ allow(mock_collection).to receive(:count).and_return(2000)
82
+ allow(mock_collection).to receive(:respond_to?).with(:find_each).and_return(true)
83
+
84
+ yielded_items = []
85
+ allow(mock_collection).to receive(:find_each).with(batch_size: 1000) do |&block|
86
+ large_data.each(&block)
87
+ end
88
+
89
+ report = report_class.new(mock_collection)
90
+
91
+ report.stream_data do |item|
92
+ yielded_items << item
93
+ end
94
+
95
+ expect(yielded_items.size).to eq(2000)
96
+ end
97
+
98
+ it 'returns enumerator when no block given' do
99
+ report = report_class.new(small_data)
100
+ enumerator = report.stream_data
101
+
102
+ expect(enumerator).to be_a(Enumerator)
103
+ expect(enumerator.to_a).to eq(small_data)
104
+ end
105
+ end
106
+
107
+ describe '#with_progress_tracking' do
108
+ it 'tracks progress and yields items with progress info' do
109
+ report = report_class.new(small_data)
110
+ progress_updates = []
111
+
112
+ report.with_progress_tracking do |_item, progress|
113
+ progress_updates << progress
114
+ end
115
+
116
+ expect(progress_updates.size).to eq(10)
117
+ expect(progress_updates.first.current).to eq(1)
118
+ expect(progress_updates.first.total).to eq(10)
119
+ expect(progress_updates.first.percentage).to eq(10.0)
120
+ expect(progress_updates.last.current).to eq(10)
121
+ expect(progress_updates.last.percentage).to eq(100.0)
122
+ end
123
+
124
+ it 'calls progress callback when provided' do
125
+ callback_calls = []
126
+ callback = proc { |progress| callback_calls << progress }
127
+
128
+ report = report_class.new(small_data, &callback)
129
+
130
+ report.with_progress_tracking do |item, progress|
131
+ # Just iterate
132
+ end
133
+
134
+ expect(callback_calls.size).to eq(10)
135
+ expect(callback_calls.first.current).to eq(1)
136
+ expect(callback_calls.last.current).to eq(10)
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RailsExcelReporter::Styling do
4
+ let(:styled_class) do
5
+ Class.new(RailsExcelReporter::Base) do
6
+ attributes :id, :name, :email
7
+
8
+ style :header, {
9
+ bg_color: 'FF0000',
10
+ fg_color: 'FFFFFF',
11
+ bold: true
12
+ }
13
+
14
+ style :id, {
15
+ alignment: { horizontal: :center }
16
+ }
17
+
18
+ style :name, {
19
+ bold: true,
20
+ font_size: 12
21
+ }
22
+ end
23
+ end
24
+
25
+ let(:sample_data) do
26
+ [
27
+ OpenStruct.new(id: 1, name: 'John', email: 'john@example.com'),
28
+ OpenStruct.new(id: 2, name: 'Jane', email: 'jane@example.com')
29
+ ]
30
+ end
31
+
32
+ let(:report) { styled_class.new(sample_data) }
33
+
34
+ describe 'class methods' do
35
+ describe '.style' do
36
+ it 'defines styles for targets' do
37
+ expect(styled_class.styles[:header]).to eq({
38
+ bg_color: 'FF0000',
39
+ fg_color: 'FFFFFF',
40
+ bold: true
41
+ })
42
+ end
43
+
44
+ it 'defines styles for columns' do
45
+ expect(styled_class.styles[:id]).to eq({
46
+ alignment: { horizontal: :center }
47
+ })
48
+ end
49
+ end
50
+
51
+ describe '.styles' do
52
+ it 'returns all defined styles' do
53
+ expect(styled_class.styles).to include(:header, :id, :name)
54
+ end
55
+ end
56
+ end
57
+
58
+ describe 'instance methods' do
59
+ describe '#get_header_style' do
60
+ it 'returns merged header style with defaults' do
61
+ header_style = report.get_header_style
62
+ expect(header_style[:bg_color]).to eq('FF0000')
63
+ expect(header_style[:bold]).to be true
64
+ end
65
+ end
66
+
67
+ describe '#get_column_style' do
68
+ it 'returns merged column style with defaults' do
69
+ id_style = report.get_column_style(:id)
70
+ expect(id_style[:alignment]).to eq({ horizontal: :center })
71
+ end
72
+
73
+ it 'returns default style for undefined columns' do
74
+ email_style = report.get_column_style(:email)
75
+ expect(email_style).to eq(RailsExcelReporter.config.default_styles[:cell])
76
+ end
77
+ end
78
+
79
+ describe '#build_caxlsx_style' do
80
+ it 'converts style options to caxlsx format' do
81
+ style_options = {
82
+ bg_color: 'FF0000',
83
+ fg_color: 'FFFFFF',
84
+ bold: true,
85
+ font_size: 12,
86
+ alignment: { horizontal: :center }
87
+ }
88
+
89
+ caxlsx_style = report.build_caxlsx_style(style_options)
90
+
91
+ expect(caxlsx_style[:bg_color]).to eq('FF0000')
92
+ expect(caxlsx_style[:fg_color]).to eq('FFFFFF')
93
+ expect(caxlsx_style[:b]).to be true
94
+ expect(caxlsx_style[:sz]).to eq(12)
95
+ expect(caxlsx_style[:alignment]).to eq({ horizontal: :center })
96
+ end
97
+ end
98
+
99
+ describe '#merge_styles' do
100
+ it 'merges multiple styles' do
101
+ styled_class.style :base, { bold: true, font_size: 10 }
102
+ styled_class.style :override, { font_size: 12, italic: true }
103
+
104
+ merged = report.merge_styles(:base, :override)
105
+
106
+ expect(merged[:bold]).to be true
107
+ expect(merged[:font_size]).to eq(12)
108
+ expect(merged[:italic]).to be true
109
+ end
110
+ end
111
+ end
112
+
113
+ describe 'inheritance' do
114
+ it 'inherits styles from parent class' do
115
+ child_class = Class.new(styled_class) do
116
+ style :email, { italic: true }
117
+ end
118
+
119
+ expect(child_class.styles).to include(:header, :id, :name, :email)
120
+ expect(child_class.styles[:header][:bold]).to be true
121
+ expect(child_class.styles[:email][:italic]).to be true
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RailsExcelReporter do
4
+ describe 'configuration' do
5
+ it 'has a default configuration' do
6
+ expect(RailsExcelReporter.configuration).to be_a(RailsExcelReporter::Configuration)
7
+ end
8
+
9
+ it 'can be configured' do
10
+ RailsExcelReporter.configure do |config|
11
+ config.date_format = '%d/%m/%Y'
12
+ config.streaming_threshold = 500
13
+ end
14
+
15
+ expect(RailsExcelReporter.config.date_format).to eq('%d/%m/%Y')
16
+ expect(RailsExcelReporter.config.streaming_threshold).to eq(500)
17
+ end
18
+ end
19
+
20
+ describe 'version' do
21
+ it 'has a version number' do
22
+ expect(RailsExcelReporter::VERSION).not_to be nil
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ require 'bundler/setup'
2
+ require 'rails_excel_reporter'
3
+ require 'rspec'
4
+ require 'tempfile'
5
+ require 'ostruct'
6
+
7
+ RSpec.configure do |config|
8
+ config.expect_with :rspec do |c|
9
+ c.syntax = :expect
10
+ end
11
+
12
+ config.mock_with :rspec do |mocks|
13
+ mocks.verify_partial_doubles = true
14
+ end
15
+
16
+ config.shared_context_metadata_behavior = :apply_to_host_groups
17
+ config.filter_run_when_matching :focus
18
+ config.disable_monkey_patching!
19
+ config.warnings = true
20
+
21
+ config.default_formatter = 'doc' if config.files_to_run.one?
22
+
23
+ config.profile_examples = 10
24
+ config.order = :random
25
+ Kernel.srand config.seed
26
+
27
+ config.before(:each) do
28
+ RailsExcelReporter.instance_variable_set(:@configuration, nil)
29
+ end
30
+ end