padrino-admin 0.16.2 → 0.17.0
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/README.rdoc +3 -3
- data/lib/padrino-admin/access_control.rb +15 -3
- data/lib/padrino-admin/generators/actions.rb +1 -1
- data/lib/padrino-admin/generators/admin_app.rb +8 -9
- data/lib/padrino-admin/generators/admin_page.rb +7 -2
- data/lib/padrino-admin/generators/orm.rb +26 -32
- data/lib/padrino-admin/generators/templates/account/seeds.rb.tt +7 -2
- data/lib/padrino-admin/generators/templates/app.rb.tt +2 -1
- data/lib/padrino-admin/generators/templates/assets/javascripts/jquery-3.7.1.min.js +2 -0
- data/lib/padrino-admin/generators/templates/erb/app/base/index.erb.tt +1 -1
- data/lib/padrino-admin/generators/templates/erb/app/layouts/application.erb.tt +1 -1
- data/lib/padrino-admin/generators/templates/erb/app/sessions/new.erb.tt +1 -1
- data/lib/padrino-admin/generators/templates/haml/app/base/index.haml.tt +1 -1
- data/lib/padrino-admin/generators/templates/haml/app/layouts/application.haml.tt +1 -1
- data/lib/padrino-admin/generators/templates/haml/app/sessions/new.haml.tt +1 -1
- data/lib/padrino-admin/generators/templates/slim/app/base/index.slim.tt +1 -1
- data/lib/padrino-admin/generators/templates/slim/app/layouts/application.slim.tt +1 -1
- data/lib/padrino-admin/generators/templates/slim/app/sessions/new.slim.tt +1 -1
- data/lib/padrino-admin.rb +1 -1
- data/test/generators/test_account_model_generator.rb +1 -36
- data/test/generators/test_admin_app_generator.rb +149 -18
- data/test/generators/test_admin_page_generator.rb +31 -8
- data/test/test_admin_application.rb +101 -0
- metadata +6 -9
- data/lib/padrino-admin/generators/templates/account/couchrest.rb.tt +0 -67
- data/lib/padrino-admin/generators/templates/account/datamapper.rb.tt +0 -61
- data/lib/padrino-admin/generators/templates/account/mongomapper.rb.tt +0 -47
- data/lib/padrino-admin/generators/templates/assets/javascripts/jquery-1.11.0.min.js +0 -4
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
.base-icons
|
|
7
7
|
.btn-group
|
|
8
|
-
.btn.btn-primary{title: 'Adapters for
|
|
8
|
+
.btn.btn-primary{title: 'Adapters for activerecord, minirecord, sequel, mongoid, ohm, dynamoid'}
|
|
9
9
|
= tag_icon('cogs fa-2x', 'Orm Agnostic')
|
|
10
10
|
.btn.btn-success{title: 'User Authentication Support, User Authorization Management'}
|
|
11
11
|
= tag_icon('group fa-2x', 'Authentication')
|
|
@@ -46,4 +46,4 @@
|
|
|
46
46
|
%li= link_to tag_icon(:github, 'code'), 'https://github.com/padrino/padrino-framework', target: :_blank, class: 'footer-links-link'
|
|
47
47
|
%li= link_to tag_icon(:twitter, 'twitter'), 'http://twitter.com/padrinorb', target: :_blank, class: 'footer-links-link'
|
|
48
48
|
|
|
49
|
-
=javascript_include_tag 'jquery-
|
|
49
|
+
=javascript_include_tag 'jquery-3.7.1.min', (Padrino.env == :production ? 'bootstrap/bootstrap.min' : %w[bootstrap/affix bootstrap/alert bootstrap/button bootstrap/carousel bootstrap/collapse bootstrap/dropdown bootstrap/tooltip bootstrap/transition bootstrap/modal bootstrap/popover bootstrap/scrollspy bootstrap/tab]), :application
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
.login-footer.modal-footer
|
|
36
36
|
= submit_tag(pat('login.sign_in'), class: 'btn btn-primary pull-right')
|
|
37
37
|
|
|
38
|
-
=javascript_include_tag 'jquery-
|
|
38
|
+
=javascript_include_tag 'jquery-3.7.1.min', (Padrino.env == :production ? 'bootstrap/bootstrap.min' : %w[bootstrap/affix bootstrap/alert bootstrap/button bootstrap/carousel bootstrap/collapse bootstrap/dropdown bootstrap/tooltip bootstrap/transition bootstrap/modal bootstrap/popover bootstrap/scrollspy bootstrap/tab]), :application
|
|
39
39
|
|
|
@@ -5,7 +5,7 @@ div class="base-text"
|
|
|
5
5
|
|
|
6
6
|
div class="base-icons"
|
|
7
7
|
div class="btn-group"
|
|
8
|
-
div class="btn btn-primary" title="Adapters for
|
|
8
|
+
div class="btn btn-primary" title="Adapters for activerecord, minirecord, sequel, mongoid, ohm, dynamoid" = tag_icon("cogs fa-2x", "Orm Agnostic")
|
|
9
9
|
div class="btn btn-success" title="User Authentication Support, User Authorization Management" = tag_icon("group fa-2x", "Authentication")
|
|
10
10
|
div class="btn btn-info" title="Erb, Haml, Slim Rendering Support" = tag_icon("tasks fa-2x", "Template Agnostic")
|
|
11
11
|
div class="btn btn-warning" title="English, German, Russian, Danish, French, Brazilian and Italian localizations" = tag_icon("flag fa-2x", "Multi Language")
|
|
@@ -46,4 +46,4 @@ html lang='en'
|
|
|
46
46
|
li = link_to tag_icon(:github, 'code'), 'https://github.com/padrino/padrino-framework', target: :_blank, class: 'footer-links-link'
|
|
47
47
|
li = link_to tag_icon(:twitter, 'twitter'), 'http://twitter.com/padrinorb', target: :_blank, class: 'footer-links-link'
|
|
48
48
|
|
|
49
|
-
= javascript_include_tag 'jquery-
|
|
49
|
+
= javascript_include_tag 'jquery-3.7.1.min', (Padrino.env == :production ? 'bootstrap/bootstrap.min' : %w[bootstrap/affix bootstrap/alert bootstrap/button bootstrap/carousel bootstrap/collapse bootstrap/dropdown bootstrap/tooltip bootstrap/transition bootstrap/modal bootstrap/popover bootstrap/scrollspy bootstrap/tab]), :application
|
|
@@ -34,4 +34,4 @@ html lang="en" xmlns="http://www.w3.org/1999/xhtml"
|
|
|
34
34
|
|
|
35
35
|
div class="login-footer modal-footer"
|
|
36
36
|
= submit_tag(pat('login.sign_in'), class: 'btn btn-primary pull-right')
|
|
37
|
-
= javascript_include_tag 'jquery-
|
|
37
|
+
= javascript_include_tag 'jquery-3.7.1.min', (Padrino.env == :production ? 'bootstrap/bootstrap.min' : %w[bootstrap/affix bootstrap/alert bootstrap/button bootstrap/carousel bootstrap/collapse bootstrap/dropdown bootstrap/tooltip bootstrap/transition bootstrap/modal bootstrap/popover bootstrap/scrollspy bootstrap/tab]), :application
|
data/lib/padrino-admin.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Padrino
|
|
|
8
8
|
##
|
|
9
9
|
# Padrino::Admin is beautiful Ajax Admin, with these features:
|
|
10
10
|
#
|
|
11
|
-
# Orm Agnostic:: Adapters for
|
|
11
|
+
# Orm Agnostic:: Adapters for activerecord, sequel, mongoid, ohm, and more
|
|
12
12
|
# Authentication:: Support for Account authentication, Account Permission management
|
|
13
13
|
# Scaffold:: You can simply create a new "admin interface" simply providing a Model
|
|
14
14
|
# Ajax Uploads:: You can upload file, manage them and attach them to any model in a quick and simple way (coming soon)
|
|
@@ -7,7 +7,7 @@ describe 'AccountModelGenerator' do
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
after do
|
|
10
|
-
|
|
10
|
+
`rm -rf #{@apptmp}`
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
describe 'activerecord' do
|
|
@@ -34,18 +34,6 @@ describe 'AccountModelGenerator' do
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
describe 'datamapper' do
|
|
38
|
-
before do
|
|
39
|
-
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=datamapper') }
|
|
40
|
-
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
41
|
-
@model = "#{@apptmp}/sample_project/models/account.rb"
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
it 'should include the datamapper resource' do
|
|
45
|
-
assert_match_in_file(/include DataMapper::Resource/m, @model)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
37
|
describe 'mongoid' do
|
|
50
38
|
before do
|
|
51
39
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=mongoid') }
|
|
@@ -58,18 +46,6 @@ describe 'AccountModelGenerator' do
|
|
|
58
46
|
end
|
|
59
47
|
end
|
|
60
48
|
|
|
61
|
-
describe 'mongomapper' do
|
|
62
|
-
before do
|
|
63
|
-
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=mongomapper') }
|
|
64
|
-
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
65
|
-
@model = "#{@apptmp}/sample_project/models/account.rb"
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it 'should include the mongomapper document' do
|
|
69
|
-
assert_match_in_file(/include MongoMapper::Document/m, @model)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
49
|
describe 'ohm' do
|
|
74
50
|
before do
|
|
75
51
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=ohm') }
|
|
@@ -94,15 +70,4 @@ describe 'AccountModelGenerator' do
|
|
|
94
70
|
end
|
|
95
71
|
end
|
|
96
72
|
|
|
97
|
-
describe 'couchrest' do
|
|
98
|
-
before do
|
|
99
|
-
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=couchrest') }
|
|
100
|
-
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
101
|
-
@model = "#{@apptmp}/sample_project/models/account.rb"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
it 'should be a couchrest model instance' do
|
|
105
|
-
assert_match_in_file(/class Account < CouchRest::Model::Base/m, @model)
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
73
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative '../helper'
|
|
2
|
+
require 'open3'
|
|
2
3
|
|
|
3
4
|
describe 'AdminAppGenerator' do
|
|
4
5
|
before do
|
|
@@ -12,27 +13,100 @@ describe 'AdminAppGenerator' do
|
|
|
12
13
|
|
|
13
14
|
describe 'the admin app generator' do
|
|
14
15
|
before do
|
|
15
|
-
# Account gets created by Datamapper's migration and then gets
|
|
16
|
-
# rejected by model generator as already defined
|
|
17
16
|
Object.send(:remove_const, :Account) if defined?(Account)
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
it 'should fail outside app root' do
|
|
21
|
-
out, = capture_io { generate(:admin_app, "-r=#{@apptmp}") }
|
|
20
|
+
out, = capture_io { assert_equal 1, assert_raises(SystemExit) { generate(:admin_app, "-r=#{@apptmp}") }.status }
|
|
22
21
|
assert_match(/not at the root/, out)
|
|
23
22
|
assert_no_file_exists("#{@apptmp}/admin")
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
it "should fail if we don't specify an orm on the project" do
|
|
27
26
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-e=haml') }
|
|
28
|
-
|
|
27
|
+
out, = capture_io { assert_equal 1, assert_raises(SystemExit) { generate(:admin_app, "-r=#{@apptmp}/sample_project") }.status }
|
|
28
|
+
assert_match(/Padrino only supports/, out)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'should fail with an unsupported renderer' do
|
|
32
|
+
capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}", '-d=activerecord', '-e=haml') }
|
|
33
|
+
out, = capture_io { assert_equal 1, assert_raises(SystemExit) { generate(:admin_app, "-r=#{@apptmp}/sample_project", '-e=liquid') }.status }
|
|
34
|
+
assert_match(/for admin we only support/, out)
|
|
35
|
+
assert_no_file_exists("#{@apptmp}/sample_project/admin")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'should reuse an existing account model' do
|
|
39
|
+
capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}", '-d=activerecord', '-e=haml') }
|
|
40
|
+
Object.const_set(:Account, Class.new)
|
|
41
|
+
begin
|
|
42
|
+
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
43
|
+
rescue SystemExit => e
|
|
44
|
+
flunk "the admin generator exited with status #{e.status}"
|
|
45
|
+
end
|
|
46
|
+
assert_file_exists("#{@apptmp}/sample_project/admin/app.rb")
|
|
47
|
+
assert_match_in_file(/class Account < ActiveRecord::Base/, "#{@apptmp}/sample_project/models/account.rb")
|
|
48
|
+
assert_no_file_exists("#{@apptmp}/sample_project/db/migrate/001_create_accounts.rb")
|
|
49
|
+
ensure
|
|
50
|
+
Object.send(:remove_const, :Account) if defined?(Account)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'should generate the account model test with the project test_config path' do
|
|
54
|
+
capture_io { generate(:project, 'sample_project', '-e=haml', "--root=#{@apptmp}", '-d=activerecord', '-t=minitest') }
|
|
55
|
+
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
56
|
+
assert_match_in_file %r{__dir__\}/\.\./test_config\.rb}, "#{@apptmp}/sample_project/test/models/account_test.rb"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'should generate seeds that read the account from piped input' do
|
|
60
|
+
capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}", '-d=sequel', '-e=haml') }
|
|
61
|
+
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
62
|
+
script = <<~RUBY
|
|
63
|
+
require 'thor'
|
|
64
|
+
# Thor's line editor can echo or buffer piped input, so without a terminal the
|
|
65
|
+
# seeds must not ask through it.
|
|
66
|
+
class NoAskShell < Thor::Base.shell
|
|
67
|
+
def ask(*)
|
|
68
|
+
raise 'shell.ask used without a terminal'
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def shell
|
|
73
|
+
@shell ||= NoAskShell.new
|
|
74
|
+
end
|
|
75
|
+
class Account
|
|
76
|
+
def initialize(attributes)
|
|
77
|
+
@attributes = attributes
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def valid?
|
|
81
|
+
true
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def save
|
|
85
|
+
puts "SAVED \#{@attributes[:email]} \#{@attributes[:password] == 'secret123'}"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
load 'db/seeds.rb'
|
|
89
|
+
RUBY
|
|
90
|
+
env = { 'BUNDLE_GEMFILE' => File.expand_path('../../../Gemfile', __dir__) }
|
|
91
|
+
out, err, status = Open3.capture3(env, RbConfig.ruby, '-rbundler/setup', '-e', script,
|
|
92
|
+
chdir: "#{@apptmp}/sample_project", stdin_data: "admin@example.com \nsecret123\r\n")
|
|
93
|
+
assert status.success?, err
|
|
94
|
+
assert_includes out.lines.map(&:strip), 'SAVED admin@example.com true'
|
|
95
|
+
refute_includes out, 'secret123'
|
|
29
96
|
end
|
|
30
97
|
|
|
31
98
|
it 'should store and apply session_secret' do
|
|
32
|
-
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=
|
|
99
|
+
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=sequel', '-e=haml') }
|
|
33
100
|
assert_match_in_file(/set :session_secret, '[0-9A-z]*'/, "#{@apptmp}/sample_project/config/apps.rb")
|
|
34
101
|
end
|
|
35
102
|
|
|
103
|
+
it 'should set SameSite=Lax on the admin session cookie' do
|
|
104
|
+
capture_io { generate(:project, 'sample_project', '-e=haml', "--root=#{@apptmp}", '-d=activerecord') }
|
|
105
|
+
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
106
|
+
assert_match_in_file(/^ set :sessions, same_site: :lax$/, "#{@apptmp}/sample_project/admin/app.rb")
|
|
107
|
+
assert_no_match_in_file(/^\s*enable\s+:sessions/, "#{@apptmp}/sample_project/admin/app.rb")
|
|
108
|
+
end
|
|
109
|
+
|
|
36
110
|
it 'should generate the admin app' do
|
|
37
111
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=activerecord') }
|
|
38
112
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
@@ -49,7 +123,7 @@ describe 'AdminAppGenerator' do
|
|
|
49
123
|
assert_file_exists("#{@apptmp}/sample_project/public/admin/stylesheets/application.css")
|
|
50
124
|
assert_file_exists("#{@apptmp}/sample_project/public/admin/stylesheets/bootstrap.css")
|
|
51
125
|
assert_file_exists("#{@apptmp}/sample_project/public/admin/javascripts/application.js")
|
|
52
|
-
assert_file_exists("#{@apptmp}/sample_project/public/admin/javascripts/jquery-
|
|
126
|
+
assert_file_exists("#{@apptmp}/sample_project/public/admin/javascripts/jquery-3.7.1.min.js")
|
|
53
127
|
assert_file_exists("#{@apptmp}/sample_project/models/account.rb")
|
|
54
128
|
assert_file_exists("#{@apptmp}/sample_project/db/seeds.rb")
|
|
55
129
|
assert_file_exists("#{@apptmp}/sample_project/db/migrate/001_create_accounts.rb")
|
|
@@ -74,6 +148,17 @@ describe 'AdminAppGenerator' do
|
|
|
74
148
|
assert_match_in_file(/Hello, custom generator/, "#{@apptmp}/sample_project/admin/views/layouts/application.slim")
|
|
75
149
|
end
|
|
76
150
|
|
|
151
|
+
it 'should generate the account pages in an admin app named with a plural' do
|
|
152
|
+
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=sequel') }
|
|
153
|
+
begin
|
|
154
|
+
capture_io { generate(:admin_app, '-a=admins', "--root=#{@apptmp}/sample_project") }
|
|
155
|
+
rescue SystemExit => e
|
|
156
|
+
flunk "the admin generator exited with status #{e.status}"
|
|
157
|
+
end
|
|
158
|
+
assert_file_exists("#{@apptmp}/sample_project/admins/controllers/accounts.rb")
|
|
159
|
+
assert_no_dir_exists("#{@apptmp}/sample_project/admin")
|
|
160
|
+
end
|
|
161
|
+
|
|
77
162
|
it 'should generate the admin app under a different folder' do
|
|
78
163
|
# TODO: FIXME Implement option --admin_root or something. See https://github.com/padrino/padrino-framework/issues/854#issuecomment-14749356
|
|
79
164
|
skip
|
|
@@ -124,6 +209,49 @@ describe 'AdminAppGenerator' do
|
|
|
124
209
|
assert_file_exists("#{@apptmp}/sample_project/admin/views/layouts/application.slim")
|
|
125
210
|
assert_file_exists("#{@apptmp}/sample_project/admin/views/sessions/new.slim")
|
|
126
211
|
end
|
|
212
|
+
|
|
213
|
+
%w[haml erb slim].each do |engine|
|
|
214
|
+
it "should serve every script the #{engine} admin pages include" do
|
|
215
|
+
skip 'the sqlite3 gem is only bundled on MRI' if RUBY_ENGINE == 'jruby'
|
|
216
|
+
capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}", '-d=sequel', "-e=#{engine}") }
|
|
217
|
+
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
218
|
+
script = <<~'RUBY'
|
|
219
|
+
ENV['RACK_ENV'] = 'test'
|
|
220
|
+
%w[sequel haml slim erubi nokogiri rack/test].each { |lib| require lib }
|
|
221
|
+
Sequel.extension :migration
|
|
222
|
+
Sequel.sqlite('db/sample_project_test.db') { |db| Sequel::Migrator.run(db, 'db/migrate') }
|
|
223
|
+
require File.expand_path('config/boot')
|
|
224
|
+
account_id = Account.dataset.insert(email: 'admin@example.com', role: 'admin', crypted_password: 'unused')
|
|
225
|
+
|
|
226
|
+
browser = Rack::Test::Session.new(Padrino.application)
|
|
227
|
+
check = lambda do |page|
|
|
228
|
+
browser.get(page)
|
|
229
|
+
puts "PAGE #{page} #{browser.last_response.status}"
|
|
230
|
+
srcs = Nokogiri::HTML(browser.last_response.body).css('script[src]').map { |tag| tag['src'] }
|
|
231
|
+
srcs.select { |src| src.start_with?('/') }.each do |src|
|
|
232
|
+
puts "SCRIPT #{page} #{src} #{browser.get(src).status}"
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
check.call('/admin/sessions/new')
|
|
236
|
+
browser.env('rack.session', SampleProject::Admin.session_id => account_id)
|
|
237
|
+
check.call('/admin/')
|
|
238
|
+
check.call('/admin/accounts')
|
|
239
|
+
RUBY
|
|
240
|
+
env = { 'BUNDLE_GEMFILE' => File.expand_path('../../../Gemfile', __dir__) }
|
|
241
|
+
out, err, status = Open3.capture3(env, RbConfig.ruby, '-rbundler/setup', '-e', script, chdir: "#{@apptmp}/sample_project")
|
|
242
|
+
assert status.success?, err
|
|
243
|
+
pages = out.lines.grep(/\APAGE /).map(&:split)
|
|
244
|
+
scripts = out.lines.grep(/\ASCRIPT /).map(&:split)
|
|
245
|
+
statuses = pages.map { |line| line[1, 2] }
|
|
246
|
+
assert_equal [['/admin/sessions/new', '200'], ['/admin/', '200'], ['/admin/accounts', '200']], statuses
|
|
247
|
+
pages.each do |(_, page)|
|
|
248
|
+
refute_empty scripts.select { |line| line[1] == page }, "#{page} includes no local scripts"
|
|
249
|
+
end
|
|
250
|
+
scripts.each do |(_, page, src, code)|
|
|
251
|
+
assert_equal '200', code, "#{page} includes #{src}, which is not served"
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
127
255
|
end
|
|
128
256
|
|
|
129
257
|
it 'should correctly generate a new padrino admin application with a custom model' do
|
|
@@ -192,24 +320,27 @@ describe 'AdminAppGenerator' do
|
|
|
192
320
|
assert_match_in_file(/ use ConnectionPoolManagement/m, "#{@apptmp}/sample_project/admin/app.rb")
|
|
193
321
|
end
|
|
194
322
|
|
|
195
|
-
it 'should not add it for #datamapper' do
|
|
196
|
-
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=datamapper', '-e=haml') }
|
|
197
|
-
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
198
|
-
assert_no_match_in_file(/ use ConnectionPoolManagement/m, "#{@apptmp}/sample_project/admin/app.rb")
|
|
199
|
-
end
|
|
200
323
|
end
|
|
201
324
|
|
|
202
|
-
describe '
|
|
203
|
-
|
|
204
|
-
|
|
325
|
+
describe 'with the vite script component' do
|
|
326
|
+
def assert_activesupport_listed_once(gemfile)
|
|
327
|
+
Bundler.ui.expects(:warn).never
|
|
328
|
+
dsl = Bundler::Dsl.new
|
|
329
|
+
dsl.eval_gemfile(gemfile)
|
|
330
|
+
assert_equal(1, dsl.dependencies.count { |dep| dep.name == 'activesupport' })
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it 'should list activesupport once when admin is added after vite' do
|
|
334
|
+
capture_io { generate(:project, 'sample_project', '-e=haml', "--root=#{@apptmp}", '-d=sequel', '--script=vite') }
|
|
205
335
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
206
|
-
|
|
336
|
+
assert_activesupport_listed_once("#{@apptmp}/sample_project/Gemfile")
|
|
207
337
|
end
|
|
208
338
|
|
|
209
|
-
it 'should
|
|
210
|
-
capture_io { generate(:project, 'sample_project', '-e=
|
|
339
|
+
it 'should list activesupport once when vite is added after admin' do
|
|
340
|
+
capture_io { generate(:project, 'sample_project', '-e=haml', "--root=#{@apptmp}", '-d=sequel') }
|
|
211
341
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
212
|
-
|
|
342
|
+
capture_io { generate(:component, '--script=vite', "-r=#{@apptmp}/sample_project") }
|
|
343
|
+
assert_activesupport_listed_once("#{@apptmp}/sample_project/Gemfile")
|
|
213
344
|
end
|
|
214
345
|
end
|
|
215
346
|
|
|
@@ -13,21 +13,50 @@ describe 'AdminPageGenerator' do
|
|
|
13
13
|
|
|
14
14
|
describe 'the admin page generator' do
|
|
15
15
|
it 'should fail outside app root' do
|
|
16
|
-
out, = capture_io { generate(:admin_page, 'foo', "-r=#{@apptmp}/sample_project") }
|
|
16
|
+
out, = capture_io { assert_equal 1, assert_raises(SystemExit) { generate(:admin_page, 'foo', "-r=#{@apptmp}/sample_project") }.status }
|
|
17
17
|
assert_match(/not at the root/, out)
|
|
18
18
|
assert_no_file_exists("#{@apptmp}/admin")
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
it 'should fail when the admin app does not exist' do
|
|
22
|
+
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=sequel') }
|
|
23
|
+
out, = capture_io { assert_equal 1, assert_raises(SystemExit) { generate(:admin_page, 'friend', "-r=#{@apptmp}/sample_project") }.status }
|
|
24
|
+
assert_match(/Unable to locate 'Admin' application/, out)
|
|
25
|
+
assert_no_dir_exists("#{@apptmp}/sample_project/admin")
|
|
26
|
+
end
|
|
27
|
+
|
|
21
28
|
it 'should fail without an existent model' do
|
|
22
29
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=activerecord') }
|
|
23
30
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
24
31
|
assert_raises(Padrino::Admin::Generators::OrmError) { generate(:admin_page, 'foo', "-r=#{@apptmp}/sample_project") }
|
|
25
32
|
end
|
|
26
33
|
|
|
34
|
+
it 'should fail when the model does not match the project orm' do
|
|
35
|
+
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=activerecord') }
|
|
36
|
+
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
37
|
+
error = assert_raises(Padrino::Admin::Generators::OrmError) { capture_io { generate(:admin_page, 'friend', "-r=#{@apptmp}/sample_project") } }
|
|
38
|
+
assert_match(/Friend.*activerecord/m, error.message)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should destroy a page even when the model does not match the project orm' do
|
|
42
|
+
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=activerecord') }
|
|
43
|
+
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
44
|
+
FileUtils.mkdir_p("#{@apptmp}/sample_project/admin/views/friends")
|
|
45
|
+
capture_io { generate(:admin_page, 'friend', "-r=#{@apptmp}/sample_project", '-d') }
|
|
46
|
+
assert_no_dir_exists("#{@apptmp}/sample_project/admin/views/friends")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'should fail when the project has no orm and the model is not activerecord' do
|
|
50
|
+
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=sequel') }
|
|
51
|
+
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
52
|
+
components = "#{@apptmp}/sample_project/.components"
|
|
53
|
+
File.write(components, File.read(components).sub(/^:orm:.*\n/, ''))
|
|
54
|
+
assert_raises(Padrino::Admin::Generators::OrmError) { capture_io { generate(:admin_page, 'friend', "-r=#{@apptmp}/sample_project") } }
|
|
55
|
+
end
|
|
56
|
+
|
|
27
57
|
it 'should correctly generate a new page' do
|
|
28
58
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=sequel', '-e=haml') }
|
|
29
59
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
30
|
-
capture_io { generate(:model, 'friend', 'name:string', 'age:integer', 'email:string', "--root=#{@apptmp}/sample_project") }
|
|
31
60
|
capture_io { generate(:admin_page, 'friend', "--root=#{@apptmp}/sample_project") }
|
|
32
61
|
assert_file_exists "#{@apptmp}/sample_project/admin/controllers/friends.rb"
|
|
33
62
|
assert_match_in_file 'SampleProject::Admin.controllers :friends do', "#{@apptmp}/sample_project/admin/controllers/friends.rb"
|
|
@@ -41,7 +70,6 @@ describe 'AdminPageGenerator' do
|
|
|
41
70
|
custom_template_path = "#{@apptmp}/sample_project/generators/templates/haml/page/"
|
|
42
71
|
`mkdir -p #{custom_template_path} && echo "%h1= 'Hello, custom generator' " > #{custom_template_path}index.haml.tt`
|
|
43
72
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
44
|
-
capture_io { generate(:model, 'friend', 'name:string', 'age:integer', 'email:string', "--root=#{@apptmp}/sample_project") }
|
|
45
73
|
capture_io { generate(:admin_page, 'friend', "--root=#{@apptmp}/sample_project") }
|
|
46
74
|
assert_match_in_file(/Hello, custom generator/, "#{@apptmp}/sample_project/admin/views/friends/index.haml")
|
|
47
75
|
end
|
|
@@ -50,7 +78,6 @@ describe 'AdminPageGenerator' do
|
|
|
50
78
|
it 'should correctly generate a new page with haml' do
|
|
51
79
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=sequel', '-e=haml') }
|
|
52
80
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
53
|
-
capture_io { generate(:model, 'friend', 'name:string', 'age:integer', 'email:string', "--root=#{@apptmp}/sample_project") }
|
|
54
81
|
capture_io { generate(:admin_page, 'friend', "--root=#{@apptmp}/sample_project") }
|
|
55
82
|
assert_file_exists "#{@apptmp}/sample_project/admin/views/friends/_form.haml"
|
|
56
83
|
assert_file_exists "#{@apptmp}/sample_project/admin/views/friends/edit.haml"
|
|
@@ -66,7 +93,6 @@ describe 'AdminPageGenerator' do
|
|
|
66
93
|
it 'should correctly generate a new page with erb' do
|
|
67
94
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=sequel', '-e=erb') }
|
|
68
95
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
69
|
-
capture_io { generate(:model, 'friend', 'name:string', 'age:integer', 'email:string', "--root=#{@apptmp}/sample_project") }
|
|
70
96
|
capture_io { generate(:admin_page, 'friend', "--root=#{@apptmp}/sample_project") }
|
|
71
97
|
assert_file_exists "#{@apptmp}/sample_project/admin/views/friends/_form.erb"
|
|
72
98
|
assert_file_exists "#{@apptmp}/sample_project/admin/views/friends/edit.erb"
|
|
@@ -82,7 +108,6 @@ describe 'AdminPageGenerator' do
|
|
|
82
108
|
it 'should correctly generate a new page with slim' do
|
|
83
109
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=sequel', '-e=slim') }
|
|
84
110
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
85
|
-
capture_io { generate(:model, 'friend', 'name:string', 'age:integer', 'email:string', "--root=#{@apptmp}/sample_project") }
|
|
86
111
|
capture_io { generate(:admin_page, 'friend', "--root=#{@apptmp}/sample_project") }
|
|
87
112
|
assert_file_exists "#{@apptmp}/sample_project/admin/views/friends/_form.slim"
|
|
88
113
|
assert_file_exists "#{@apptmp}/sample_project/admin/views/friends/edit.slim"
|
|
@@ -99,8 +124,6 @@ describe 'AdminPageGenerator' do
|
|
|
99
124
|
it 'should correctly generate a new padrino admin application with multiple models at the same time' do
|
|
100
125
|
capture_io { generate(:project, 'sample_project', '-e=slim', "--root=#{@apptmp}", '-d=sequel', '-e=haml') }
|
|
101
126
|
capture_io { generate(:admin_app, "--root=#{@apptmp}/sample_project") }
|
|
102
|
-
capture_io { generate(:model, 'friend', 'name:string', 'age:integer', 'email:string', "-root=#{@apptmp}/sample_project") }
|
|
103
|
-
capture_io { generate(:model, 'page', 'name:string', 'body:string', "-root=#{@apptmp}/sample_project") }
|
|
104
127
|
capture_io { generate(:admin_page, 'friend', 'page', "--root=#{@apptmp}/sample_project") }
|
|
105
128
|
# For Friend
|
|
106
129
|
assert_file_exists "#{@apptmp}/sample_project/admin/controllers/friends.rb"
|
|
@@ -163,6 +163,100 @@ describe 'AdminApplication' do
|
|
|
163
163
|
assert_equal 'posts', body
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
+
it 'should match paths literally without regex metacharacter interpretation' do
|
|
167
|
+
mock_app do
|
|
168
|
+
register Padrino::Admin::AccessControl
|
|
169
|
+
enable :sessions
|
|
170
|
+
|
|
171
|
+
access_control.roles_for :any do |role|
|
|
172
|
+
role.protect '/'
|
|
173
|
+
role.allow '/accounts.json'
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Without authentication, /accounts.json should be allowed
|
|
178
|
+
assert @app.access_control.allowed?(nil, '/accounts.json')
|
|
179
|
+
# /accountsXjson must NOT match - the dot in .json is literal, not a regex wildcard
|
|
180
|
+
refute @app.access_control.allowed?(nil, '/accountsXjson')
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'should not allow paths that merely start with an allowed path' do
|
|
184
|
+
mock_app do
|
|
185
|
+
register Padrino::Admin::AccessControl
|
|
186
|
+
enable :sessions
|
|
187
|
+
|
|
188
|
+
access_control.roles_for :any do |role|
|
|
189
|
+
role.protect '/'
|
|
190
|
+
role.allow '/sessions'
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
access_control.roles_for :editor do |role|
|
|
194
|
+
role.project_module :posts, '/posts'
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
access_control.roles_for :admin do |role|
|
|
198
|
+
role.project_module :post_admin, '/posts_admin'
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
assert @app.access_control.allowed?(nil, '/sessions')
|
|
203
|
+
assert @app.access_control.allowed?(nil, '/sessions/new')
|
|
204
|
+
assert @app.access_control.allowed?(nil, '/sessions.json')
|
|
205
|
+
refute @app.access_control.allowed?(nil, '/sessions_admin')
|
|
206
|
+
refute @app.access_control.allowed?(nil, '/sessionsX')
|
|
207
|
+
|
|
208
|
+
# An editor's '/posts' must not reach the admin-only '/posts_admin' module.
|
|
209
|
+
assert @app.access_control.allowed?(Account.editor, '/posts')
|
|
210
|
+
assert @app.access_control.allowed?(Account.editor, '/posts/1/edit')
|
|
211
|
+
refute @app.access_control.allowed?(Account.editor, '/posts_admin')
|
|
212
|
+
assert @app.access_control.allowed?(Account.admin, '/posts_admin')
|
|
213
|
+
|
|
214
|
+
# Paths are compared after percent-decoding.
|
|
215
|
+
refute @app.access_control.allowed?(Account.editor, '/%70osts_admin')
|
|
216
|
+
assert @app.access_control.allowed?(Account.editor, '/%70osts')
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'should allow decoded non-ASCII paths and prefixes ending in a dot' do
|
|
220
|
+
mock_app do
|
|
221
|
+
register Padrino::Admin::AccessControl
|
|
222
|
+
enable :sessions
|
|
223
|
+
|
|
224
|
+
access_control.roles_for :any do |role|
|
|
225
|
+
role.protect '/'
|
|
226
|
+
role.allow '/café'
|
|
227
|
+
role.allow '/v1.'
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
assert @app.access_control.allowed?(nil, '/caf%C3%A9'.b)
|
|
232
|
+
assert @app.access_control.allowed?(nil, '/café')
|
|
233
|
+
assert @app.access_control.allowed?(nil, '/v1.json')
|
|
234
|
+
refute @app.access_control.allowed?(nil, '/v2.json')
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it 'should match Regexp paths at the start of the path' do
|
|
238
|
+
mock_app do
|
|
239
|
+
register Padrino::Admin::AccessControl
|
|
240
|
+
enable :sessions
|
|
241
|
+
|
|
242
|
+
access_control.roles_for :any do |role|
|
|
243
|
+
role.protect %r{\A/secret}
|
|
244
|
+
role.allow %r{\A/reports/\d+\z}
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
access_control.roles_for :admin do |role|
|
|
248
|
+
role.allow %r{\A/stats}
|
|
249
|
+
role.project_module :posts, '/posts'
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
assert @app.access_control.allowed?(nil, '/reports/1')
|
|
254
|
+
refute @app.access_control.allowed?(nil, '/secret/x')
|
|
255
|
+
assert @app.access_control.allowed?(Account.admin, '/stats/today')
|
|
256
|
+
assert @app.access_control.allowed?(Account.admin, '/posts')
|
|
257
|
+
refute @app.access_control.allowed?(Account.editor, '/stats')
|
|
258
|
+
end
|
|
259
|
+
|
|
166
260
|
it 'should emulate an ecommerce app' do
|
|
167
261
|
mock_app do
|
|
168
262
|
register Padrino::Admin::AccessControl
|
|
@@ -282,4 +376,11 @@ describe 'AdminApplication' do
|
|
|
282
376
|
assert_equal '/foo', app1.access_control.project_modules(:any).first.path
|
|
283
377
|
assert_equal '/bar', app2.access_control.project_modules(:any).first.path
|
|
284
378
|
end
|
|
379
|
+
|
|
380
|
+
it 'should raise AccessControlError for invalid roles' do
|
|
381
|
+
access_control = Padrino::Admin::AccessControl::Base.new
|
|
382
|
+
error = assert_raises(Padrino::Admin::AccessControlError) { access_control.roles_for('admin') }
|
|
383
|
+
assert_match(/"admin"/, error.message)
|
|
384
|
+
assert_raises(Padrino::Admin::AccessControlError) { access_control.roles_for }
|
|
385
|
+
end
|
|
285
386
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: padrino-admin
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Padrino Team
|
|
@@ -19,28 +19,28 @@ dependencies:
|
|
|
19
19
|
requirements:
|
|
20
20
|
- - '='
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
|
-
version: 0.
|
|
22
|
+
version: 0.17.0
|
|
23
23
|
type: :runtime
|
|
24
24
|
prerelease: false
|
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
26
|
requirements:
|
|
27
27
|
- - '='
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: 0.
|
|
29
|
+
version: 0.17.0
|
|
30
30
|
- !ruby/object:Gem::Dependency
|
|
31
31
|
name: padrino-helpers
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
|
33
33
|
requirements:
|
|
34
34
|
- - '='
|
|
35
35
|
- !ruby/object:Gem::Version
|
|
36
|
-
version: 0.
|
|
36
|
+
version: 0.17.0
|
|
37
37
|
type: :runtime
|
|
38
38
|
prerelease: false
|
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
|
40
40
|
requirements:
|
|
41
41
|
- - '='
|
|
42
42
|
- !ruby/object:Gem::Version
|
|
43
|
-
version: 0.
|
|
43
|
+
version: 0.17.0
|
|
44
44
|
- !ruby/object:Gem::Dependency
|
|
45
45
|
name: therubyracer
|
|
46
46
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -143,12 +143,9 @@ files:
|
|
|
143
143
|
- lib/padrino-admin/generators/admin_page.rb
|
|
144
144
|
- lib/padrino-admin/generators/orm.rb
|
|
145
145
|
- lib/padrino-admin/generators/templates/account/activerecord.rb.tt
|
|
146
|
-
- lib/padrino-admin/generators/templates/account/couchrest.rb.tt
|
|
147
|
-
- lib/padrino-admin/generators/templates/account/datamapper.rb.tt
|
|
148
146
|
- lib/padrino-admin/generators/templates/account/dynamoid.rb.tt
|
|
149
147
|
- lib/padrino-admin/generators/templates/account/minirecord.rb.tt
|
|
150
148
|
- lib/padrino-admin/generators/templates/account/mongoid.rb.tt
|
|
151
|
-
- lib/padrino-admin/generators/templates/account/mongomapper.rb.tt
|
|
152
149
|
- lib/padrino-admin/generators/templates/account/ohm.rb.tt
|
|
153
150
|
- lib/padrino-admin/generators/templates/account/seeds.rb.tt
|
|
154
151
|
- lib/padrino-admin/generators/templates/account/sequel.rb.tt
|
|
@@ -176,7 +173,7 @@ files:
|
|
|
176
173
|
- lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/tab.js
|
|
177
174
|
- lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/tooltip.js
|
|
178
175
|
- lib/padrino-admin/generators/templates/assets/javascripts/bootstrap/transition.js
|
|
179
|
-
- lib/padrino-admin/generators/templates/assets/javascripts/jquery-
|
|
176
|
+
- lib/padrino-admin/generators/templates/assets/javascripts/jquery-3.7.1.min.js
|
|
180
177
|
- lib/padrino-admin/generators/templates/assets/stylesheets/application.css
|
|
181
178
|
- lib/padrino-admin/generators/templates/assets/stylesheets/bootstrap.css
|
|
182
179
|
- lib/padrino-admin/generators/templates/assets/stylesheets/fonts/FontAwesome.otf
|