activeadmin-xls 1.0.5 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|