activeadmin-axlsx 1.0.0a

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,23 @@
1
+ require 'rake'
2
+ require File.expand_path('../lib/active_admin/axlsx/version', __FILE__)
3
+ Gem::Specification.new do |s|
4
+ s.name = 'activeadmin-axlsx'
5
+ s.version = ActiveAdmin::Axlsx::VERSION
6
+ s.author = "Randy Morgan"
7
+ s.email = 'digital.ipseity@gmail.com'
8
+ s.homepage = 'https://github.com/randym/activeadmin-axlsx'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.date = Time.now.strftime('%Y-%m-%d')
11
+ s.summary = "Adds excel downloads for resources within the Active Admin framework via Axlsx."
12
+ s.description = <<-eof
13
+ This gem uses axlsx to provide excel/xlsx downloads for resources in Active Admin. Often, users are happier with excel, so why not give it to them instead of CSV?
14
+ eof
15
+ s.files = `git ls-files`.split("\n").sort
16
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
17
+ s.test_files = Dir.glob("{spec/**/*}")
18
+ s.add_runtime_dependency 'activeadmin', '>=0.5.0'
19
+ s.add_runtime_dependency 'axlsx'
20
+
21
+ s.required_ruby_version = '>= 1.9.2'
22
+ s.require_path = 'lib'
23
+ end
@@ -0,0 +1,34 @@
1
+ require 'active_admin'
2
+ require 'active_admin/axlsx/autoload_extension'
3
+ require 'active_admin/axlsx/build_download_format_links'
4
+ require 'active_admin/axlsx/version'
5
+ require 'active_admin/axlsx/builder'
6
+ require 'active_admin/axlsx/dsl'
7
+ require 'active_admin/axlsx/resource_extension'
8
+ require 'active_admin/axlsx/resource_controller_extension'
9
+ class Railtie < ::Rails::Railtie
10
+ config.after_initialize do
11
+ begin
12
+ if Mime::Type.lookup_by_extension(:xlsx).nil?
13
+ # The mime type to be used in respond_to |format| style web-services in rails
14
+ Mime::Type.register "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", :xlsx
15
+ end
16
+ rescue NameError
17
+ puts "Mime module not defined. Skipping registration of xlsx"
18
+ end
19
+ ActiveAdmin.send :include, ActiveAdmin::Axlsx::AutoloadExtension
20
+ ActiveAdmin::ResourceDSL.send :include, ActiveAdmin::Axlsx::DSL
21
+ ActiveAdmin::Resource.send :include, ActiveAdmin::Axlsx::ResourceExtension
22
+ ActiveAdmin::ResourceController.send :include, ActiveAdmin::Axlsx::ResourceControllerExtension
23
+ # TODO remove < 0.5.1 block once active admin has released.
24
+ # Pull request to fix build download format links has already been merged into active admin.
25
+ if ActiveAdmin::VERSION < '0.5.1'
26
+ ActiveAdmin::Views::PaginatedCollection.send :include, ActiveAdmin::Axlsx::BuildDownloadFormatLinks
27
+ ActiveAdmin::Views::Pages::Index.send :include, ActiveAdmin::Axlsx::BuildDownloadFormatLinks
28
+ else
29
+ ActiveAdmin::Views::PaginatedCollection.add_format :xlsx
30
+ end
31
+ end
32
+ end
33
+
34
+
@@ -0,0 +1,7 @@
1
+ module ActiveAdmin
2
+ module Axlsx
3
+ module AutoloadExtension
4
+ autoload :XlsxBuilder, 'active_admin/xlsx_builder'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveAdmin
2
+ module Axlsx
3
+ module BuildDownloadFormatLinks
4
+ def self.included(base)
5
+ base.send :alias_method_chain, :build_download_format_links, :xlsx
6
+ end
7
+
8
+ def build_download_format_links_with_xlsx(formats = [:csv, :xml, :json, :xlsx])
9
+ build_download_format_links_without_xlsx(formats)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,116 @@
1
+ require 'axlsx'
2
+
3
+ module ActiveAdmin
4
+ module Axlsx
5
+ # XlsxBuilder extends CSVBuilder adding in xlsx specific options
6
+ #
7
+ # Usage example
8
+ #
9
+ # xlsx_builder = XlsxBuilder.new
10
+ # xlsx_builder.column :id
11
+ # xlsx_builder.column('Name') { |resource| resource.full_name }
12
+ #
13
+ # xlsx_builder = XlsxBuilder.new :shared_strings => true
14
+ # xlsx_builder.column :id
15
+ #
16
+ # xlsx_builder = XlsxBuiler.new :header_style => { :bg_color => '00', :fg_color => 'FF', :sz => 14, :alignment => { :horizontal => :center } }
17
+ # xlsx_buider.i18n_scope [:active_record, :models, :posts]
18
+ class Builder < ActiveAdmin::CSVBuilder
19
+
20
+ include MethodOrProcHelper
21
+
22
+ @@default_header_style = { :bg_color => '00', :fg_color => 'FF', :sz => 12, :alignment => { :horizontal => :center } }
23
+
24
+ # Return a default XlsxBuilder for a resource
25
+ # The XlsxBuilder columns will be id, follwed by this resource's content columns
26
+ # The default header_style is applied.
27
+ def self.default_for_resource(resource)
28
+ xlsx_builder = super
29
+ xlsx_builder.header_style = @@default_header_style
30
+ xlsx_builder
31
+ end
32
+
33
+ attr_reader :columns
34
+
35
+ # when this is set to true the xlsx file will be generated with
36
+ # shared strings and will inter-operate with Numbers for Mac
37
+ # This is true by default, but you can set it to false to minimize the generation time.
38
+ attr_accessor :shared_strings
39
+
40
+ # This has can be used to override the default header style for your
41
+ # sheet.
42
+ # @see https://github.com/randym/axlsx for more details on how to
43
+ # create and apply style.
44
+ # @return [Hash]
45
+ attr_accessor :header_style
46
+
47
+ # This is the I18n scope that will be used when looking up your
48
+ # colum names in the current I18n locale.
49
+ # If you set it to [:active_admin, :resources, :posts] the
50
+ # serializer will render the value at active_admin.resources.posts.title in the
51
+ # current translations
52
+ # @note If you do not set this, the column name will be titleized.
53
+ attr_accessor :i18n_scope
54
+
55
+ # @param [Hash] options the options for this builder
56
+ # @option [Hash] :header_style - a hash of style properties to apply
57
+ # to the header row
58
+ # @option [Array] :i18n_scope - the I18n scope to use when looking
59
+ # up localized column headers.
60
+ # @option [Boolean] :shared_strings - Tells the serializer to use
61
+ # shared strings or not when parsing out the package.
62
+ # @note shared strings are an optional part of the ECMA-376 spec,and
63
+ # are only required when you need to support Numbers.
64
+ def initialize(options={}, &block)
65
+ super
66
+ @header_style = options.delete(:header_style) || @@default_header_style
67
+ @i18n_scope = options.delete(:i18n_scope)
68
+ @shared_strings = options.delete(:shared_strings) || true
69
+ instance_eval &block if block_given?
70
+ end
71
+
72
+ # the Axlsx::Package
73
+ def package
74
+ @package ||= ::Axlsx::Package.new(:use_shared_strings => shared_strings)
75
+ end
76
+
77
+ # Serializes the collection provided
78
+ # @return [Axlsx::Package]
79
+ def serialize(collection)
80
+ header_style_id = package.workbook.styles.add_style header_style
81
+ package.workbook.add_worksheet do |sheet|
82
+ sheet.add_row header_row, :style => header_style_id
83
+ collection.each do |resource|
84
+ sheet.add_row columns.map { |column| call_method_or_proc_on resource, column.data }
85
+ end
86
+ end
87
+ package
88
+ end
89
+
90
+ # tranform column names into array of localized strings
91
+ # @return [Array]
92
+ def header_row
93
+ columns.map { |column| column.localized_name(i18n_scope) }
94
+ end
95
+
96
+ # Add a column
97
+ def column(name, &block)
98
+ @columns << Column.new(name, block)
99
+ end
100
+
101
+ class Column
102
+ attr_reader :name, :data
103
+
104
+ def initialize(name, block = nil)
105
+ @name = name.to_sym
106
+ @data = block || @name
107
+ end
108
+
109
+ def localized_name(i18n_scope = nil)
110
+ return name.to_s.titleize unless i18n_scope
111
+ I18n.t name, i18n_scope
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveAdmin
2
+ module Axlsx
3
+ module DSL
4
+ # Configure the xlsx format
5
+ #
6
+ # For example:
7
+ #
8
+ # xlsx do
9
+ # i18n_scope = [:active_admin, :resources, :post]
10
+ # column :name
11
+ # column(:author) { |post| post.author.full_name }
12
+ # end
13
+ #
14
+ # xlsx :header_style => { :bg_color => "00", :fg_color => "FF" } do
15
+ # column :name
16
+ # end
17
+ def xlsx(options={}, &block)
18
+ config.xlsx_builder = XlsxBuilder.new(options, &block)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,33 @@
1
+ module ActiveAdmin
2
+ module Axlsx
3
+ module ResourceControllerExtension
4
+ def self.included(base)
5
+ base.send :alias_method_chain, :per_page, :xlsx
6
+ base.send :alias_method_chain, :index, :xlsx
7
+ base.send :respond_to, :xlsx
8
+ end
9
+
10
+ def index_with_xlsx(options={}, &block)
11
+ index_without_xlsx(options) do |format|
12
+ format.xlsx do
13
+ xlsx = active_admin_config.xlsx_builder.serialize(collection)
14
+ send_data xlsx.to_stream.read, :filename => "#{xlsx_filename}", :type => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
15
+ end
16
+ end
17
+ end
18
+
19
+ def per_page_with_xlsx
20
+ if request.format == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
21
+ return max_csv_records
22
+ end
23
+ per_page_without_xlsx
24
+ end
25
+
26
+ # Returns a filename for the xlsx file using the collection_name
27
+ # and current date such as 'my-articles-2011-06-24.xlsx'.
28
+ def xlsx_filename
29
+ "#{resource_collection_name.to_s.gsub('_', '-')}-#{Time.now.strftime("%Y-%m-%d")}.xlsx"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveAdmin
2
+ module Axlsx
3
+ module ResourceExtension
4
+
5
+ def xlsx_builder=(builder)
6
+ @xlsx_builder = builder
7
+ end
8
+
9
+ def xlsx_builder
10
+ @xlsx_builder ||= default_xlsx_builder
11
+ end
12
+
13
+ private
14
+
15
+ def default_xlsx_builder
16
+ @default_xlsx_builder ||= Axlsx::Builder.default_for_resource(resource_class)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveAdmin
2
+ module Axlsx
3
+ VERSION = '1.0.0a'
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'active_admin/axlsx.rb'
Binary file
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ module ActiveAdmin
3
+ module Axlsx
4
+ describe Builder do
5
+ context "with shared strings" do
6
+ let(:builder) do
7
+ Builder.new :shared_strings => true
8
+ end
9
+
10
+ it "should be set to use shared strings" do
11
+ builder.shared_strings.should be_true
12
+ end
13
+ end
14
+
15
+ context "with i18n_scope" do
16
+ let(:builder) do
17
+ Builder.new :i18n_scope => [:active_admin, :resource, :category]
18
+ end
19
+
20
+ it "should have the defined i18n_scope set" do
21
+ builder.i18n_scope.should == [:active_admin, :resource, :category]
22
+ end
23
+ end
24
+
25
+ context "with a customized header style" do
26
+ let (:header_style) do
27
+ { :bg_color => '00', :fg_color => 'FF', :sz => 12, :alignment => { :horizontal=> :center } }
28
+ end
29
+
30
+ let (:builder) do
31
+ Builder.new :header_style => header_style
32
+ end
33
+
34
+ it "should be set up with a header style" do
35
+ header_style.each { |key , value| builder.header_style[key].should == value }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ include ActiveAdmin
3
+
4
+ module ActiveAdmin
5
+ module Axlsx
6
+ describe Resource do
7
+ before { load_defaults! }
8
+
9
+ let(:application){ ActiveAdmin::Application.new }
10
+ let(:namespace){ Namespace.new(application, :admin) }
11
+
12
+ def config(options = {})
13
+ @config ||= Resource.new(namespace, Category, options)
14
+ end
15
+
16
+ describe "#xlsx_builder" do
17
+ context "when no xlsx_builder set" do
18
+ it "should return a default xlsx_builder with id and content columns" do
19
+ config.xlsx_builder.columns.size.should == Category.content_columns.size + 1
20
+ end
21
+ end
22
+
23
+ context "when xslx_builder set" do
24
+ it "should return the xlsx_builder we set" do
25
+ xlsx_builder = Builder.new
26
+ config.xlsx_builder = xlsx_builder
27
+ config.xlsx_builder.should == xlsx_builder
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ require 'rails'
2
+ require 'activeadmin'
3
+
4
+ ENV['RAILS_ROOT'] = File.expand_path("../rails/rails-#{Rails::VERSION::STRING}", __FILE__)
5
+
6
+ unless File.exists?(ENV['RAILS_ROOT'])
7
+ puts "Please run bundle exec rake setup before running the specs."
8
+ exit
9
+ end
10
+
11
+ def load_defaults!
12
+ ActiveAdmin.unload!
13
+ ActiveAdmin.load!
14
+ ActiveAdmin.register(Category)
15
+ ActiveAdmin.register(User)
16
+ ActiveAdmin.register(Post){ belongs_to :user, :optional => true }
17
+ end
18
+
19
+ ENV['RAILS_ENV'] = 'test'
20
+ ActiveAdmin.application.load_paths = [ENV['RAILS_ROOT'] + "/app/admin"]
21
+ require ENV['RAILS_ROOT'] + '/config/environment'
22
+ require 'activeadmin-axlsx'
@@ -0,0 +1,73 @@
1
+ # Rails template to build the sample app for specs
2
+
3
+ # Create a cucumber database and environment
4
+ copy_file File.expand_path('../templates/cucumber.rb', __FILE__), "config/environments/cucumber.rb"
5
+ copy_file File.expand_path('../templates/cucumber_with_reloading.rb', __FILE__), "config/environments/cucumber_with_reloading.rb"
6
+
7
+ gsub_file 'config/database.yml', /^test:.*\n/, "test: &test\n"
8
+ gsub_file 'config/database.yml', /\z/, "\ncucumber:\n <<: *test\n database: db/cucumber.sqlite3"
9
+ gsub_file 'config/database.yml', /\z/, "\ncucumber_with_reloading:\n <<: *test\n database: db/cucumber.sqlite3"
10
+
11
+ # Generate some test models
12
+ generate :model, "post title:string body:text published_at:datetime author_id:integer category_id:integer"
13
+ inject_into_file 'app/models/post.rb', " belongs_to :author, :class_name => 'User'\n belongs_to :category\n accepts_nested_attributes_for :author\n", :after => "class Post < ActiveRecord::Base\n"
14
+ # Rails 3.2.3 model generator declare attr_accessible
15
+ inject_into_file 'app/models/post.rb', " attr_accessible :author\n", :before => "end" if Rails::VERSION::STRING >= '3.2.3'
16
+ generate :model, "user type:string first_name:string last_name:string username:string age:integer"
17
+ inject_into_file 'app/models/user.rb', " has_many :posts, :foreign_key => 'author_id'\n", :after => "class User < ActiveRecord::Base\n"
18
+ generate :model, "publisher --migration=false --parent=User"
19
+ generate :model, 'category name:string description:text'
20
+ inject_into_file 'app/models/category.rb', " has_many :posts\n accepts_nested_attributes_for :posts\n", :after => "class Category < ActiveRecord::Base\n"
21
+ generate :model, 'store name:string'
22
+
23
+ # Generate a model with string ids
24
+ generate :model, "tag name:string"
25
+ gsub_file(Dir['db/migrate/*_create_tags.rb'][0], /\:tags\sdo\s.*/, ":tags, :id => false, :primary_key => :id do |t|\n\t\t\tt.string :id\n" )
26
+ id_model_setup = <<-EOF
27
+ self.primary_key = :id
28
+ before_create :set_id
29
+
30
+ private
31
+ def set_id
32
+ self.id = 8.times.inject("") { |s,e| s << (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr }
33
+ end
34
+ EOF
35
+ inject_into_file 'app/models/tag.rb', id_model_setup, :after => "class Tag < ActiveRecord::Base\n"
36
+
37
+ if Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR == 1 #Rails 3.1 Gotcha
38
+ gsub_file 'app/models/tag.rb', /self\.primary_key.*$/, "define_attr_method :primary_key, :id"
39
+ end
40
+
41
+ # Configure default_url_options in test environment
42
+ inject_into_file "config/environments/test.rb", " config.action_mailer.default_url_options = { :host => 'example.com' }\n", :after => "config.cache_classes = true\n"
43
+
44
+ # Add our local Active Admin to the load path
45
+ inject_into_file "config/environment.rb", "\n$LOAD_PATH.unshift('#{File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib'))}')\nrequire \"active_admin\"\n", :after => "require File.expand_path('../application', __FILE__)"
46
+
47
+ # Add some translations
48
+ append_file "config/locales/en.yml", File.read(File.expand_path('../templates/en.yml', __FILE__))
49
+
50
+ # Add predefined admin resources
51
+ directory File.expand_path('../templates/admin', __FILE__), "app/admin"
52
+
53
+ run "rm Gemfile"
54
+ run "rm -r test"
55
+ run "rm -r spec"
56
+
57
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
58
+
59
+ # we need this routing path, named "logout_path", for testing
60
+ route <<-EOS
61
+ devise_scope :user do
62
+ match '/admin/logout' => 'active_admin/devise/sessions#destroy', :as => :logout
63
+ end
64
+ EOS
65
+
66
+ generate :'active_admin:install'
67
+
68
+ # Setup a root path for devise
69
+ route "root :to => 'admin/dashboard#index'"
70
+
71
+ rake "db:migrate"
72
+ rake "db:test:prepare"
73
+ run "/usr/bin/env RAILS_ENV=cucumber rake db:migrate"