csv_rb 0.5.2
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 +7 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +24 -0
- data/Gemfile.lock +250 -0
- data/Guardfile +16 -0
- data/MIT-LICENSE +20 -0
- data/README.md +510 -0
- data/Rakefile +29 -0
- data/lib/csv_rb/action_controller.rb +66 -0
- data/lib/csv_rb/plain_builder.rb +27 -0
- data/lib/csv_rb/railtie.rb +16 -0
- data/lib/csv_rb/stream_csv_deflator.rb +25 -0
- data/lib/csv_rb/template_handler.rb +31 -0
- data/lib/csv_rb/version.rb +5 -0
- data/lib/csv_rb.rb +6 -0
- data/lib/tasks/csv_rb_tasks.rake +4 -0
- data/spec/ci.rb +4 -0
- data/spec/csv_rb_builder_spec.rb +39 -0
- data/spec/csv_rb_mailer_spec.rb +17 -0
- data/spec/csv_rb_renderer_spec.rb +18 -0
- data/spec/csv_rb_request_spec.rb +220 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/controllers/home_controller.rb +58 -0
- data/spec/dummy/app/controllers/likes_controller.rb +20 -0
- data/spec/dummy/app/controllers/users_controller.rb +39 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/notifier.rb +14 -0
- data/spec/dummy/app/models/like.rb +3 -0
- data/spec/dummy/app/models/user.rb +24 -0
- data/spec/dummy/app/views/home/_cover_sheet.csv.csvrb +1 -0
- data/spec/dummy/app/views/home/index.csv.csvrb +2 -0
- data/spec/dummy/app/views/home/index.html.erb +4 -0
- data/spec/dummy/app/views/home/only_html.html.erb +2 -0
- data/spec/dummy/app/views/home/useheader.csv.csvrb +1 -0
- data/spec/dummy/app/views/home/withpartial.csv.csvrb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +12 -0
- data/spec/dummy/app/views/layouts/users.html.erb +12 -0
- data/spec/dummy/app/views/likes/index.csv.csvrb +4 -0
- data/spec/dummy/app/views/likes/index.html.erb +17 -0
- data/spec/dummy/app/views/notifier/instructions.html.erb +14 -0
- data/spec/dummy/app/views/notifier/instructions.txt.erb +6 -0
- data/spec/dummy/app/views/users/export.csv.csvrb +1 -0
- data/spec/dummy/app/views/users/index.csv.csvrb +1 -0
- data/spec/dummy/app/views/users/index.html.erb +23 -0
- data/spec/dummy/app/views/users/noaction.csv.csvrb +1 -0
- data/spec/dummy/app/views/users/respond_with.csv.csvrb +1 -0
- data/spec/dummy/app/views/users/send_instructions.csv.csvrb +2 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +22 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +83 -0
- data/spec/dummy/config/environments/test.rb +40 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/secret_token.rb +2 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +17 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20120717192452_create_users.rb +12 -0
- data/spec/dummy/db/migrate/20121206210955_create_likes.rb +10 -0
- data/spec/dummy/db/schema.rb +31 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +257 -0
- data/spec/dummy/log/test.log +25347 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/reset_gems.sh +2 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/test_5.2.sh +16 -0
- data/spec/test_all_rails.sh +4 -0
- data/spec/test_revert.sh +3 -0
- metadata +413 -0
data/README.md
ADDED
@@ -0,0 +1,510 @@
|
|
1
|
+
csvrb-Rails — Spreadsheet templates for Rails
|
2
|
+
===================================================
|
3
|
+
|
4
|
+
[](http://travis-ci.org/straydogstudio/csv_rb)
|
5
|
+
[](http://badge.fury.io/rb/csv_rb)
|
7
|
+
[](https://gemnasium.com/straydogstudio/csv_rb)
|
8
|
+
[](https://coveralls.io/r/straydogstudio/csv_rb)
|
10
|
+
|
11
|
+

|
12
|
+

|
13
|
+

|
14
|
+

|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
In your Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'rubyzip', '>= 1.2.1'
|
22
|
+
gem 'csvrb', git: 'https://github.com/randym/csvrb.git', ref: 'c8ac844'
|
23
|
+
gem 'csv_rb'
|
24
|
+
```
|
25
|
+
|
26
|
+
**NOTE:** csvrb has been pending release for a long time. You must specify the master on github to support Rubyzip 1.2.1.
|
27
|
+
|
28
|
+
If `rubyzip 1.0.0` is needed:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
gem 'rubyzip', '= 1.0.0'
|
32
|
+
gem 'csvrb', '= 2.0.1'
|
33
|
+
gem 'csv_rb'
|
34
|
+
```
|
35
|
+
|
36
|
+
If `rubyzip >= 1.1.0` is needed:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
gem 'rubyzip', '~> 1.1.0'
|
40
|
+
gem 'csvrb', '2.1.0.pre'
|
41
|
+
gem 'csv_rb'
|
42
|
+
```
|
43
|
+
|
44
|
+
## Requirements
|
45
|
+
|
46
|
+
* Rails 4.2, 5.0 or 5.1 (tested)
|
47
|
+
* For Rails 3.1 or 3.2 use version 3.0
|
48
|
+
* **As of 0.5.0 requires csvrb 2.0.1, but strongly suggests 2.1.0.pre, which requires rubyzip 1.1.0**
|
49
|
+
* As of Rails 4.1 you must use `render_to_string` to render a mail attachment.
|
50
|
+
|
51
|
+
## FYI
|
52
|
+
|
53
|
+
* This gem depends on [csvrb](https://github.com/randym/csvrb). See [the blog](http://csvrb.blog.randym.net/) or the [examples page](https://github.com/randym/csvrb/blob/master/examples/example.rb) for usage.
|
54
|
+
* Check out [csvrb_styler](https://github.com/sakovias/csvrb_styler) by [sakovias](https://github.com/sakovias) for easier styles and borders!
|
55
|
+
|
56
|
+
## Usage
|
57
|
+
|
58
|
+
csvrb-Rails provides a renderer and a template handler. It adds the `:csv` format and parses `.csv.csvrb` templates. This lets you take all the [csvrb](https://github.com/randym/csvrb) code out of your controller or model and place it inside the template, where view code belongs! **See [this blog post](http://csvrb.blog.randym.net/2012/08/excel-on-rails-like-pro-with-CSVRb.html) for a more complete walkthrough.**
|
59
|
+
|
60
|
+
### Controller
|
61
|
+
|
62
|
+
To use csvrb-Rails set your instance variables in your controller and configure the response if needed:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
class ButtonController < ApplicationController
|
66
|
+
def action_name
|
67
|
+
@buttons = Button.all
|
68
|
+
respond_to do |format|
|
69
|
+
format.csv
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
### Template
|
76
|
+
|
77
|
+
Create the template with the `.csv.csvrb` extension (`action_name.csv.csvrb` for example.) [**Watch out for typos!**](#troubleshooting) In the template, use csv_package variable to create your spreadsheet:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
wb = csv_package.workbook
|
81
|
+
wb.add_worksheet(name: "Buttons") do |sheet|
|
82
|
+
@buttons.each do |button|
|
83
|
+
sheet.add_row [button.name, button.category, button.price]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
This is where you place all your [csvrb](https://github.com/randym/csvrb) specific markup. Add worksheets, fill content, merge cells, add styles. See the [csvrb examples](https://github.com/randym/csvrb/tree/master/examples/example.rb) page to see what you can do.
|
89
|
+
|
90
|
+
Remember, like in `erb` templates, view helpers are available to use the `.csv.csvrb` template.
|
91
|
+
|
92
|
+
That's it. Call your action and your spreadsheet will be delivered.
|
93
|
+
|
94
|
+
### Rendering Options
|
95
|
+
|
96
|
+
You can call render in any of the following ways:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
# rendered, no disposition/filename header
|
100
|
+
render 'buttons'
|
101
|
+
# rendered from another controller, no disposition/filename header
|
102
|
+
render 'featured/latest'
|
103
|
+
# template and filename of 'buttons'
|
104
|
+
render csv: 'buttons'
|
105
|
+
# template from another controller, filename of 'latest_buttons'
|
106
|
+
render csv: 'latest_buttons', template: 'featured/latest'
|
107
|
+
```
|
108
|
+
|
109
|
+
### Disposition
|
110
|
+
|
111
|
+
To specify a disposition (such as `inline` so the spreadsheet is opened inside the browser), use the `disposition` option:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
render csv: "buttons", disposition: 'inline'
|
115
|
+
```
|
116
|
+
|
117
|
+
If `render csv:` is called, the disposition defaults to `attachment`.
|
118
|
+
|
119
|
+
### File name
|
120
|
+
|
121
|
+
If Rails calls csvrb through default channels (because you use `format.csv {}` for example) you must set the filename using the response header:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
format.csv {
|
125
|
+
response.headers['Content-Disposition'] = 'attachment; filename="my_new_filename.csv"'
|
126
|
+
}
|
127
|
+
```
|
128
|
+
|
129
|
+
If you use `render csv:` the gem will try to guess the file name:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
# filename of 'buttons'
|
133
|
+
render csv: 'buttons'
|
134
|
+
# filename of 'latest_buttons'
|
135
|
+
render csv: 'latest_buttons', template: 'featured/latest'
|
136
|
+
```
|
137
|
+
|
138
|
+
If that fails, pass the `:filename` parameter:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
render csv: "action_or_template", filename: "my_new_filename.csv"
|
142
|
+
```
|
143
|
+
|
144
|
+
### csv_rb Package Options
|
145
|
+
|
146
|
+
csv_rb provides three options for initializing a spreadsheet:
|
147
|
+
|
148
|
+
- **:csv_author** (String) - The author of the document
|
149
|
+
- **:csv_created_at** (Time) - Timestamp in the document properties (defaults to current time)
|
150
|
+
- **:csv_use_shared_strings** (Boolean) - This is passed to the workbook to specify that shared strings should be used when serializing the package.
|
151
|
+
|
152
|
+
To pass these to the new package, pass them to `render :csv` _or_ pass them as local variables.
|
153
|
+
|
154
|
+
For example, to set the author name, pass the `:csv_author` parameter to `render :csv` _or_ as a local variable:
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
render csv: "index", csv_author: "Elmer Fudd"
|
158
|
+
render "index", locals: {csv_author: "Elmer Fudd"}
|
159
|
+
```
|
160
|
+
|
161
|
+
Other examples:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
render csv: "index", csv_created_at: 3.days.ago
|
165
|
+
render "index", locals: {csv_use_shared_strings: true}
|
166
|
+
```
|
167
|
+
|
168
|
+
### Partials
|
169
|
+
|
170
|
+
Partials work as expected, but you must pass in relevant spreadsheet variables:
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
wb = csv_package.workbook
|
174
|
+
render :partial => 'cover_sheet', :locals => {:wb => wb}
|
175
|
+
wb.add_worksheet(name: "Content") do |sheet|
|
176
|
+
sheet.add_row ['Content']
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
With the partial simply using the passed variables:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
wb.add_worksheet(name: "Cover Sheet") do |sheet|
|
184
|
+
sheet.add_row ['Cover', 'Sheet']
|
185
|
+
end
|
186
|
+
```
|
187
|
+
|
188
|
+
### Mailers
|
189
|
+
|
190
|
+
To use an csv template to render a mail attachment, use the following syntax:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
class UserMailer < ActionMailer::Base
|
194
|
+
def export(users)
|
195
|
+
csv = render_to_string layout: false, handlers: [:csvrb], formats: [:csv], template: "users/export", locals: {users: users}
|
196
|
+
attachment = Base64.encode64(csv)
|
197
|
+
attachments["Users.csv"] = {mime_type: Mime[:csv], content: attachment, encoding: 'base64'}
|
198
|
+
# For rails 4 use Mime::csv
|
199
|
+
# attachments["Users.csv"] = {mime_type: Mime::csv, content: attachment, encoding: 'base64'}
|
200
|
+
# self.instance_variable_set(:@_lookup_context, nil) # If attachments are rendered as content, try this and open an issue
|
201
|
+
...
|
202
|
+
end
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
* If the route specifies or suggests the `:csv` format you do not need to specify `formats` or `handlers`.
|
207
|
+
* If the template (`users/export`) can refer to only one file (the csv.csvrb template), you do not need to specify `handlers`, provided the `formats` includes `:csv`.
|
208
|
+
* Specifying the encoding as 'base64' can avoid UTF-8 errors.
|
209
|
+
|
210
|
+
### Scripts
|
211
|
+
|
212
|
+
To generate a template within a script, you need to instantiate an ActionView context. Here are two gists showing how to perform this:
|
213
|
+
|
214
|
+
* [Using rails runner](https://gist.github.com/straydogstudio/323139591f2cc5d48fbc)
|
215
|
+
* [Without rails runner](https://gist.github.com/straydogstudio/dceb775ead81470cea70)
|
216
|
+
|
217
|
+
### Testing
|
218
|
+
|
219
|
+
There is no built-in way to test your resulting workbooks / templates. But here is a piece of code that may help you to find a way.
|
220
|
+
|
221
|
+
#### First, create a shared context
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
RSpec.shared_context 'csvrb' do
|
225
|
+
|
226
|
+
# all csv specs describe must be normalized
|
227
|
+
# "folder/view_name.csv.csvrb"
|
228
|
+
# allow to infer the template path
|
229
|
+
template_name = description
|
230
|
+
|
231
|
+
let(:template_path) do
|
232
|
+
['app', 'views', template_name]
|
233
|
+
end
|
234
|
+
|
235
|
+
# This helper will be used in tests
|
236
|
+
def render_template(locals = {})
|
237
|
+
csvrb_binding = Kernel.binding
|
238
|
+
locals.each do |key, value|
|
239
|
+
csvrb_binding.local_variable_set key, value
|
240
|
+
end
|
241
|
+
# define a default workbook and a default sheet useful when testing partial in isolation
|
242
|
+
wb = CSVRb::Package.new.workbook
|
243
|
+
csvrb_binding.local_variable_set(:wb, wb)
|
244
|
+
csvrb_binding.local_variable_set(:sheet, wb.add_worksheet)
|
245
|
+
|
246
|
+
# mimics an ActionView::Template class, presenting a 'source' method
|
247
|
+
# to retrieve the content of the template
|
248
|
+
csvrb_binding.eval(ActionView::Template::Handlers::CSVRbBuilder.call(Struct.new(:source).new(File.read(Rails.root.join(*template_path)))))
|
249
|
+
csvrb_binding.local_variable_get(:wb)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
#### Include it in your spec files:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
require 'spec_helper'
|
258
|
+
require 'helpers/csvrb_context'
|
259
|
+
|
260
|
+
describe 'shared/_total_request.csv.csvrb' do
|
261
|
+
include_context 'csvrb'
|
262
|
+
|
263
|
+
before :each do
|
264
|
+
# all the instance variables here are the one used in 'shared/_total_request.csv.csvrb'
|
265
|
+
@widget = mock_model(Widget, name: 'My widget')
|
266
|
+
@message_counts = Struct.new(:count_all, :positives, :negatives, :neutrals).new(42, 23, 15, 25)
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'has a title line mentioning the widget' do
|
270
|
+
wb = render_template
|
271
|
+
sheet = wb.sheet_by_name('Réf. Requête')
|
272
|
+
expect(sheet).to have_header_cells ['My widget : Messages de la requête']
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'exports the message counts' do
|
276
|
+
wb = render_template
|
277
|
+
sheet = wb.sheet_by_name('Réf. Requête')
|
278
|
+
expect(sheet).to have_cells(['Toutes tonalités', 'Tonalité positive', 'Tonalité négative', 'Tonalité neutre']).in_row(2)
|
279
|
+
expect(sheet).to have_cells([42, 23, 15, 25]).in_row(3)
|
280
|
+
end
|
281
|
+
|
282
|
+
end
|
283
|
+
```
|
284
|
+
|
285
|
+
#### Matchers used
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
|
289
|
+
# encoding: UTF-8
|
290
|
+
|
291
|
+
require 'rspec/expectations'
|
292
|
+
|
293
|
+
module XslsMatchers
|
294
|
+
|
295
|
+
RSpec::Matchers.define :have_header_cells do |cell_values|
|
296
|
+
match do |worksheet|
|
297
|
+
worksheet.rows[0].cells.map(&:value) == cell_values
|
298
|
+
end
|
299
|
+
|
300
|
+
failure_message do |actual|
|
301
|
+
"Expected #{actual.rows[0].cells.map(&:value)} to be #{expected}"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
RSpec::Matchers.define :have_cells do |expected|
|
306
|
+
match do |worksheet|
|
307
|
+
worksheet.rows[@index].cells.map(&:value) == expected
|
308
|
+
end
|
309
|
+
|
310
|
+
chain :in_row do |index|
|
311
|
+
@index = index
|
312
|
+
end
|
313
|
+
|
314
|
+
failure_message do |actual|
|
315
|
+
"Expected #{actual.rows[@index].cells.map(&:value)} to include #{expected} at row #{@index}."
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
```
|
321
|
+
|
322
|
+
|
323
|
+
## Troubleshooting
|
324
|
+
|
325
|
+
### Mispellings
|
326
|
+
|
327
|
+
**It is easy to get the spelling wrong in the extension name, the format.csv statement, or in a render call.** Here are some possibilities:
|
328
|
+
|
329
|
+
* If it says your template is missing, check that its extension is `.csv.csvrb`.
|
330
|
+
* If you get the error `uninitialized constant Mime::XSLX` you have used `format.xslx` instead of `format.csv`, or something similar.
|
331
|
+
|
332
|
+
### Mailer Attachments: No content, cannot read, Invalid Byte Sequence in UTF-8
|
333
|
+
|
334
|
+
If you are having problems with rendering a template and attaching it to a template, try a few options:
|
335
|
+
|
336
|
+
* Make sure the attachment template does not have the same name as the mailer.
|
337
|
+
* After you have rendered the template to string, and before you call the mailer, execute `self.instance_variable_set(:@_lookup_context, nil)`. If you must do this, please open an issue.
|
338
|
+
* If you get Invalid Byte Sequence in UTF-8, pass `encoding: 'base64'` with the attachment:
|
339
|
+
|
340
|
+
```ruby
|
341
|
+
class UserMailer < ActionMailer::Base
|
342
|
+
def export(users)
|
343
|
+
csv = render_to_string handlers: [:csvrb], formats: [:csv], template: "users/export", locals: {users: users}
|
344
|
+
attachments["Users.csv"] = {mime_type: Mime[:csv], content: csv, encoding: 'base64'}
|
345
|
+
# For Rails 4 use Mime::csv
|
346
|
+
# attachments["Users.csv"] = {mime_type: Mime::csv, content: csv, encoding: 'base64'}
|
347
|
+
# self.instance_variable_set(:@_lookup_context, nil) # If attachments are rendered as content, try this and open an issue
|
348
|
+
...
|
349
|
+
end
|
350
|
+
end
|
351
|
+
```
|
352
|
+
|
353
|
+
If you get these errors, please open an issue and share code so the bug can be isolated. Or comment on issue [#29](https://github.com/straydogstudio/csv_rb/issues/29) or [#25](https://github.com/straydogstudio/csv_rb/issues/25).
|
354
|
+
|
355
|
+
### Generated Files Can't Be Opened or Invalid Byte Sequence in UTF-8
|
356
|
+
|
357
|
+
Both these errors *appear* to be caused by Rails applying a layout to the template. Passing `layout: false` to `render :csv` should fix this issue. Version 0.5.0 attempts to fix this issue.
|
358
|
+
|
359
|
+
If you get this error, please open an issue and share code so the bug can be isolated.
|
360
|
+
|
361
|
+
### Rails 4.2 changes
|
362
|
+
|
363
|
+
Before Rails 4.2 you could call:
|
364
|
+
|
365
|
+
```ruby
|
366
|
+
render csv: "users/index"
|
367
|
+
```
|
368
|
+
|
369
|
+
And csv_rb could adjust the paths and make sure the template was loaded from the right directory. This is no longer possible because the paths are cached between requests for a given controller. As a result, to display a template in another directory you must use the `:template` parameter (which is normal Rails behavior anyway):
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
render csv: "index", template: "users/index"
|
373
|
+
```
|
374
|
+
|
375
|
+
If the request format matches you should be able to call:
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
render "users/index"
|
379
|
+
```
|
380
|
+
|
381
|
+
This is a breaking change if you have the old syntax!
|
382
|
+
|
383
|
+
### Turbolinks
|
384
|
+
|
385
|
+
If you are using turbolinks, you may need to disable turbolinks when you link to your spreadsheet:
|
386
|
+
|
387
|
+
```ruby
|
388
|
+
# turbolinks 5:
|
389
|
+
link_to 'Download spreadsheet', path_to_sheet, data: {turbolinks: false}
|
390
|
+
```
|
391
|
+
|
392
|
+
### What to do
|
393
|
+
|
394
|
+
If you are having problems, try to isolate the issue. Use the console or a script to make sure your data is good. Then create the spreadsheet line by line without csvrb-Rails to see if you are having csvrb problems. If you can manually create the spreadsheet, create an issue and we will work it out.
|
395
|
+
|
396
|
+
## Dependencies
|
397
|
+
|
398
|
+
- [Rails](https://github.com/rails/rails)
|
399
|
+
- [csvrb](https://github.com/randym/csvrb)
|
400
|
+
|
401
|
+
## Authors
|
402
|
+
|
403
|
+
* [Noel Peden](https://github.com/straydogstudio)
|
404
|
+
|
405
|
+
## Contributors
|
406
|
+
|
407
|
+
Many thanks to [contributors](https://github.com/straydogstudio/csv_rb/graphs/contributors):
|
408
|
+
|
409
|
+
* [randym](https://github.com/randym)
|
410
|
+
* [sugi](https://github.com/sugi)
|
411
|
+
* [envek](https://github.com/envek)
|
412
|
+
* [engwan](https://github.com/engwan)
|
413
|
+
* [maxd](https://github.com/maxd)
|
414
|
+
* [firien](https://github.com/firien)
|
415
|
+
* [kaluzny](https://github.com/kaluznyo)
|
416
|
+
* [sly7-7](https://github.com/sly7-7)
|
417
|
+
* [kodram](https://github.com/kodram)
|
418
|
+
* [JohnSmall](https://github.com/JohnSmall)
|
419
|
+
* [BenoitHiller](https://github.com/BenoitHiller)
|
420
|
+
|
421
|
+
## Donations
|
422
|
+
|
423
|
+
Say thanks for csvrb-Rails by donating! It makes it easier for me to provide to open
|
424
|
+
source:
|
425
|
+
|
426
|
+
[](http://www.pledgie.com/campaigns/27737)
|
427
|
+
|
428
|
+
## Change log
|
429
|
+
|
430
|
+
**May 1st, 2018**: 0.5.2 release
|
431
|
+
|
432
|
+
- Improved Rails 5 compatibility re MIME type
|
433
|
+
|
434
|
+
**March 29th, 2017**: 0.5.1 release
|
435
|
+
|
436
|
+
- Fix stack trace line numbers
|
437
|
+
- Thanks to [BenoitHiller](https://github.com/BenoitHiller)
|
438
|
+
|
439
|
+
**July 26st, 2016**: 0.5.0 release
|
440
|
+
|
441
|
+
- Support for Rails 5
|
442
|
+
- **Tested on on Rails 4.0, 4.1, 4.2, and 5.0**
|
443
|
+
- Bug fixes for unreadable files and UTF-8 errors
|
444
|
+
|
445
|
+
**July 13th, 2015**: 0.4.0 release
|
446
|
+
|
447
|
+
- Support for Rails 4.2
|
448
|
+
- **Removal of forced default_formats** (url format must match)
|
449
|
+
- **Tested only on Rails 4.1 and 4.2**
|
450
|
+
- **For Rails 3.2 or below, use 0.3.0**
|
451
|
+
|
452
|
+
**November 20th, 2014**: 0.3.0 release
|
453
|
+
|
454
|
+
- Support for Rails 4.2.beta4.
|
455
|
+
- **Removal of shorthand template syntax** (`render csv: 'another/directory'`)
|
456
|
+
|
457
|
+
**September 4, 2014**: 0.2.1 release
|
458
|
+
|
459
|
+
- Rails 4.2.beta1 no longer includes responder. This release checks for the existence of responder before configuring a default responder.
|
460
|
+
- Rails 4.2 testing, though not yet on Travis CI
|
461
|
+
- Author, created_at, and use_shared_strings parameters for CSVRb::Package.new
|
462
|
+
|
463
|
+
**April 9, 2014**: 0.2.0 release
|
464
|
+
|
465
|
+
- Require csvrb 2.0.1, which requires rubyzip 1.0.0
|
466
|
+
- Better render handling and testing, which might break former usage
|
467
|
+
- Rails 4.1 testing
|
468
|
+
- Mailer example update (**use render_to_string not render**)
|
469
|
+
|
470
|
+
**October 11, 2013**
|
471
|
+
|
472
|
+
- Handle (and test) respond_to override
|
473
|
+
|
474
|
+
**October 4, 2013**
|
475
|
+
|
476
|
+
- Added coveralls
|
477
|
+
- Raised testing to csvrb 2.0.1, roo 1.12.2, and rubyzip 1.0.0
|
478
|
+
|
479
|
+
**July 25, 2013**
|
480
|
+
|
481
|
+
- Documentation improved
|
482
|
+
- Testing for generating partial in mailer
|
483
|
+
|
484
|
+
**January 18, 2013**: 0.1.4 release
|
485
|
+
|
486
|
+
- Now supports Rails 4 (thanks [Envek](https://github.com/Envek))
|
487
|
+
- If you call render :csv on a request without :csv format, it should force the :csv format. Works on Rails 3.2+.
|
488
|
+
|
489
|
+
**December 6, 2012**: 0.1.3 release
|
490
|
+
|
491
|
+
- Fix for absolute template paths
|
492
|
+
|
493
|
+
**July 25, 2012**: 0.1.2 release
|
494
|
+
|
495
|
+
- Partials tested
|
496
|
+
|
497
|
+
**July 19, 2012**: 0.1.1 release
|
498
|
+
|
499
|
+
- Travis-ci added (thanks [randym](https://github.com/randym))
|
500
|
+
- render statements and filename tests fixes (thanks [engwan](https://github.com/engwan))
|
501
|
+
|
502
|
+
**July 17, 2012**: 0.1.0 release
|
503
|
+
|
504
|
+
- Tests completed
|
505
|
+
- Acts_as_csv tested, example in docs
|
506
|
+
|
507
|
+
**July 12, 2012**: 0.0.1 release
|
508
|
+
|
509
|
+
- Initial posting.
|
510
|
+
- It works, but there are no tests! Bad programmer!
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'CSVRb'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
require "rspec/core/rake_task"
|
24
|
+
RSpec::Core::RakeTask.new('spec')
|
25
|
+
|
26
|
+
task :default => :spec
|
27
|
+
|
28
|
+
Bundler::GemHelper.install_tasks
|
29
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_controller'
|
4
|
+
require 'csv_rb/stream_csv_deflator'
|
5
|
+
unless Mime[:csv]
|
6
|
+
Mime::Type.register 'text/csv', :csv
|
7
|
+
end
|
8
|
+
|
9
|
+
ActionController::Renderers.add :csv do |filename, options|
|
10
|
+
#
|
11
|
+
# You can always specify a template:
|
12
|
+
#
|
13
|
+
# def called_action
|
14
|
+
# render csv: 'filename', template: 'controller/diff_action'
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# And the normal use case works:
|
18
|
+
#
|
19
|
+
# def called_action
|
20
|
+
# render 'diff_action'
|
21
|
+
# # or
|
22
|
+
# render 'controller/diff_action'
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
options[:template] = filename.gsub(/^.*\//,'') if options[:template] == action_name
|
26
|
+
options[:template] = "#{options[:template]}.csv.csvrb" unless options[:template] =~ /\.csvrb/
|
27
|
+
options[:layout] = false
|
28
|
+
options[:locals] ||= {}
|
29
|
+
file_name = "#{options.delete(:filename) || filename.gsub(/^.*\//,'')}#{options.delete(:with_time) ? "-#{Time.zone.now.to_s}" : ''}.csv".sub(/(\.csv)+$/, '.csv')
|
30
|
+
|
31
|
+
|
32
|
+
# if options.delete(:should_stream)
|
33
|
+
response.headers["X-Accel-Buffering"] = 'no'
|
34
|
+
response.headers["Content-Type"] = "text/csv; charset=utf-8"
|
35
|
+
response.headers["Content-Encoding"] = 'deflate'
|
36
|
+
response.headers["Content-Disposition"] = %(attachment; filename="#{file_name}")
|
37
|
+
|
38
|
+
|
39
|
+
return self.response_body = Enumerator.new do |y|
|
40
|
+
csv = CSVRb::StreamCSVDeflator.new(y)
|
41
|
+
instance_eval lookup_context.find_template(options[:template], options[:prefixes], options[:partial], options.dup.merge(formats: [:csv])).source
|
42
|
+
csv.close
|
43
|
+
end
|
44
|
+
# else
|
45
|
+
# disposition = options.delete(:disposition) || 'attachment'
|
46
|
+
#
|
47
|
+
# send_data render_to_string(options), filename: file_name, type: Mime[:csv], disposition: disposition
|
48
|
+
# end
|
49
|
+
end
|
50
|
+
|
51
|
+
# For respond_to default
|
52
|
+
begin
|
53
|
+
ActionController::Responder
|
54
|
+
rescue
|
55
|
+
else
|
56
|
+
class ActionController::Responder
|
57
|
+
def to_csv
|
58
|
+
@_action_has_layout = false
|
59
|
+
if @default_response
|
60
|
+
@default_response.call(options)
|
61
|
+
else
|
62
|
+
controller.render({csv: controller.action_name}.merge(options))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CSVRb
|
2
|
+
class PlainBuilder
|
3
|
+
def value
|
4
|
+
@value ||= "#{}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def stream(row)
|
8
|
+
value << CSV.generate_line(row, force_quotes: true, encoding: 'utf-8')
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(row)
|
12
|
+
stream(row)
|
13
|
+
end
|
14
|
+
|
15
|
+
def close
|
16
|
+
to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_str
|
20
|
+
to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVRb
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
initializer 'csv_rb.initialization' do
|
6
|
+
ActiveSupport.on_load(:action_view) do
|
7
|
+
require 'csv_rb/template_handler'
|
8
|
+
ActionView::Template.register_template_handler :csvrb, ActionView::Template::Handlers::CSVRbBuilder.new
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveSupport.on_load(:action_controller) do
|
12
|
+
require 'csv_rb/action_controller'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CSVRb
|
2
|
+
class StreamCSVDeflator
|
3
|
+
def initialize(enum)
|
4
|
+
@enum = enum
|
5
|
+
@deflator = Zlib::Deflate.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def y
|
9
|
+
@enum
|
10
|
+
end
|
11
|
+
|
12
|
+
def stream(row)
|
13
|
+
v = CSV.generate_line(row, force_quotes: true, encoding: 'utf-8')
|
14
|
+
y << @deflator.deflate(v, Zlib::SYNC_FLUSH)
|
15
|
+
end
|
16
|
+
|
17
|
+
def <<(row)
|
18
|
+
stream(row)
|
19
|
+
end
|
20
|
+
|
21
|
+
def close
|
22
|
+
y << @deflator.flush(Zlib::FINISH)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|