sinatra-admin 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +176 -0
- data/Rakefile +2 -0
- data/dummy/config.ru +12 -0
- data/dummy/config/mongoid.yml +6 -0
- data/dummy/dummy.rb +19 -0
- data/dummy/models/admin.rb +6 -0
- data/dummy/models/comment.rb +5 -0
- data/dummy/models/user.rb +11 -0
- data/dummy/views/admin/customs/index.haml +4 -0
- data/dummy/views/admin/users/custom.haml +2 -0
- data/features/admin_login.feature +36 -0
- data/features/admin_logout.feature +14 -0
- data/features/creating_users.feature +44 -0
- data/features/custom_pages.feature +34 -0
- data/features/default_root.feature +43 -0
- data/features/editing_users.feature +46 -0
- data/features/listing_users.feature +46 -0
- data/features/main_menu_resources.feature +41 -0
- data/features/removing_users.feature +34 -0
- data/features/step_definitions/common_steps.rb +59 -0
- data/features/step_definitions/web_steps.rb +212 -0
- data/features/support/database_cleaner.rb +17 -0
- data/features/support/env.rb +18 -0
- data/features/support/paths.rb +30 -0
- data/features/support/sinatra_admin.rb +3 -0
- data/features/support/warden.rb +9 -0
- data/features/user_details.feature +31 -0
- data/lib/sinatra-admin.rb +42 -0
- data/lib/sinatra-admin/app.rb +74 -0
- data/lib/sinatra-admin/config.rb +45 -0
- data/lib/sinatra-admin/helpers/session.rb +24 -0
- data/lib/sinatra-admin/helpers/template_lookup.rb +7 -0
- data/lib/sinatra-admin/models/admin.rb +40 -0
- data/lib/sinatra-admin/register.rb +8 -0
- data/lib/sinatra-admin/register/base.rb +29 -0
- data/lib/sinatra-admin/register/custom.rb +10 -0
- data/lib/sinatra-admin/register/model.rb +75 -0
- data/lib/sinatra-admin/version.rb +3 -0
- data/lib/sinatra-admin/views/auth/login.haml +16 -0
- data/lib/sinatra-admin/views/edit.haml +21 -0
- data/lib/sinatra-admin/views/index.haml +34 -0
- data/lib/sinatra-admin/views/layout.haml +19 -0
- data/lib/sinatra-admin/views/new.haml +19 -0
- data/lib/sinatra-admin/views/show.haml +16 -0
- data/lib/sinatra-admin/warden_strategies/sinatra_admin.rb +27 -0
- data/sinatra-admin.gemspec +36 -0
- data/spec/sinatra-admin/app_spec.rb +15 -0
- data/spec/sinatra-admin/config_spec.rb +111 -0
- data/spec/sinatra-admin/models/admin_spec.rb +33 -0
- data/spec/sinatra-admin/register/base_spec.rb +13 -0
- data/spec/sinatra-admin/register/custom_spec.rb +40 -0
- data/spec/sinatra-admin/register/model_spec.rb +26 -0
- data/spec/sinatra-admin/register_spec.rb +15 -0
- data/spec/sinatra-admin/version_spec.rb +5 -0
- data/spec/sinatra-admin_spec.rb +73 -0
- data/spec/spec_helper.rb +81 -0
- metadata +343 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
.header
|
2
|
+
%h1="Login - SinatraAdmin #{SinatraAdmin::VERSION}"
|
3
|
+
|
4
|
+
.content
|
5
|
+
%div
|
6
|
+
%form{method: 'post', action: "/admin/login"}
|
7
|
+
%fieldset
|
8
|
+
%div
|
9
|
+
%label{for: "email"} Email
|
10
|
+
%input{id: "email", type: "text", value: params['data'].try(:[], 'email'), name: "data[email]"}
|
11
|
+
%div
|
12
|
+
%label{for: "password"} Password
|
13
|
+
%input{id: "password", type: "password", value: params['data'].try(:[], 'password'), name: "data[password]"}
|
14
|
+
=styled_flash
|
15
|
+
%div
|
16
|
+
%button{type: "submit", id: "login"} Login
|
@@ -0,0 +1,21 @@
|
|
1
|
+
.header
|
2
|
+
%h1="#{@model.to_s} - Edit"
|
3
|
+
|
4
|
+
.actions
|
5
|
+
%ul
|
6
|
+
%li
|
7
|
+
%a{href: "/admin/#{@route}"} Index
|
8
|
+
|
9
|
+
.content
|
10
|
+
%div
|
11
|
+
%form{method: 'post', action: "/admin/#{@route}/#{@resource.id}"}
|
12
|
+
%fieldset
|
13
|
+
%div
|
14
|
+
%input{name: "_method", type: "hidden", value: "put"}
|
15
|
+
- (@model.attribute_names - @model.protected_attributes.to_a).each do |attr|
|
16
|
+
%div
|
17
|
+
%label{for: "#{attr}"}= attr
|
18
|
+
%input{id: "#{attr}", type: "text", value: "#{@resource[attr]}", name: "data[#{attr}]"}
|
19
|
+
%span= @resource.errors[attr] if @resource.errors[attr].any?
|
20
|
+
%div
|
21
|
+
%button{type: "submit", id: "update"} Update
|
@@ -0,0 +1,34 @@
|
|
1
|
+
.header
|
2
|
+
%h1="#{@model.to_s.pluralize} - Index"
|
3
|
+
|
4
|
+
.actions
|
5
|
+
%ul
|
6
|
+
%li
|
7
|
+
%a{href: "/admin/#{@route}/new"} New
|
8
|
+
|
9
|
+
.content
|
10
|
+
%table
|
11
|
+
%thead
|
12
|
+
%tr
|
13
|
+
- @model.attribute_names.each do |attr|
|
14
|
+
%th= attr
|
15
|
+
%tbody
|
16
|
+
- if @collection.any?
|
17
|
+
- @collection.each do |record|
|
18
|
+
%tr
|
19
|
+
- @model.attribute_names.each do |attr|
|
20
|
+
- if attr.eql?('_id')
|
21
|
+
%td
|
22
|
+
%a{href: "#{@route}/#{record.send(attr)}"}
|
23
|
+
= record.send(attr)
|
24
|
+
- else
|
25
|
+
%td
|
26
|
+
= record.send(attr)
|
27
|
+
%td
|
28
|
+
%form{method: 'post', action: "/admin/#{@route}/#{record.id}"}
|
29
|
+
%input{name: "_method", type: "hidden", value: "delete"}
|
30
|
+
%button{type: "submit", id: "delete_#{record.id}"} Delete
|
31
|
+
- else
|
32
|
+
%tr
|
33
|
+
%td{colspan: "#{@model.attribute_names.size}"}
|
34
|
+
%h5 There are not records in the database
|
@@ -0,0 +1,19 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head>
|
4
|
+
%meta{charset: 'UTF=8'}
|
5
|
+
%title Sinatra Admin
|
6
|
+
|
7
|
+
%body
|
8
|
+
.top-bar{style: "height:30px;"}
|
9
|
+
.main-menu
|
10
|
+
%ul{style: "list-style-type:none;"}
|
11
|
+
- SinatraAdmin.config.routes.each do |route|
|
12
|
+
%li{style: "float:left; padding-right:15px;"}
|
13
|
+
%a{href: "#{route}"}= route.humanize
|
14
|
+
|
15
|
+
.logout{style: "float:right;"}
|
16
|
+
%a{href: "/admin/logout"} Logout
|
17
|
+
|
18
|
+
.wrapper
|
19
|
+
=yield
|
@@ -0,0 +1,19 @@
|
|
1
|
+
.header
|
2
|
+
%h1="#{@model.to_s} - New"
|
3
|
+
|
4
|
+
.actions
|
5
|
+
%ul
|
6
|
+
%li
|
7
|
+
%a{href: "/admin/#{@route}"} Index
|
8
|
+
|
9
|
+
.content
|
10
|
+
%div
|
11
|
+
%form{method: 'post', action: "/admin/#{@route}/"}
|
12
|
+
%fieldset
|
13
|
+
- (@model.attribute_names - @model.protected_attributes.to_a).each do |attr|
|
14
|
+
%div
|
15
|
+
%label{for: "#{attr}"}= attr
|
16
|
+
%input{id: "#{attr}", type: "text", value: "#{@resource[attr]}", name: "data[#{attr}]"}
|
17
|
+
%span= @resource.errors[attr] if @resource.errors[attr].any?
|
18
|
+
%div
|
19
|
+
%button{type: "submit", id: "create"} Create
|
@@ -0,0 +1,16 @@
|
|
1
|
+
.header
|
2
|
+
%h1="#{@model.to_s} - Show"
|
3
|
+
|
4
|
+
.actions
|
5
|
+
%ul
|
6
|
+
%li
|
7
|
+
%a{href: "/admin/#{@route}"} Index
|
8
|
+
%li
|
9
|
+
%a{href: "/admin/#{@route}/#{@resource.id}/edit"} Edit
|
10
|
+
|
11
|
+
.content
|
12
|
+
%div
|
13
|
+
- @model.attribute_names.each do |attr|
|
14
|
+
%div
|
15
|
+
%span= "#{attr}: "
|
16
|
+
%span= @resource.send(attr)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Warden::Strategies.add(:sinatra_admin) do
|
2
|
+
def valid?
|
3
|
+
email && password
|
4
|
+
end
|
5
|
+
|
6
|
+
def authenticate!
|
7
|
+
admin = SinatraAdmin.config.admin_model.find_by(email: email)
|
8
|
+
|
9
|
+
if admin.nil?
|
10
|
+
fail!("The email you entered does not exist.")
|
11
|
+
elsif admin.authenticate(password)
|
12
|
+
success!(admin)
|
13
|
+
else
|
14
|
+
fail!("You entered an incorrect password")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def email
|
21
|
+
params['data']['email']
|
22
|
+
end
|
23
|
+
|
24
|
+
def password
|
25
|
+
params['data']['password']
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sinatra-admin/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'sinatra-admin'
|
8
|
+
spec.version = SinatraAdmin::VERSION
|
9
|
+
spec.authors = ['Fco. Delgado', 'Carlo Cajucom', 'Vahak Matavosian']
|
10
|
+
spec.email = ['franciscodelgadodev@gmail.com']
|
11
|
+
spec.summary = "Sinatra application that allow us to have an admin dashboard with minimal effort."
|
12
|
+
spec.homepage = 'https://github.com/Herbalifedev/sinatra-admin'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
21
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
22
|
+
spec.add_development_dependency 'rspec', '~> 3.0.0'
|
23
|
+
spec.add_development_dependency 'capybara', '~> 2.4.1'
|
24
|
+
spec.add_development_dependency 'cucumber', '~> 1.3.16'
|
25
|
+
spec.add_development_dependency 'cucumber-sinatra', '~> 0.5.0'
|
26
|
+
spec.add_development_dependency 'database_cleaner'
|
27
|
+
|
28
|
+
spec.add_dependency 'mongoid', '~> 3.1.6'
|
29
|
+
spec.add_dependency 'sinatra', '~> 1.4.5'
|
30
|
+
spec.add_dependency 'sinatra-contrib', '~> 1.4.2'
|
31
|
+
spec.add_dependency 'sinatra-flash', '~> 0.3.0'
|
32
|
+
spec.add_dependency 'haml', '~> 4.0.5'
|
33
|
+
spec.add_dependency 'warden', '~> 1.2.3'
|
34
|
+
spec.add_dependency 'activesupport'
|
35
|
+
spec.add_dependency 'bcrypt'
|
36
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SinatraAdmin::App do
|
4
|
+
it 'inherits from Sinatra::Base' do
|
5
|
+
expect(described_class < Sinatra::Base).to eq(true)
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'registers Sinatra::Namespace' do
|
9
|
+
expect(described_class.extensions).to include(Sinatra::Namespace)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'adds Rack::MethodOverride middleware' do
|
13
|
+
expect(described_class.middleware[0]).to include(Rack::MethodOverride)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class User; end
|
4
|
+
|
5
|
+
describe SinatraAdmin::Config do
|
6
|
+
describe ':ATTRIBUTES' do
|
7
|
+
let(:expected_attrs) { [:root, :admin_model] }
|
8
|
+
it { expect(described_class::ATTRIBUTES).to eq(expected_attrs) }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#default_route' do
|
12
|
+
context 'when root is initialized' do
|
13
|
+
before do
|
14
|
+
subject.root = 'User'
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when defined root is not registered' do
|
18
|
+
it 'raises RegistrationException' do
|
19
|
+
expect {
|
20
|
+
subject.default_route
|
21
|
+
}.to raise_error(SinatraAdmin::RegistrationException, 'The resource User was not registered')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when defined root is registered' do
|
26
|
+
before do
|
27
|
+
subject.routes << 'users'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns /admin/users' do
|
31
|
+
expect(subject.default_route).to eq('/admin/users')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when root is not initialized' do
|
37
|
+
context 'when there are not registered routes' do
|
38
|
+
it 'raises RegistrationException' do
|
39
|
+
expect {
|
40
|
+
subject.default_route
|
41
|
+
}.to raise_error(SinatraAdmin::RegistrationException, 'You should register at least one resource')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when there are registered routes' do
|
46
|
+
before do
|
47
|
+
subject.routes << 'tags' << 'users'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns first registered route' do
|
51
|
+
expect(subject.default_route).to eq('/admin/tags')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#admin_model' do
|
58
|
+
context 'when model_name was initialized with User' do
|
59
|
+
before do
|
60
|
+
subject.admin_model = User
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'returns User' do
|
64
|
+
expect(subject.admin_model).to eq(User)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when model_name was not initialized' do
|
69
|
+
before do
|
70
|
+
subject.admin_model = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns SinatraAdmin::Admin' do
|
74
|
+
expect(subject.admin_model).to eq(SinatraAdmin::Admin)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#routes' do
|
80
|
+
context 'when there are not registered routes' do
|
81
|
+
it 'returns []' do
|
82
|
+
expect(subject.routes).to eq([])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when there are registered routes' do
|
87
|
+
before do
|
88
|
+
subject.routes << 'users'
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'returns array with routes' do
|
92
|
+
expect(subject.routes).to eq(['users'])
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#reset!' do
|
98
|
+
it 'sets nil to ALL config attributes' do
|
99
|
+
described_class::ATTRIBUTES.each do |attr|
|
100
|
+
expect(subject).to receive("#{attr.to_s}=").with(nil)
|
101
|
+
end
|
102
|
+
subject.reset!
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'clears routes array' do
|
106
|
+
subject.routes << 'users'
|
107
|
+
subject.reset!
|
108
|
+
expect(subject.routes).to eq([])
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SinatraAdmin::Admin do
|
4
|
+
subject do
|
5
|
+
SinatraAdmin::Admin.new(email: "admin@mail.com", password: "admin")
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'includes Mongoid::Document' do
|
9
|
+
expect(described_class < Mongoid::Document).to eq(true)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'hashes password' do
|
13
|
+
expect(subject.password).not_to eql('admin')
|
14
|
+
expect(subject.password == 'admin').to eq(true)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'validations' do
|
18
|
+
[:email, :password_hash].each do |required_attr|
|
19
|
+
it "#{required_attr} is required" do
|
20
|
+
subject.send("#{required_attr}=", nil)
|
21
|
+
subject.valid?
|
22
|
+
expect(subject.errors[required_attr]).to include("can't be blank")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'email should be valid format' do
|
27
|
+
subject.email = 'invalid'
|
28
|
+
subject.valid?
|
29
|
+
expect(subject.errors[:email]).to include("is not an email")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SinatraAdmin::Register::Base do
|
4
|
+
before { SinatraAdmin.config.reset! }
|
5
|
+
|
6
|
+
describe '.add' do
|
7
|
+
it 'raises exception(this is an abstract class)' do
|
8
|
+
expect{
|
9
|
+
described_class.add 'Tag'
|
10
|
+
}.to raise_error(NotImplementedError, "You must implement me!")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SinatraAdmin::Register::Custom do
|
4
|
+
before { SinatraAdmin.config.reset! }
|
5
|
+
|
6
|
+
describe '.add' do
|
7
|
+
context 'when block is given' do
|
8
|
+
context 'when resource has been registered already' do
|
9
|
+
before do
|
10
|
+
described_class.add 'Custom Page' do
|
11
|
+
get '/' do
|
12
|
+
puts "In tag custom page"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'raises RegistrationException' do
|
18
|
+
expect{
|
19
|
+
SinatraAdmin.register 'Custom Page' do
|
20
|
+
get '/' do
|
21
|
+
puts "In tag custom page"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
}.to raise_error(SinatraAdmin::RegistrationException, "The resource Custom Page is already registered")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when resource has not been registered yet' do
|
29
|
+
it 'adds route to config' do
|
30
|
+
described_class.add 'Custom Page' do
|
31
|
+
get '/' do
|
32
|
+
puts "In tag custom page"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
expect(SinatraAdmin.config.routes).to include('custom_pages')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Tag; end
|
4
|
+
|
5
|
+
describe SinatraAdmin::Register::Model do
|
6
|
+
before { SinatraAdmin.config.reset! }
|
7
|
+
|
8
|
+
describe '.add' do
|
9
|
+
context 'when resource has been registered already' do
|
10
|
+
before { described_class.add 'Tag' }
|
11
|
+
|
12
|
+
it 'raises RegistrationException' do
|
13
|
+
expect{
|
14
|
+
SinatraAdmin.register 'Tag'
|
15
|
+
}.to raise_error(SinatraAdmin::RegistrationException, "The resource Tag is already registered")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when resource has not been registered yet' do
|
20
|
+
it 'adds route to config' do
|
21
|
+
described_class.add 'Tag'
|
22
|
+
expect(SinatraAdmin.config.routes).to include('tags')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|