ditty 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.pryrc +6 -0
- data/.rspec +2 -0
- data/.rubocop.yml +15 -0
- data/.travis.yml +15 -0
- data/Gemfile.ci +19 -0
- data/License.txt +7 -0
- data/Rakefile +10 -0
- data/Readme.md +67 -0
- data/config.ru +33 -0
- data/ditty.gemspec +46 -0
- data/lib/ditty/components/app.rb +78 -0
- data/lib/ditty/controllers/application.rb +79 -0
- data/lib/ditty/controllers/audit_logs.rb +44 -0
- data/lib/ditty/controllers/component.rb +161 -0
- data/lib/ditty/controllers/main.rb +86 -0
- data/lib/ditty/controllers/roles.rb +16 -0
- data/lib/ditty/controllers/users.rb +183 -0
- data/lib/ditty/db.rb +12 -0
- data/lib/ditty/helpers/authentication.rb +58 -0
- data/lib/ditty/helpers/component.rb +63 -0
- data/lib/ditty/helpers/pundit.rb +34 -0
- data/lib/ditty/helpers/views.rb +50 -0
- data/lib/ditty/helpers/wisper.rb +14 -0
- data/lib/ditty/listener.rb +23 -0
- data/lib/ditty/models/audit_log.rb +14 -0
- data/lib/ditty/models/base.rb +7 -0
- data/lib/ditty/models/identity.rb +70 -0
- data/lib/ditty/models/role.rb +16 -0
- data/lib/ditty/models/user.rb +63 -0
- data/lib/ditty/policies/application_policy.rb +21 -0
- data/lib/ditty/policies/audit_log_policy.rb +41 -0
- data/lib/ditty/policies/identity_policy.rb +25 -0
- data/lib/ditty/policies/role_policy.rb +41 -0
- data/lib/ditty/policies/user_policy.rb +47 -0
- data/lib/ditty/rake_tasks.rb +85 -0
- data/lib/ditty/seed.rb +1 -0
- data/lib/ditty/services/logger.rb +48 -0
- data/lib/ditty/version.rb +5 -0
- data/lib/ditty.rb +142 -0
- data/migrate/20170207_base_tables.rb +40 -0
- data/migrate/20170208_audit_log.rb +12 -0
- data/migrate/20170416_audit_log_details.rb +9 -0
- data/public/browserconfig.xml +9 -0
- data/public/images/apple-icon.png +0 -0
- data/public/images/favicon-16x16.png +0 -0
- data/public/images/favicon-32x32.png +0 -0
- data/public/images/launcher-icon-1x.png +0 -0
- data/public/images/launcher-icon-2x.png +0 -0
- data/public/images/launcher-icon-4x.png +0 -0
- data/public/images/mstile-150x150.png +0 -0
- data/public/images/safari-pinned-tab.svg +43 -0
- data/public/manifest.json +25 -0
- data/views/404.haml +7 -0
- data/views/audit_logs/index.haml +30 -0
- data/views/error.haml +4 -0
- data/views/identity/login.haml +19 -0
- data/views/identity/register.haml +14 -0
- data/views/index.haml +1 -0
- data/views/layout.haml +55 -0
- data/views/partials/delete_form.haml +4 -0
- data/views/partials/footer.haml +5 -0
- data/views/partials/form_control.haml +20 -0
- data/views/partials/navbar.haml +24 -0
- data/views/partials/notifications.haml +24 -0
- data/views/partials/pager.haml +14 -0
- data/views/partials/sidebar.haml +35 -0
- data/views/roles/display.haml +18 -0
- data/views/roles/edit.haml +11 -0
- data/views/roles/form.haml +1 -0
- data/views/roles/index.haml +22 -0
- data/views/roles/new.haml +10 -0
- data/views/users/display.haml +50 -0
- data/views/users/edit.haml +11 -0
- data/views/users/identity.haml +3 -0
- data/views/users/index.haml +23 -0
- data/views/users/new.haml +11 -0
- data/views/users/profile.haml +39 -0
- data/views/users/user.haml +3 -0
- metadata +431 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9f88c10fb42b6cc8dd793998baaaf909a4dc6c0a
|
4
|
+
data.tar.gz: 5a72a520051492ee1a769cdeef56d943bbda6c55
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2ba59d3510c55c01ca48b49cc1f601b2dcd2f4872670fedade8a2d0d8a8f36cc42382f05361f1cda1cd7646f2d3af71f9f105f4230bc5c9a56051e15b7a38817
|
7
|
+
data.tar.gz: ae4ea9466221bd370ce5de8b1e19993804c77c16f9070ca323ece9e6f8ebe4ba578ad207ec51741115303672fcb4006d346bea9b1e5c2a3a0ec5c49dd8e24838
|
data/.gitignore
ADDED
data/.pryrc
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 2.4.0
|
5
|
+
- 2.3.3
|
6
|
+
- 2.2.6
|
7
|
+
gemfile: Gemfile.ci
|
8
|
+
env:
|
9
|
+
- DATABASE_URL="sqlite::memory:" RACK_ENV=test
|
10
|
+
before_install: gem install bundler -v 1.12.5
|
11
|
+
addons:
|
12
|
+
code_climate:
|
13
|
+
repo_token: 289860573c6284a8e277de86848caba84d840be49e35f3601bcd672ab40d1e35
|
14
|
+
after_success:
|
15
|
+
- bundle exec codeclimate-test-reporter
|
data/Gemfile.ci
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
source 'https://rubygems.org'
|
3
|
+
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'rubocop'
|
7
|
+
gem 'simplecov', '~> 0.13.0'
|
8
|
+
gem 'sqlite3'
|
9
|
+
|
10
|
+
if RUBY_VERSION < '2.1'
|
11
|
+
gem 'sidekiq', '3.0.0'
|
12
|
+
gem 'activesupport', '<4.0.0'
|
13
|
+
gem 'omniauth', '~>1.4.2'
|
14
|
+
elsif RUBY_VERSION < '2.2.0'
|
15
|
+
gem 'sidekiq', '4.0.0'
|
16
|
+
gem 'activesupport', '<5.0.0'
|
17
|
+
else
|
18
|
+
gem 'activesupport'
|
19
|
+
end
|
data/License.txt
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2017 Jade IT cc
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/Readme.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/EagerELK/ditty.svg?branch=master)](https://travis-ci.org/EagerELK/ditty)
|
2
|
+
[![Code Climate](https://codeclimate.com/github/EagerELK/ditty/badges/gpa.svg)](https://codeclimate.com/github/EagerELK/ditty)
|
3
|
+
[![Test Coverage](https://codeclimate.com/github/EagerELK/ditty/badges/coverage.svg)](https://codeclimate.com/github/EagerELK/ditty/coverage)
|
4
|
+
|
5
|
+
# Ditty
|
6
|
+
|
7
|
+
Ditty provides an extra layer of functionality on top of [Sinatra](http://sinatrarb.com/) to give structure and basic tools to an already great framework. You can get a new application, with user authentication and basic CRUD / REST interfaces, up and running in minutes.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add these lines to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'ditty'
|
15
|
+
gem 'sqlite3'
|
16
|
+
```
|
17
|
+
|
18
|
+
You can replace `sqlite3` with a DB adapter of your choice.
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
```bash
|
23
|
+
bundle install
|
24
|
+
```
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
```bash
|
29
|
+
gem install ditty
|
30
|
+
```
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
1. Add the components to your rack config file. See the included [`config.ru`](https://github.com/EagerELK/ditty/blob/master/config.ru) file for an example setup
|
35
|
+
2. Add the Ditty rake tasks to your Rakefile: `require 'ditty/rake_tasks'`
|
36
|
+
3. Set the DB connection as the `DATABASE_URL` ENV variable: `DATABASE_URL=sqlite://development.db`
|
37
|
+
4. Create and populate the DB and secret tokens:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
bundle exec rake proxes:prep
|
41
|
+
bundle exec rake proxes:generate_tokens
|
42
|
+
bundle exec rake proxes:migrate
|
43
|
+
bundle exec rake proxes:seed
|
44
|
+
```
|
45
|
+
|
46
|
+
4. Start up the web app: `bundle exec rackup`
|
47
|
+
|
48
|
+
## Components
|
49
|
+
|
50
|
+
The application can now be further extended by creating components.
|
51
|
+
|
52
|
+
## Development
|
53
|
+
|
54
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
55
|
+
|
56
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
57
|
+
|
58
|
+
|
59
|
+
## Contributing
|
60
|
+
|
61
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/EagerELK/ditty.
|
62
|
+
|
63
|
+
## License
|
64
|
+
|
65
|
+
The Ditty gem is an Open Source project licensed under the terms of
|
66
|
+
the MIT license. Please see [MIT license](License.txt)
|
67
|
+
for license text.
|
data/config.ru
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack/protection'
|
4
|
+
use Rack::Session::Cookie,
|
5
|
+
key: '_Ditty_session',
|
6
|
+
# :secure=>!TEST_MODE, # Uncomment if only allowing https:// access
|
7
|
+
secret: File.read('.session_secret')
|
8
|
+
use Rack::Protection::RemoteToken
|
9
|
+
use Rack::Protection::SessionHijacking
|
10
|
+
|
11
|
+
require 'ditty/components/app'
|
12
|
+
Ditty.component :app
|
13
|
+
|
14
|
+
require 'omniauth'
|
15
|
+
require 'omniauth/identity'
|
16
|
+
OmniAuth.config.logger = Ditty::Services::Logger.instance
|
17
|
+
OmniAuth.config.on_failure = proc { |env|
|
18
|
+
OmniAuth::FailureEndpoint.new(env).redirect_to_failure
|
19
|
+
}
|
20
|
+
require 'ditty/controllers/main'
|
21
|
+
require 'ditty/models/identity'
|
22
|
+
use OmniAuth::Builder do
|
23
|
+
# The identity provider is used by the App.
|
24
|
+
provider :identity,
|
25
|
+
fields: [:username],
|
26
|
+
callback_path: '/auth/identity/callback',
|
27
|
+
model: Ditty::Identity,
|
28
|
+
on_login: Ditty::Main,
|
29
|
+
on_registration: Ditty::Main,
|
30
|
+
locate_conditions: ->(req) { { username: req['username'] } }
|
31
|
+
end
|
32
|
+
|
33
|
+
run Rack::URLMap.new Ditty::Components.routes
|
data/ditty.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'ditty/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'ditty'
|
9
|
+
spec.version = Ditty::VERSION
|
10
|
+
spec.authors = ['Jurgens du Toit']
|
11
|
+
spec.email = ['jrgns@jadeit.co.za']
|
12
|
+
|
13
|
+
spec.summary = 'Sinatra Based Application Framework'
|
14
|
+
spec.description = 'Sinatra Based Application Framework'
|
15
|
+
spec.homepage = 'https://github.com/eagerelk/ditty'
|
16
|
+
spec.license = 'LGPLv3'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = 'exe'
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.12'
|
24
|
+
spec.add_development_dependency 'database_cleaner'
|
25
|
+
spec.add_development_dependency 'factory_girl'
|
26
|
+
spec.add_development_dependency 'rack-test'
|
27
|
+
spec.add_development_dependency 'racksh'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
spec.add_development_dependency 'timecop'
|
30
|
+
|
31
|
+
spec.add_dependency 'activesupport', '>= 3'
|
32
|
+
spec.add_dependency 'bcrypt', '~> 3.0'
|
33
|
+
spec.add_dependency 'haml', '~> 5.0'
|
34
|
+
spec.add_dependency 'logger', '~> 1.0'
|
35
|
+
spec.add_dependency 'omniauth', '~> 1.0'
|
36
|
+
spec.add_dependency 'omniauth-identity', '~> 1.0'
|
37
|
+
spec.add_dependency 'pundit', '~> 1.0'
|
38
|
+
spec.add_dependency 'rack-contrib', '~> 1.0'
|
39
|
+
spec.add_dependency 'rake', '~> 12.0'
|
40
|
+
spec.add_dependency 'sequel', '~> 4.0'
|
41
|
+
spec.add_dependency 'sinatra', '~> 2.0'
|
42
|
+
spec.add_dependency 'sinatra-contrib', '~> 2.0'
|
43
|
+
spec.add_dependency 'sinatra-flash', '~> 0.3'
|
44
|
+
spec.add_dependency 'tilt', '>= 2'
|
45
|
+
spec.add_dependency 'wisper', '~> 2.0'
|
46
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ditty'
|
4
|
+
|
5
|
+
module Ditty
|
6
|
+
class App
|
7
|
+
def self.load_models
|
8
|
+
require 'ditty/models/user'
|
9
|
+
require 'ditty/models/role'
|
10
|
+
require 'ditty/models/identity'
|
11
|
+
require 'ditty/models/audit_log'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.configure(_container)
|
15
|
+
require 'ditty/db' unless defined? ::DB
|
16
|
+
Sequel::Model.plugin :auto_validations
|
17
|
+
Sequel::Model.plugin :update_or_create
|
18
|
+
Sequel::Model.plugin :timestamps, update_on_create: true
|
19
|
+
|
20
|
+
DB.extension(:pagination)
|
21
|
+
require 'ditty/listener'
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.migrations
|
25
|
+
File.expand_path('../../../../migrate', __FILE__)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.view_folder
|
29
|
+
File.expand_path('../../../../views', __FILE__)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.routes
|
33
|
+
controllers = File.expand_path('../../controllers', __FILE__)
|
34
|
+
Dir.glob("#{controllers}/*.rb").each { |f| require f }
|
35
|
+
{
|
36
|
+
'/' => ::Ditty::Main,
|
37
|
+
'/users' => ::Ditty::Users,
|
38
|
+
'/roles' => ::Ditty::Roles,
|
39
|
+
'/audit-logs' => ::Ditty::AuditLogs
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.navigation
|
44
|
+
load_models
|
45
|
+
|
46
|
+
[
|
47
|
+
{
|
48
|
+
group: 'User Management',
|
49
|
+
order: 10,
|
50
|
+
icon: 'lock',
|
51
|
+
items: [
|
52
|
+
{ order: 10, link: '/users/', text: 'Users', target: ::Ditty::User, icon: 'user' },
|
53
|
+
{ order: 20, link: '/roles/', text: 'Roles', target: ::Ditty::Role, icon: 'check-square' },
|
54
|
+
{ order: 30, link: '/audit-logs/', text: 'Audit Logs', target: ::Ditty::AuditLog, icon: 'history' }
|
55
|
+
]
|
56
|
+
}
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.seeder
|
61
|
+
proc do
|
62
|
+
load_models
|
63
|
+
|
64
|
+
::Ditty::Role.find_or_create(name: 'super_admin')
|
65
|
+
::Ditty::Role.find_or_create(name: 'admin')
|
66
|
+
user_role = ::Ditty::Role.find_or_create(name: 'user')
|
67
|
+
|
68
|
+
# Anonymous User
|
69
|
+
anon = ::Ditty::User.find_or_create(email: 'anonymous@ditty.io')
|
70
|
+
anon.remove_role user_role
|
71
|
+
anon_role = ::Ditty::Role.find_or_create(name: 'anonymous')
|
72
|
+
anon.add_role anon_role unless anon.role?('anonymous')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Ditty::Components.register_component(:app, Ditty::App)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'wisper'
|
4
|
+
require 'sinatra/base'
|
5
|
+
require 'sinatra/flash'
|
6
|
+
require 'sinatra/respond_with'
|
7
|
+
require 'ditty/helpers/views'
|
8
|
+
require 'ditty/helpers/pundit'
|
9
|
+
require 'ditty/helpers/wisper'
|
10
|
+
require 'ditty/helpers/authentication'
|
11
|
+
require 'ditty/services/logger'
|
12
|
+
require 'active_support'
|
13
|
+
require 'active_support/inflector'
|
14
|
+
require 'rack/contrib'
|
15
|
+
|
16
|
+
module Ditty
|
17
|
+
class Application < Sinatra::Base
|
18
|
+
include ActiveSupport::Inflector
|
19
|
+
|
20
|
+
set :root, ENV['APP_ROOT'] || ::File.expand_path(::File.dirname(__FILE__) + '/../../../')
|
21
|
+
set :map_path, nil
|
22
|
+
set :view_location, nil
|
23
|
+
set :model_class, nil
|
24
|
+
# The order here is important, since Wisper has a deprecated method respond_with method
|
25
|
+
helpers Wisper::Publisher, Helpers::Wisper
|
26
|
+
helpers Helpers::Pundit, Helpers::Views, Helpers::Authentication
|
27
|
+
|
28
|
+
register Sinatra::Flash, Sinatra::RespondWith
|
29
|
+
|
30
|
+
use Rack::PostBodyContentTypeParser
|
31
|
+
use Rack::MethodOverride
|
32
|
+
|
33
|
+
def view_location
|
34
|
+
return settings.view_location if settings.view_location
|
35
|
+
return underscore(pluralize(demodulize(settings.model_class))) if settings.model_class
|
36
|
+
underscore(demodulize(self.class))
|
37
|
+
end
|
38
|
+
|
39
|
+
configure :production do
|
40
|
+
disable :show_exceptions
|
41
|
+
end
|
42
|
+
|
43
|
+
configure :development do
|
44
|
+
set :show_exceptions, :after_handler
|
45
|
+
end
|
46
|
+
|
47
|
+
configure :production, :development do
|
48
|
+
enable :logging
|
49
|
+
# use Rack::CommonLogger, Ditty::Services::Logger.instance
|
50
|
+
end
|
51
|
+
|
52
|
+
not_found do
|
53
|
+
haml :'404', locals: { title: '4 oh 4' }
|
54
|
+
end
|
55
|
+
|
56
|
+
error do
|
57
|
+
error = env['sinatra.error']
|
58
|
+
haml :error, locals: { title: 'Something went wrong', error: error }
|
59
|
+
end
|
60
|
+
|
61
|
+
error Helpers::NotAuthenticated do
|
62
|
+
flash[:warning] = 'Please log in first.'
|
63
|
+
redirect "#{settings.map_path}/auth/identity"
|
64
|
+
end
|
65
|
+
|
66
|
+
error ::Pundit::NotAuthorizedError do
|
67
|
+
flash[:warning] = 'Please log in first.'
|
68
|
+
redirect "#{settings.map_path}/auth/identity"
|
69
|
+
end
|
70
|
+
|
71
|
+
before(/.*/) do
|
72
|
+
::Ditty::Services::Logger.instance.debug "Running with #{self.class}"
|
73
|
+
if request.url =~ /.json/
|
74
|
+
request.accept.unshift('application/json')
|
75
|
+
request.path_info = request.path_info.gsub(/.json/, '')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ditty/controllers/component'
|
4
|
+
require 'ditty/models/audit_log'
|
5
|
+
require 'ditty/policies/audit_log_policy'
|
6
|
+
|
7
|
+
module Ditty
|
8
|
+
class AuditLogs < Ditty::Component
|
9
|
+
set model_class: AuditLog
|
10
|
+
|
11
|
+
def find_template(views, name, engine, &block)
|
12
|
+
super(views, name, engine, &block) # Root
|
13
|
+
super(::Ditty::App.view_folder, name, engine, &block) # Ditty
|
14
|
+
end
|
15
|
+
|
16
|
+
def list
|
17
|
+
super.order(:created_at).reverse
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/new' do
|
21
|
+
halt 404
|
22
|
+
end
|
23
|
+
|
24
|
+
post '/' do
|
25
|
+
halt 404
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/:id' do
|
29
|
+
halt 404
|
30
|
+
end
|
31
|
+
|
32
|
+
get '/:id/edit' do
|
33
|
+
halt 404
|
34
|
+
end
|
35
|
+
|
36
|
+
put '/:id' do
|
37
|
+
halt 404
|
38
|
+
end
|
39
|
+
|
40
|
+
delete '/:id' do
|
41
|
+
halt 404
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ditty/controllers/application'
|
4
|
+
require 'ditty/helpers/component'
|
5
|
+
require 'sinatra/json'
|
6
|
+
|
7
|
+
module Ditty
|
8
|
+
class Component < Application
|
9
|
+
helpers Helpers::Component
|
10
|
+
|
11
|
+
set base_path: nil
|
12
|
+
set dehumanized: nil
|
13
|
+
set view_location: nil
|
14
|
+
set track_actions: false
|
15
|
+
|
16
|
+
# List
|
17
|
+
get '/', provides: %i[html json] do
|
18
|
+
authorize settings.model_class, :list
|
19
|
+
|
20
|
+
actions = {}
|
21
|
+
actions["#{base_path}/new"] = "New #{heading}" if policy(settings.model_class).create?
|
22
|
+
|
23
|
+
log_action("#{dehumanized}_list".to_sym) if settings.track_actions
|
24
|
+
respond_to do |format|
|
25
|
+
format.html do
|
26
|
+
haml :"#{view_location}/index",
|
27
|
+
locals: { list: list, title: heading(:list), actions: actions }
|
28
|
+
end
|
29
|
+
format.json do
|
30
|
+
# TODO: Add links defined by actions (New #{heading})
|
31
|
+
json(
|
32
|
+
'items' => list.map(&:for_json),
|
33
|
+
'page' => params[:page],
|
34
|
+
'count' => list.count,
|
35
|
+
'total' => dataset.count
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create Form
|
42
|
+
get '/new' do
|
43
|
+
authorize settings.model_class, :create
|
44
|
+
|
45
|
+
entity = settings.model_class.new(permitted_attributes(settings.model_class, :create))
|
46
|
+
haml :"#{view_location}/new", locals: { entity: entity, title: heading(:new) }
|
47
|
+
end
|
48
|
+
|
49
|
+
# Create
|
50
|
+
post '/' do
|
51
|
+
authorize settings.model_class, :create
|
52
|
+
entity = settings.model_class.new(permitted_attributes(settings.model_class, :create))
|
53
|
+
success = entity.valid? && entity.save
|
54
|
+
|
55
|
+
log_action("#{dehumanized}_create".to_sym) if success && settings.track_actions
|
56
|
+
respond_to do |format|
|
57
|
+
format.html do
|
58
|
+
if success
|
59
|
+
flash[:success] = "#{heading} Created"
|
60
|
+
redirect "#{base_path}/#{entity.id}"
|
61
|
+
else
|
62
|
+
haml :"#{view_location}/new", locals: { entity: entity, title: heading(:new) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
format.json do
|
66
|
+
headers 'Content-Type' => 'application/json'
|
67
|
+
if success
|
68
|
+
redirect "#{base_path}/#{entity.id}", 201
|
69
|
+
else
|
70
|
+
400
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Read
|
77
|
+
get '/:id' do |id|
|
78
|
+
entity = dataset.first(settings.model_class.primary_key => id)
|
79
|
+
halt 404 unless entity
|
80
|
+
authorize entity, :read
|
81
|
+
|
82
|
+
actions = {}
|
83
|
+
actions["#{base_path}/#{entity.id}/edit"] = "Edit #{heading}" if policy(entity).update?
|
84
|
+
|
85
|
+
log_action("#{dehumanized}_read".to_sym) if settings.track_actions
|
86
|
+
respond_to do |format|
|
87
|
+
format.html do
|
88
|
+
haml :"#{view_location}/display",
|
89
|
+
locals: { entity: entity, title: heading, actions: actions }
|
90
|
+
end
|
91
|
+
format.json do
|
92
|
+
# TODO: Add links defined by actions (Edit #{heading})
|
93
|
+
json entity.for_json
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Update Form
|
99
|
+
get '/:id/edit' do |id|
|
100
|
+
entity = dataset.first(settings.model_class.primary_key => id)
|
101
|
+
halt 404 unless entity
|
102
|
+
authorize entity, :update
|
103
|
+
|
104
|
+
haml :"#{view_location}/edit", locals: { entity: entity, title: heading(:edit) }
|
105
|
+
end
|
106
|
+
|
107
|
+
# Update
|
108
|
+
put '/:id' do |id|
|
109
|
+
entity = dataset.first(settings.model_class.primary_key => id)
|
110
|
+
halt 404 unless entity
|
111
|
+
authorize entity, :update
|
112
|
+
|
113
|
+
entity.set(permitted_attributes(settings.model_class, :update))
|
114
|
+
|
115
|
+
success = entity.valid? && entity.save
|
116
|
+
log_action("#{dehumanized}_update".to_sym) if success && settings.track_actions
|
117
|
+
if success
|
118
|
+
respond_to do |format|
|
119
|
+
format.html do
|
120
|
+
flash[:success] = "#{heading} Updated"
|
121
|
+
redirect "#{base_path}/#{entity.id}"
|
122
|
+
end
|
123
|
+
format.json do
|
124
|
+
headers 'Location' => "#{base_path}/#{entity.id}"
|
125
|
+
json body entity.for_json
|
126
|
+
end
|
127
|
+
end
|
128
|
+
else
|
129
|
+
respond_to do |format|
|
130
|
+
format.html do
|
131
|
+
haml :"#{view_location}/edit", locals: { entity: entity, title: heading(:edit) }
|
132
|
+
end
|
133
|
+
format.json do
|
134
|
+
400
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
delete '/:id' do |id|
|
141
|
+
entity = dataset.first(settings.model_class.primary_key => id)
|
142
|
+
halt 404 unless entity
|
143
|
+
authorize entity, :delete
|
144
|
+
|
145
|
+
entity.destroy
|
146
|
+
|
147
|
+
log_action("#{dehumanized}_delete".to_sym) if settings.track_actions
|
148
|
+
respond_to do |format|
|
149
|
+
format.html do
|
150
|
+
flash[:success] = "#{heading} Deleted"
|
151
|
+
redirect base_path.to_s
|
152
|
+
end
|
153
|
+
format.json do
|
154
|
+
content_type 'application/json'
|
155
|
+
headers 'Location' => '/users'
|
156
|
+
status 204
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|