activeadmin-xls 1.0.4

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 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'