activeadmin-xls 1.0.5 → 2.0.3
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 +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +13 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +54 -4
- data/Gemfile +1 -1
- data/README.md +71 -31
- data/Rakefile +23 -3
- data/activeadmin-xls.gemspec +3 -3
- data/gemfiles/rails_42.gemfile +4 -2
- data/gemfiles/rails_52.gemfile +24 -0
- data/gemfiles/rails_60.gemfile +25 -0
- data/lib/active_admin/xls/builder.rb +174 -47
- data/lib/active_admin/xls/dsl.rb +49 -1
- data/lib/active_admin/xls/engine.rb +5 -2
- data/lib/active_admin/xls/resource_controller_extension.rb +24 -29
- data/lib/active_admin/xls/resource_extension.rb +15 -0
- data/lib/active_admin/xls/version.rb +2 -1
- metadata +11 -28
- data/.yardops +0 -1
- data/gemfiles/rails_32.gemfile +0 -30
- data/spec/spec_helper.rb +0 -38
- data/spec/support/rails_template.rb +0 -102
- data/spec/support/rails_template_with_data.rb +0 -62
- data/spec/support/templates/admin/stores.rb +0 -1
- data/spec/support/templates/cucumber.rb +0 -24
- data/spec/support/templates/cucumber_with_reloading.rb +0 -5
- data/spec/support/templates/en.yml +0 -15
- data/spec/xls/unit/build_download_format_links_spec.rb +0 -52
- data/spec/xls/unit/builder_spec.rb +0 -194
- data/spec/xls/unit/dsl_spec.rb +0 -59
- data/spec/xls/unit/resource_controller_spec.rb +0 -52
- data/spec/xls/unit/resource_spec.rb +0 -26
@@ -1,62 +0,0 @@
|
|
1
|
-
# Use the default
|
2
|
-
apply File.expand_path('../rails_template.rb', __FILE__)
|
3
|
-
|
4
|
-
# Register Active Admin controllers
|
5
|
-
%w[Post User Category].each do |type|
|
6
|
-
generate :'active_admin:resource', type
|
7
|
-
end
|
8
|
-
|
9
|
-
scopes = <<-SCOPES
|
10
|
-
scope :all, default: true
|
11
|
-
|
12
|
-
scope :drafts do |posts|
|
13
|
-
posts.where(["published_at IS NULL"])
|
14
|
-
end
|
15
|
-
|
16
|
-
scope :scheduled do |posts|
|
17
|
-
posts.where(["posts.published_at IS NOT NULL AND posts.published_at > ?", Time.now.utc])
|
18
|
-
end
|
19
|
-
|
20
|
-
scope :published do |posts|
|
21
|
-
posts.where(["posts.published_at IS NOT NULL AND posts.published_at < ?", Time.now.utc])
|
22
|
-
end
|
23
|
-
|
24
|
-
scope :my_posts do |posts|
|
25
|
-
posts.where(:author_id => current_admin_user.id)
|
26
|
-
end
|
27
|
-
SCOPES
|
28
|
-
|
29
|
-
inject_into_file 'app/admin/post.rb',
|
30
|
-
scopes,
|
31
|
-
after: "ActiveAdmin.register Post do\n"
|
32
|
-
|
33
|
-
# Setup some default data
|
34
|
-
append_file 'db/seeds.rb', <<-SEEDS
|
35
|
-
|
36
|
-
users = ['Jimi Hendrix', 'Jimmy Page', 'Yngwie Malmsteen', 'Eric Clapton', 'Kirk Hammett'].collect do |name|
|
37
|
-
first, last = name.split(" ")
|
38
|
-
User.create! first_name: first,
|
39
|
-
last_name: last,
|
40
|
-
username: [first,last].join('-').downcase,
|
41
|
-
age: rand(80)
|
42
|
-
end
|
43
|
-
|
44
|
-
categories = ['Rock', 'Pop Rock', 'Alt-Country', 'Blues', 'Dub-Step'].collect do |name|
|
45
|
-
Category.create! name: name
|
46
|
-
end
|
47
|
-
|
48
|
-
published_at_values = [Time.now.utc - 5.days, Time.now.utc - 1.day, nil, Time.now.utc + 3.days]
|
49
|
-
|
50
|
-
1_000.times do |i|
|
51
|
-
user = users[i % users.size]
|
52
|
-
cat = categories[i % categories.size]
|
53
|
-
published_at = published_at_values[i % published_at_values.size]
|
54
|
-
Post.create title: "Blog Post \#{i}",
|
55
|
-
body: "Blog post \#{i} is written by \#{user.username} about \#{cat.name}",
|
56
|
-
category_id: cat.id,
|
57
|
-
published_at: published_at,
|
58
|
-
author: user
|
59
|
-
end
|
60
|
-
SEEDS
|
61
|
-
|
62
|
-
rake 'db:seed'
|
@@ -1 +0,0 @@
|
|
1
|
-
ActiveAdmin.register Store
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require File.expand_path('config/environments/test', Rails.root)
|
2
|
-
|
3
|
-
# rails/railties/lib/rails/test_help.rb aborts if the environment is not 'test'. (Rails 3.0.0.beta3)
|
4
|
-
# We can't run Cucumber/RSpec/Test_Unit tests in different environments then.
|
5
|
-
#
|
6
|
-
# For now, I patch StringInquirer so that Rails.env.test? returns true when Rails.env is 'test' or 'cucumber'
|
7
|
-
#
|
8
|
-
# https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4458-rails-should-allow-test-to-run-in-cucumber-environment
|
9
|
-
module ActiveSupport
|
10
|
-
class StringInquirer < String
|
11
|
-
def method_missing(method_name, *arguments)
|
12
|
-
if method_name.to_s[-1,1] == "?"
|
13
|
-
test_string = method_name.to_s[0..-2]
|
14
|
-
if test_string == 'test'
|
15
|
-
self == 'test' or self == 'cucumber'
|
16
|
-
else
|
17
|
-
self == test_string
|
18
|
-
end
|
19
|
-
else
|
20
|
-
super
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# Sample translations used to test ActiveAdmin's I18n integration.
|
2
|
-
xls:
|
3
|
-
post:
|
4
|
-
id: ID
|
5
|
-
title: Title
|
6
|
-
body: Content
|
7
|
-
published_at: Published On
|
8
|
-
author: Publisher
|
9
|
-
created_at: Created
|
10
|
-
updated_at: Updated
|
11
|
-
activerecord:
|
12
|
-
models:
|
13
|
-
store:
|
14
|
-
one: Bookstore
|
15
|
-
other: Bookstores
|
@@ -1,52 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
describe ActiveAdmin::Views::PaginatedCollection do
|
3
|
-
def arbre(assigns = {}, helpers = mock_action_view, &block)
|
4
|
-
Arbre::Context.new(assigns, helpers, &block)
|
5
|
-
end
|
6
|
-
|
7
|
-
def render_arbre_component(assigns = {}, helpers = mock_action_view, &block)
|
8
|
-
arbre(assigns, helpers, &block).children.first
|
9
|
-
end
|
10
|
-
|
11
|
-
# Returns a fake action view instance to use with our renderers
|
12
|
-
def mock_action_view(assigns = {})
|
13
|
-
controller = ActionView::TestCase::TestController.new
|
14
|
-
ActionView::Base.send :include, ActionView::Helpers
|
15
|
-
ActionView::Base.send :include, ActiveAdmin::ViewHelpers
|
16
|
-
ActionView::Base.send :include, Rails.application.routes.url_helpers
|
17
|
-
ActionView::Base.new(ActionController::Base.view_paths, assigns, controller)
|
18
|
-
end
|
19
|
-
|
20
|
-
let(:view) do
|
21
|
-
view = mock_action_view
|
22
|
-
allow(view.request).to receive(:query_parameters) { { page: '1' } }
|
23
|
-
allow(view.request).to receive(:path_parameters) do
|
24
|
-
{ controller: 'admin/posts', action: 'index' }
|
25
|
-
end
|
26
|
-
view
|
27
|
-
end
|
28
|
-
|
29
|
-
# Helper to render paginated collections within an arbre context
|
30
|
-
def paginated_collection(*args)
|
31
|
-
render_arbre_component({ paginated_collection_args: args }, view) do
|
32
|
-
paginated_collection(*paginated_collection_args)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
let(:collection) do
|
37
|
-
posts = [Post.new(title: 'First Post')]
|
38
|
-
Kaminari.paginate_array(posts).page(1).per(5)
|
39
|
-
end
|
40
|
-
|
41
|
-
let(:pagination) { paginated_collection(collection) }
|
42
|
-
|
43
|
-
before do
|
44
|
-
allow(collection).to receive(:except) { collection } unless collection.respond_to? :except
|
45
|
-
allow(collection).to receive(:group_values) { [] } unless collection.respond_to? :group_values
|
46
|
-
allow(collection).to receive(:reorder) { collection }
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'renders the xls download link' do
|
50
|
-
expect(pagination.children.last.content).to match(/XLS/)
|
51
|
-
end
|
52
|
-
end
|
@@ -1,194 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module ActiveAdmin
|
4
|
-
module Xls
|
5
|
-
describe Builder do
|
6
|
-
let(:builder) { Builder.new(Post) }
|
7
|
-
let(:content_columns) { Post.content_columns }
|
8
|
-
|
9
|
-
context 'the default builder' do
|
10
|
-
it 'has no header style' do
|
11
|
-
expect(builder.header_style).to eq({})
|
12
|
-
end
|
13
|
-
it 'has no i18n scope' do
|
14
|
-
expect(builder.i18n_scope).to be_nil
|
15
|
-
end
|
16
|
-
it 'has default columns' do
|
17
|
-
expect(builder.columns.size).to eq(content_columns.size + 1)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'customizing a builder' do
|
22
|
-
it 'deletes columns we tell it we dont want' do
|
23
|
-
builder.delete_columns :id, :body
|
24
|
-
expect(builder.columns.size).to eq(content_columns.size - 1)
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'lets us say we dont want the header' do
|
28
|
-
builder.skip_header
|
29
|
-
expect(builder.instance_values['skip_header']).to be_truthy
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'lets us add custom columns' do
|
33
|
-
builder.column(:hoge)
|
34
|
-
expect(builder.columns.size).to eq(content_columns.size + 2)
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'lets us clear all columns' do
|
38
|
-
builder.clear_columns
|
39
|
-
expect(builder.columns.size).to eq(0)
|
40
|
-
end
|
41
|
-
|
42
|
-
context 'Using Procs for delayed content generation' do
|
43
|
-
let(:post) { Post.new(title: 'Hot Dawg') }
|
44
|
-
|
45
|
-
before do
|
46
|
-
builder.column(:hoge) do |resource|
|
47
|
-
"#{resource.title} - with cheese"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'stores the block when defining a column for later execution.' do
|
52
|
-
expect(builder.columns.last.data).to be_a(Proc)
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'evaluates custom column blocks' do
|
56
|
-
expect(builder.columns.last.data.call(post)).to eq('Hot Dawg - with cheese')
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
context 'sheet generation without headers' do
|
62
|
-
let!(:users) { [User.new(first_name: 'bob', last_name: 'nancy')] }
|
63
|
-
|
64
|
-
let!(:posts) { [Post.new(title: 'bob', body: 'is a swell guy', author: users.first)] }
|
65
|
-
|
66
|
-
let!(:builder) do
|
67
|
-
Builder.new(Post, header_format: { weight: :bold }, i18n_scope: %i[xls post]) do
|
68
|
-
skip_header
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
before do
|
73
|
-
# disable clean up so we can get the book.
|
74
|
-
allow(builder).to receive(:clean_up) { false }
|
75
|
-
# @book = Spreadsheet.open(builder.serialize(posts))
|
76
|
-
builder.serialize(posts)
|
77
|
-
@book = builder.send(:book)
|
78
|
-
@collection = builder.collection
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'does not serialize the header' do
|
82
|
-
expect(@book.worksheets.first[0, 0]).not_to eq('Title')
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
context 'whitelisted sheet generation' do
|
87
|
-
let!(:users) { [User.new(first_name: 'bob', last_name: 'nancy')] }
|
88
|
-
|
89
|
-
let!(:posts) do
|
90
|
-
[Post.new(title: 'bob', body: 'is a swell guy', author: users.first)]
|
91
|
-
end
|
92
|
-
|
93
|
-
let!(:builder) do
|
94
|
-
Builder.new(Post, header_style: {}, i18n_scope: %i[xls post]) do
|
95
|
-
skip_header
|
96
|
-
whitelist
|
97
|
-
column :title
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
before do
|
102
|
-
allow(User).to receive(:all) { users }
|
103
|
-
allow(Post).to receive(:all) { posts }
|
104
|
-
# disable clean up so we can get the book.
|
105
|
-
allow(builder).to receive(:clean_up) { false }
|
106
|
-
builder.serialize(Post.all)
|
107
|
-
@book = builder.send(:book)
|
108
|
-
@collection = builder.collection
|
109
|
-
end
|
110
|
-
|
111
|
-
it 'does not serialize the header' do
|
112
|
-
sheet = @book.worksheets.first
|
113
|
-
expect(sheet.column_count).to eq(1)
|
114
|
-
expect(sheet[0, 0]).to eq(@collection.first.title)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
context 'Sheet generation with a highly customized configuration.' do
|
119
|
-
let!(:builder) do
|
120
|
-
Builder.new(Post, header_style: { size: 10, color: 'red' }, i18n_scope: %i[xls post]) do
|
121
|
-
delete_columns :id, :created_at, :updated_at
|
122
|
-
column(:author) do |resource|
|
123
|
-
"#{resource.author.first_name} #{resource.author.last_name}"
|
124
|
-
end
|
125
|
-
after_filter do |sheet|
|
126
|
-
row_number = sheet.dimensions[1]
|
127
|
-
sheet.update_row(row_number)
|
128
|
-
row_number += 1
|
129
|
-
sheet.update_row(row_number, 'Author Name', 'Number of Posts')
|
130
|
-
users = collection.map(&:author).uniq(&:id)
|
131
|
-
users.each do |user|
|
132
|
-
row_number += 1
|
133
|
-
sheet.update_row(row_number,
|
134
|
-
"#{user.first_name} #{user.last_name}",
|
135
|
-
user.posts.size)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
before_filter do |sheet|
|
139
|
-
users = collection.map(&:author)
|
140
|
-
users.each do |user|
|
141
|
-
user.first_name = 'Set In Proc' if user.first_name == 'bob'
|
142
|
-
end
|
143
|
-
row_number = sheet.dimensions[1]
|
144
|
-
sheet.update_row(row_number, 'Created', Time.zone.now)
|
145
|
-
row_number += 1
|
146
|
-
sheet.update_row(row_number, '')
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
before do
|
152
|
-
Post.all.each(&:destroy)
|
153
|
-
User.all.each(&:destroy)
|
154
|
-
@user = User.create!(first_name: 'bob', last_name: 'nancy')
|
155
|
-
@post = Post.create!(title: 'bob',
|
156
|
-
body: 'is a swell guy',
|
157
|
-
author: @user)
|
158
|
-
# disable clean up so we can get the book.
|
159
|
-
allow(builder).to receive(:clean_up) { false }
|
160
|
-
builder.serialize(Post.all)
|
161
|
-
@book = builder.send(:book)
|
162
|
-
@collection = builder.collection
|
163
|
-
end
|
164
|
-
|
165
|
-
it 'provides the collection object' do
|
166
|
-
expect(@collection.count).to eq(Post.all.count)
|
167
|
-
end
|
168
|
-
|
169
|
-
it 'merges our customizations with the default header style' do
|
170
|
-
expect(builder.header_style[:size]).to eq(10)
|
171
|
-
expect(builder.header_style[:color]).to eq('red')
|
172
|
-
# expect(builder.header_style[:pattern_bg_color]).to eq('00')
|
173
|
-
end
|
174
|
-
|
175
|
-
it 'uses the specified i18n_scope' do
|
176
|
-
expect(builder.i18n_scope).to eq(%i[xls post])
|
177
|
-
end
|
178
|
-
|
179
|
-
it 'translates the header row based on our i18n scope' do
|
180
|
-
header_row = @book.worksheets.first.row(2)
|
181
|
-
expect(header_row).to eq(['Title', 'Content', 'Published On', 'Publisher'])
|
182
|
-
end
|
183
|
-
|
184
|
-
it 'processes the before filter' do
|
185
|
-
expect(@book.worksheets.first.cell(0, 0)).to eq('Created')
|
186
|
-
end
|
187
|
-
|
188
|
-
it 'lets us work against the collection in the before filter' do
|
189
|
-
expect(@book.worksheets.first.last_row[0]).to eq('Set In Proc nancy')
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
data/spec/xls/unit/dsl_spec.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module ActiveAdmin
|
4
|
-
module Xls
|
5
|
-
describe ::ActiveAdmin::ResourceDSL do
|
6
|
-
context 'in a registration block' do
|
7
|
-
let(:builder) do
|
8
|
-
config = ActiveAdmin.register(Post) do
|
9
|
-
xls(i18n_scope: [:rspec], header_style: { size: 20 }) do
|
10
|
-
delete_columns :id, :created_at
|
11
|
-
column(:author) { |post| post.author.first_name }
|
12
|
-
before_filter do |sheet|
|
13
|
-
row_number = sheet.dimensions[1]
|
14
|
-
sheet.update_row(row_number, 'before_filter')
|
15
|
-
end
|
16
|
-
after_filter do |sheet|
|
17
|
-
row_number = sheet.dimensions[1]
|
18
|
-
sheet.update_row(row_number, 'after_filter')
|
19
|
-
end
|
20
|
-
skip_header
|
21
|
-
end
|
22
|
-
end
|
23
|
-
config.xls_builder
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'uses our customized i18n scope' do
|
27
|
-
expect(builder.i18n_scope).to eq([:rspec])
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'removed the columns we told it to ignore' do
|
31
|
-
%i[id create_at].each do |removed|
|
32
|
-
column_index = builder.columns.index { |col| col.name == removed }
|
33
|
-
expect(column_index).to be_nil
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'added the columns we declared' do
|
38
|
-
added_index = builder.columns.index { |col| col.name == :author }
|
39
|
-
expect(added_index).not_to be_nil
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'has a before filter set' do
|
43
|
-
expect(builder.instance_values['before_filter']).to be_a(Proc)
|
44
|
-
end
|
45
|
-
it 'has an after filter set' do
|
46
|
-
expect(builder.instance_values['after_filter']).to be_a(Proc)
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'indicates that the header should be excluded' do
|
50
|
-
expect(builder.instance_values['skip_header']).to be_truthy
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'updates the header style' do
|
54
|
-
expect(builder.header_style[:size]).to eq(20)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
describe ActiveAdmin::ResourceController do
|
3
|
-
let(:mime) { Mime::Type.lookup_by_extension(:xls) }
|
4
|
-
|
5
|
-
let(:request) do
|
6
|
-
ActionController::TestRequest.new.tap do |test_request|
|
7
|
-
test_request.accept = mime
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
let(:response) { ActionController::TestResponse.new }
|
12
|
-
|
13
|
-
let(:controller) do
|
14
|
-
Admin::CategoriesController.new.tap do |controller|
|
15
|
-
controller.request = request
|
16
|
-
controller.response = response
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
let(:filename) do
|
21
|
-
"#{controller.resource_class.to_s.downcase.pluralize}-#{Time.now.strftime('%Y-%m-%d')}.xls"
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'generates an xls filename' do
|
25
|
-
expect(controller.xls_filename).to eq(filename)
|
26
|
-
end
|
27
|
-
|
28
|
-
context 'when making requests with the xls mime type' do
|
29
|
-
it 'returns xls attachment when requested' do
|
30
|
-
controller.send :index
|
31
|
-
disposition = "attachment; filename=\"#{filename}\""
|
32
|
-
expect(response.headers['Content-Disposition']).to eq(disposition)
|
33
|
-
expect(response.headers['Content-Transfer-Encoding']).to eq('binary')
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'returns max_csv_records for per_page' do
|
37
|
-
# this might need to go away!
|
38
|
-
max_csv_records = if controller.respond_to?(:max_csv_records, true)
|
39
|
-
controller.send(:max_csv_records)
|
40
|
-
else
|
41
|
-
controller.send(:per_page)
|
42
|
-
end
|
43
|
-
expect(controller.send(:per_page)).to eq(max_csv_records)
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'uses to the default per_page when we do not specify xls mime type' do
|
47
|
-
controller.request.accept = 'text/html'
|
48
|
-
aa_default_per_page = ActiveAdmin.application.default_per_page
|
49
|
-
expect(controller.send(:per_page)).to eq(aa_default_per_page)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|