activeadmin-xls 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5542c9ca3fb9ec53761a8f17c84d33590ac41fd4
4
+ data.tar.gz: 262f8f4e6e8068e1f8e2fb41744c1695454a965c
5
+ SHA512:
6
+ metadata.gz: 0d13b40efc0caab8e86510fbec44a8d2c9f9eb196440a311a8a2d8113218782f2311d0afe23700ab1ca394a19f647c87c944f25da3e67e4123011b9fbec4b5a2
7
+ data.tar.gz: 6b80baec72b523eda62d72975e3158026c76f37a3e0d1ad149264ce4331fa6a583752fa5d63679354c14812b1669b471d0066f2925ad1778e5d212b7295b90f8
data/.bundle/config ADDED
@@ -0,0 +1 @@
1
+ --- {}
data/.editorconfig ADDED
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 2
6
+ end_of_line = crlf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = false
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ spec/rails
2
+ *.gem
3
+ coverage
4
+ *.xlsx
5
+ doc
6
+ *.un~
7
+ .yardoc
8
+ .rbenv-version
9
+ .ruby-version
10
+ /Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.yardops ADDED
@@ -0,0 +1 @@
1
+ --no-private --protected app/**/*.rb - README.md LEGAL
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## 1.0.2
4
+
5
+ ### Bug Fixes
6
+
7
+ * Fixes undefined local variable or `method max_per_page` [#3][] by [@rewritten][]
8
+
9
+ ## 1.0.3
10
+
11
+ ### Updates
12
+
13
+ * Move require rake from gemspec to lib/activeadmin-xls.rb [#4][] by [@ejaypcanaria][]
14
+
15
+ <!--- Link List --->
16
+ [#3]: https://github.com/thambley/activeadmin-xls/issues/3
17
+ [#4]: https://github.com/thambley/activeadmin-xls/pull/4
18
+
19
+ [@rewritten]: https://github.com/rewritten
20
+ [@ejaypcanaria]: https://github.com/ejaypcanaria
data/Gemfile ADDED
@@ -0,0 +1,30 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activeadmin', '~> 1.0'
4
+ gem 'spreadsheet', '~> 1.1', '>= 1.1.4'
5
+
6
+ group :development, :test do
7
+ gem 'haml', require: false
8
+ gem 'rails-i18n' # Gives us default i18n for many languages
9
+ gem 'rdiscount' # For yard
10
+ gem 'sprockets'
11
+ gem 'sqlite3'
12
+ gem 'yard'
13
+ end
14
+
15
+ group :test do
16
+ gem 'capybara'
17
+ gem 'cucumber-rails', require: false
18
+ gem 'database_cleaner'
19
+ gem 'guard-coffeescript'
20
+ gem 'guard-rspec'
21
+ gem 'inherited_resources'
22
+ gem 'jasmine'
23
+ gem 'jslint_on_rails', '~> 1.0.6'
24
+ gem 'launchy'
25
+ gem 'rspec-mocks'
26
+ gem 'rspec-rails'
27
+ gem 'sass-rails'
28
+ gem 'shoulda-matchers', '1.0.0'
29
+ gem 'simplecov', require: false
30
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Todd Hambley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # Active Admin Xls: Excel Spreadsheet Export for Active Admin
2
+
3
+ **Git**: [http://github.com/thambley/activeadmin-xls](http://github.com/thambley/activeadmin-xls)
4
+
5
+ **Author**: Todd Hambley
6
+
7
+ **Copyright**: 2014 ~ 2017
8
+
9
+ **License**: MIT License
10
+
11
+ **Latest Version**: 1.0.4
12
+
13
+ **Release Date**: 2017.11.22
14
+
15
+ ## Synopsis
16
+
17
+ This gem provides xls downloads for Active Admin resources.
18
+
19
+ This gem borrows heavily from [https://github.com/randym/activeadmin-axlsx](https://github.com/randym/activeadmin-axlsx) and [https://github.com/splendeo/to_xls](https://github.com/splendeo/to_xls).
20
+
21
+ Usage example:
22
+
23
+ Add the following to your Gemfile and you are good to go.
24
+ All resource index views will now include a link for download directly
25
+ to xls.
26
+
27
+ ```ruby
28
+ gem 'activeadmin-xls'
29
+ ```
30
+
31
+ ## Cool Toys
32
+
33
+ Here are a few quick examples of things you can easily tweak.
34
+
35
+ ### Localize column headers
36
+
37
+ ```ruby
38
+ # app/admin/posts.rb
39
+ ActiveAdmin.register Post do
40
+ config.xls_builder.i18n_scope = [:active_record, :models, :posts]
41
+ end
42
+ ```
43
+
44
+ ### Use blocks for adding computed fields
45
+
46
+ ```ruby
47
+ # app/admin/posts.rb
48
+ ActiveAdmin.register Post do
49
+ config.xls_builder.column('author_name') do |resource|
50
+ resource.author.name
51
+ end
52
+ end
53
+ ```
54
+
55
+ ### Change the column header format
56
+
57
+ ```ruby
58
+ # app/admin/posts.rb
59
+ ActiveAdmin.register Post do
60
+ config.xls_builder.header_format = { weight: :bold,
61
+ color: :blue }
62
+ end
63
+ ```
64
+
65
+ ### Remove columns
66
+
67
+ ```ruby
68
+ # app/admin/posts.rb
69
+ ActiveAdmin.register Post do
70
+ config.xls_builder.delete_columns :id, :created_at, :updated_at
71
+ end
72
+ ```
73
+
74
+ ## Using the DSL
75
+
76
+ Everything that you do with the config's default builder can be done via
77
+ the resource DSL.
78
+
79
+ Below is an example of the DSL
80
+
81
+ ```ruby
82
+ ActiveAdmin.register Post do
83
+
84
+ # i18n_scope and header style are set via options
85
+ xls(i18n_scope: [:active_admin, :xls, :post],
86
+ header_format: { weight: :bold, color: :blue }) do
87
+
88
+ # Specify that you want to white list column output.
89
+ # whitelist
90
+
91
+ # Do not serialize the header, only output data.
92
+ # skip_header
93
+
94
+ # deleting columns from the report
95
+ delete_columns :id, :created_at, :updated_at
96
+
97
+ # adding a column to the report
98
+ column(:author) { |post| "#{post.author.first_name} #{post.author.last_name}" }
99
+
100
+ # inserting additional data with after_filter
101
+ after_filter do |sheet|
102
+ # todo
103
+ end
104
+
105
+ # inserting data with before_filter
106
+ before_filter do |sheet|
107
+ # todo
108
+ end
109
+ end
110
+ end
111
+ ```
112
+
113
+ ## Specs
114
+
115
+ Running specs for this gem requires that you construct a rails application.
116
+ To execute the specs, navigate to the gem directory,
117
+ run bundle install and run these to rake tasks:
118
+
119
+ ```text
120
+ bundle exec rake setup
121
+ ```
122
+
123
+ ```text
124
+ bundle exec rake
125
+ ```
126
+
127
+ ## Copyright and License
128
+
129
+ activeadmin-xls &copy; 2014 by [Todd Hambley](mailto:thambley@travelleaders.com).
130
+
131
+ activeadmin-xls is licensed under the MIT license. Please see the LICENSE document for more information.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env rake
2
+ require "activeadmin"
3
+ require "rspec/core/rake_task"
4
+
5
+ desc "Creates a test rails app for the specs to run against"
6
+ task :setup do
7
+ require 'rails/version'
8
+ system("mkdir spec/rails") unless File.exists?("spec/rails")
9
+ system "bundle exec rails new spec/rails/rails-#{Rails::VERSION::STRING} -m spec/support/rails_template_with_data.rb"
10
+ end
11
+
12
+ RSpec::Core::RakeTask.new
13
+ task :default => :spec
14
+ task :test => :spec
15
+
16
+ desc "build the gem"
17
+ task :build do
18
+ system "gem build activeadmin-xls.gemspec"
19
+ end
20
+ desc "build and release the gem"
21
+ task :release => :build do
22
+ system "gem push activeadmin-xls-#{ActiveAdmin::Xls::VERSION}.gem"
23
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path('../lib/active_admin/xls/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'activeadmin-xls'
5
+ s.version = ActiveAdmin::Xls::VERSION
6
+ s.author = 'Todd Hambley'
7
+ s.email = 'thambley@travelleaders.com'
8
+ s.homepage = 'https://github.com/thambley/activeadmin-xls'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.date = Time.now.strftime('%Y-%m-%d')
11
+ s.license = 'MIT'
12
+ s.summary = <<-SUMMARY
13
+ Adds excel (xls) downloads for resources within the Active Admin framework.
14
+ SUMMARY
15
+ s.description = <<-DESC
16
+ This gem provides excel/xls downloads for resources in Active Admin.
17
+ DESC
18
+ s.files = `git ls-files`.split("\n").sort
19
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
20
+ s.test_files = Dir.glob('{spec/**/*}')
21
+
22
+ s.add_runtime_dependency 'activeadmin', '>= 0.6.6', '< 2'
23
+ s.add_runtime_dependency 'spreadsheet', '~> 1.0'
24
+
25
+ s.required_ruby_version = '>= 1.9.2'
26
+ s.require_path = 'lib'
27
+ end
@@ -0,0 +1,259 @@
1
+ require 'stringio'
2
+ require 'spreadsheet'
3
+
4
+ module ActiveAdmin
5
+ module Xls
6
+ # Builder for xls data.
7
+ class Builder
8
+ include MethodOrProcHelper
9
+
10
+ # @param resource_class The resource this builder generate column
11
+ # information for.
12
+ # @param [Hash] options the options for this builder
13
+ # @option [Hash] :header_format - a hash of format properties to apply
14
+ # to the header row. Any properties specified will be merged with the
15
+ # default header styles. @see https://github.com/zdavatz/spreadsheet/blob/master/lib/spreadsheet/format.rb
16
+ # @option [Array] :i18n_scope - the I18n scope to use when looking
17
+ # up localized column headers.
18
+ # @param [Block] Any block given will evaluated against this instance of
19
+ # Builder. That means you can call any method on the builder from within
20
+ # that block.
21
+ # @example
22
+ # ActiveAdmin::Xls:Builder.new(Post, i18n: [:xls]) do
23
+ # delete_columns :id, :created_at, :updated_at
24
+ # column(:author_name) { |post| post.author.name }
25
+ # after_filter { |sheet|
26
+ #
27
+ # }
28
+ # end
29
+ # @see ActiveAdmin::Axlsx::DSL
30
+ def initialize(resource_class, options = {}, &block)
31
+ @skip_header = false
32
+ @columns = resource_columns(resource_class)
33
+ parse_options options
34
+ instance_eval(&block) if block_given?
35
+ end
36
+
37
+ # The default header style
38
+ # @return [Hash]
39
+ def header_format
40
+ @header_format ||= {}
41
+ end
42
+
43
+ alias header_style header_format
44
+
45
+ # This has can be used to override the default header style for your
46
+ # sheet. Any values you provide will be merged with the default styles.
47
+ # Precidence is given to your hash
48
+ # @see https://github.com/zdavatz/spreadsheet/blob/master/lib/spreadsheet/format.rb
49
+ # for more details on how to create and apply style.
50
+ def header_format=(format_hash)
51
+ @header_format = header_format.merge(format_hash)
52
+ end
53
+
54
+ alias header_style= header_format=
55
+
56
+ # Indicates that we do not want to serialize the column headers
57
+ def skip_header
58
+ @skip_header = true
59
+ end
60
+
61
+ # The scope to use when looking up column names to generate the
62
+ # report header
63
+ def i18n_scope
64
+ @i18n_scope ||= nil
65
+ end
66
+
67
+ # This is the I18n scope that will be used when looking up your
68
+ # colum names in the current I18n locale.
69
+ # If you set it to [:active_admin, :resources, :posts] the
70
+ # serializer will render the value at active_admin.resources.posts.title
71
+ # in the current translations
72
+ # @note If you do not set this, the column name will be titleized.
73
+ attr_writer :i18n_scope
74
+
75
+ # The stored block that will be executed after your report is generated.
76
+ def after_filter(&block)
77
+ @after_filter = block
78
+ end
79
+
80
+ # the stored block that will be executed before your report is generated.
81
+ def before_filter(&block)
82
+ @before_filter = block
83
+ end
84
+
85
+ # The columns this builder will be serializing
86
+ attr_reader :columns
87
+
88
+ # The collection we are serializing.
89
+ # @note This is only available after serialize has been called,
90
+ # and is reset on each subsequent call.
91
+ attr_reader :collection
92
+
93
+ # removes all columns from the builder. This is useful when you want to
94
+ # only render specific columns. To remove specific columns use
95
+ # ignore_column.
96
+ def clear_columns
97
+ @columns = []
98
+ end
99
+
100
+ # Clears the default columns array so you can whitelist only the columns
101
+ # you want to export
102
+ def whitelist
103
+ @columns = []
104
+ end
105
+
106
+ # Add a column
107
+ # @param [Symbol] name The name of the column.
108
+ # @param [Proc] block A block of code that is executed on the resource
109
+ # when generating row data for this column.
110
+ def column(name, &block)
111
+ @columns << Column.new(name, block)
112
+ end
113
+
114
+ # removes columns by name
115
+ # each column_name should be a symbol
116
+ def delete_columns(*column_names)
117
+ @columns.delete_if { |column| column_names.include?(column.name) }
118
+ end
119
+
120
+ # Serializes the collection provided
121
+ # @return [Spreadsheet::Workbook]
122
+ def serialize(collection, view_context)
123
+ @collection = collection
124
+ @view_context = view_context
125
+ apply_filter @before_filter
126
+ export_collection(collection)
127
+ apply_filter @after_filter
128
+ to_stream
129
+ end
130
+
131
+ # Xls column
132
+ class Column
133
+ def initialize(name, block = nil)
134
+ @name = name
135
+ @data = block || @name
136
+ end
137
+
138
+ attr_reader :name, :data
139
+
140
+ def localized_name(i18n_scope = nil)
141
+ return name.to_s.titleize unless i18n_scope
142
+ I18n.t name, scope: i18n_scope
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ def to_stream
149
+ stream = StringIO.new('')
150
+ book.write stream
151
+ clean_up
152
+ stream.string
153
+ end
154
+
155
+ def clean_up
156
+ @book = @sheet = nil
157
+ end
158
+
159
+ def export_collection(collection)
160
+ return if columns.none?
161
+ row_index = 0
162
+
163
+ unless @skip_header
164
+ header_row(collection)
165
+ row_index = 1
166
+ end
167
+
168
+ collection.each do |resource|
169
+ fill_row(sheet.row(row_index), resource_data(resource))
170
+ row_index += 1
171
+ end
172
+ end
173
+
174
+ # tranform column names into array of localized strings
175
+ # @return [Array]
176
+ def header_row(collection)
177
+ row = sheet.row(0)
178
+ apply_format_to_row(row, create_format(header_format))
179
+ fill_row(row, header_data_for(collection))
180
+ end
181
+
182
+ def header_data_for(collection)
183
+ resource = collection.first
184
+ columns.map do |column|
185
+ column.localized_name(i18n_scope) if in_scope(resource, column)
186
+ end.compact
187
+ end
188
+
189
+ def apply_filter(filter)
190
+ filter.call(sheet) if filter
191
+ end
192
+
193
+ def parse_options(options)
194
+ options.each do |key, value|
195
+ send("#{key}=", value) if respond_to?("#{key}=") && !value.nil?
196
+ end
197
+ end
198
+
199
+ def resource_data(resource)
200
+ columns.map do |column|
201
+ call_method_or_proc_on resource, column.data if in_scope(resource,
202
+ column)
203
+ end
204
+ end
205
+
206
+ def in_scope(resource, column)
207
+ return true unless column.name.is_a?(Symbol)
208
+ resource.respond_to?(column.name)
209
+ end
210
+
211
+ def sheet
212
+ @sheet ||= book.create_worksheet
213
+ end
214
+
215
+ def book
216
+ @book ||= ::Spreadsheet::Workbook.new
217
+ end
218
+
219
+ def resource_columns(resource)
220
+ [Column.new(:id)] + resource.content_columns.map do |column|
221
+ Column.new(column.name.to_sym)
222
+ end
223
+ end
224
+
225
+ def create_format(format_hash)
226
+ Spreadsheet::Format.new format_hash
227
+ end
228
+
229
+ def apply_format_to_row(row, format)
230
+ row.default_format = format if format
231
+ end
232
+
233
+ def fill_row(row, column)
234
+ case column
235
+ when Hash
236
+ column.each_value { |values| fill_row(row, values) }
237
+ when Array
238
+ column.each { |value| fill_row(row, value) }
239
+ else
240
+ # raise ArgumentError,
241
+ # "column #{column} has an invalid class (#{ column.class })"
242
+ row.push(column)
243
+ end
244
+ end
245
+
246
+ def method_missing(method_name, *arguments)
247
+ if @view_context.respond_to? method_name
248
+ @view_context.send method_name, *arguments
249
+ else
250
+ super
251
+ end
252
+ end
253
+
254
+ def respond_to_missing?(method_name, include_private = false)
255
+ @view_context.respond_to?(method_name) || super
256
+ end
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveAdmin
2
+ module Xls
3
+ # extends activeadmin dsl to include xls
4
+ module DSL
5
+ delegate(:after_filter,
6
+ :before_filter,
7
+ :column,
8
+ :delete_columns,
9
+ :header_format,
10
+ :header_style,
11
+ :i18n_scope,
12
+ :skip_header,
13
+ :whitelist,
14
+ to: :xls_builder,
15
+ prefix: :config)
16
+
17
+ def xls(options = {}, &block)
18
+ config.xls_builder = ActiveAdmin::Xls::Builder.new(
19
+ config.resource_class,
20
+ options,
21
+ &block
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveAdmin
2
+ module Xls
3
+ # extends activeadmin with xls downloads
4
+ class Engine < ::Rails::Engine
5
+ engine_name 'active_admin_xls'
6
+
7
+ initializer 'active_admin.xls', group: :all do
8
+ if Mime::Type.lookup_by_extension(:xls).nil?
9
+ Mime::Type.register 'application/vnd.ms-excel', :xls
10
+ end
11
+
12
+ ActiveAdmin::Views::PaginatedCollection.add_format :xls
13
+
14
+ ActiveAdmin::ResourceDSL.send :include, ActiveAdmin::Xls::DSL
15
+ ActiveAdmin::Resource.send :include, ActiveAdmin::Xls::ResourceExtension
16
+ ActiveAdmin::ResourceController.send :include, ActiveAdmin::Xls::ResourceControllerExtension
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,57 @@
1
+ module ActiveAdmin
2
+ module Xls
3
+ module ResourceControllerExtension
4
+ def self.included(base)
5
+ base.send :alias_method_chain, :per_page, :xls
6
+ base.send :alias_method_chain, :index, :xls
7
+ base.send :alias_method_chain, :rescue_active_admin_access_denied, :xls
8
+ base.send :respond_to, :xls
9
+ end
10
+
11
+ def index_with_xls
12
+ index_without_xls do |format|
13
+ yield format if block_given?
14
+
15
+ format.xls do
16
+ xls = active_admin_config.xls_builder.serialize(collection,
17
+ view_context)
18
+ send_data(xls,
19
+ filename: xls_filename,
20
+ type: Mime::Type.lookup_by_extension(:xls))
21
+ end
22
+ end
23
+ end
24
+
25
+ def rescue_active_admin_access_denied_with_xls(exception)
26
+ if request.format == Mime::Type.lookup_by_extension(:xls)
27
+ respond_to do |format|
28
+ format.xls do
29
+ flash[:error] = "#{exception.message} Review download_links in initializers/active_admin.rb"
30
+ redirect_backwards_or_to_root
31
+ end
32
+ end
33
+ else
34
+ rescue_active_admin_access_denied_without_xls(exception)
35
+ end
36
+ end
37
+
38
+ # patch per_page to use the CSV record max for pagination
39
+ # when the format is xls
40
+ def per_page_with_xls
41
+ if request.format == Mime::Type.lookup_by_extension(:xls)
42
+ return max_per_page if respond_to?(:max_per_page, true)
43
+ active_admin_config.max_per_page
44
+ end
45
+
46
+ per_page_without_xls
47
+ end
48
+
49
+ # Returns a filename for the xls file using the collection_name
50
+ # and current date such as 'my-articles-2011-06-24.xls'.
51
+ def xls_filename
52
+ timestamp = Time.now.strftime('%Y-%m-%d')
53
+ "#{resource_collection_name.to_s.tr('_', '-')}-#{timestamp}.xls"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveAdmin
2
+ module Xls
3
+ module ResourceExtension
4
+ def xls_builder=(builder)
5
+ @xls_builder = builder
6
+ end
7
+
8
+ def xls_builder
9
+ @xls_builder ||= ActiveAdmin::Xls::Builder.new(resource_class)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveAdmin
2
+ module Xls
3
+ VERSION = '1.0.4'.freeze
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ require 'rake'
2
+ require 'active_admin'
3
+
4
+ require 'active_admin/xls/version'
5
+ require 'active_admin/xls/builder'
6
+ require 'active_admin/xls/dsl'
7
+ require 'active_admin/xls/resource_extension'
8
+ require 'active_admin/xls/resource_controller_extension'
9
+ require 'active_admin/xls/engine'