ditty 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2fea055fdddd9b21951527617015b2926b064191dfda753fd930037738122c50
4
- data.tar.gz: b8d1cc9f9430ff39d3f345719a0988f51bc03f4a7f718fabe78a69d314e5762a
3
+ metadata.gz: a6c4bdcc080384afbc372c0811697ff421ab6ed3d552ec912cd0a3c0f7ed82da
4
+ data.tar.gz: 1f09c726e251d380cd0b55d8c552b064dae7c6811f3a24b0e16a38e55a0c6408
5
5
  SHA512:
6
- metadata.gz: eafbcf7aaadabc7e9458f403cf2252b3311dc9a716f08884c1651055426864e796866e60f7c3986be4405cc83d3d0552d49fca43e2764e516066884a56e7c4ea
7
- data.tar.gz: f2f3b62750d7711bdd59ee7e0df4630ec89992cda013d5e5e96736aa541a5a2cd55ff096de65b774bef4ab74c90e0983a7a3826d30a498e3a3a474bd1cdfc1cf
6
+ metadata.gz: d0f86ea3db1913ad4c603a6ba96c535705589fd7fbd28ea3fbe3a262d956838fdc2c27e1950e6aae2f6e1bce20e3ffd0dc32e0bde74ae371869c3ac1922a5b03
7
+ data.tar.gz: ad5ea5fe818c8f8d20177082f11ecfeb176b4969b21cb5155410a4f8dbbc581dde05d3cb4c468898a86dd16a8aede7f487f3fd36b60e187df094280f805e1c8e
data/.travis.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.4.0
5
- - 2.3.3
6
- - 2.2.6
4
+ - 2.5.1
5
+ - 2.4.0
6
+ - 2.3.3
7
+ - 2.2.6
7
8
  gemfile: Gemfile.ci
8
9
  env:
9
10
  global:
10
11
  - CC_TEST_REPORTER_ID=289860573c6284a8e277de86848caba84d840be49e35f3601bcd672ab40d1e35
12
+ - DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL=true
11
13
  matrix:
12
14
  - DATABASE_URL="sqlite::memory:" RACK_ENV=test
13
15
  before_install:
data/ditty.gemspec CHANGED
@@ -32,13 +32,14 @@ Gem::Specification.new do |spec|
32
32
  spec.add_dependency 'bcrypt', '~> 3.1'
33
33
  spec.add_dependency 'haml', '~> 5.0'
34
34
  spec.add_dependency 'logger', '~> 1.0'
35
+ spec.add_dependency 'oga', '>= 2.14'
35
36
  spec.add_dependency 'omniauth', '~> 1.0'
36
37
  spec.add_dependency 'omniauth-identity', '~> 1.0'
37
38
  spec.add_dependency 'pundit', '~> 1.0'
38
39
  spec.add_dependency 'rack-contrib', '~> 1.0'
39
40
  spec.add_dependency 'rake', '~> 12.0'
40
- spec.add_dependency 'sequel', '~> 4.0'
41
- spec.add_dependency 'sinatra', '~> 2.0'
41
+ spec.add_dependency 'sequel', '>= 4.0'
42
+ spec.add_dependency 'sinatra', '>= 2.0'
42
43
  spec.add_dependency 'sinatra-contrib', '~> 2.0'
43
44
  spec.add_dependency 'sinatra-flash', '~> 0.3'
44
45
  spec.add_dependency 'tilt', '>= 2'
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'wisper'
4
+ require 'oga'
4
5
  require 'sinatra/base'
5
6
  require 'sinatra/flash'
6
7
  require 'sinatra/respond_with'
@@ -30,10 +31,16 @@ module Ditty
30
31
  use Rack::PostBodyContentTypeParser
31
32
  use Rack::MethodOverride
32
33
 
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))
34
+ helpers do
35
+ def base_path
36
+ settings.base_path || "#{settings.map_path}/#{dasherize(view_location)}"
37
+ end
38
+
39
+ def view_location
40
+ return settings.view_location if settings.view_location
41
+ return underscore(pluralize(demodulize(settings.model_class))) if settings.model_class
42
+ underscore(demodulize(self.class))
43
+ end
37
44
  end
38
45
 
39
46
  configure :production do
@@ -54,7 +61,7 @@ module Ditty
54
61
  respond_to do |format|
55
62
  status 404
56
63
  format.html do
57
- haml :'404', locals: { title: '4 oh 4' }
64
+ haml :'404', locals: { title: '4 oh 4' }, layout: layout
58
65
  end
59
66
  format.json do
60
67
  json code: 404, errors: ['Not Found']
@@ -67,7 +74,7 @@ module Ditty
67
74
  status 401
68
75
  format.html do
69
76
  flash[:warning] = 'Please log in first.'
70
- redirect "#{settings.map_path}/auth/identity"
77
+ redirect with_layout("#{settings.map_path}/auth/identity")
71
78
  end
72
79
  format.json do
73
80
  json code: 401, errors: ['Not Authenticated']
@@ -82,10 +89,10 @@ module Ditty
82
89
  status 400
83
90
  format.html do
84
91
  action = entity.id ? :edit : :new
85
- haml :"#{view_location}/#{action}", locals: { entity: entity, title: heading(action) }
92
+ haml :"#{view_location}/#{action}", locals: { entity: entity, title: heading(action) }, layout: layout
86
93
  end
87
94
  format.json do
88
- json code: 400, errors: errors
95
+ json code: 400, errors: errors, full_errors: errors.full_messages
89
96
  end
90
97
  end
91
98
  end
@@ -97,7 +104,7 @@ module Ditty
97
104
  respond_to do |format|
98
105
  status 400
99
106
  format.html do
100
- haml :error, locals: { title: 'Something went wrong', error: error }
107
+ haml :error, locals: { title: 'Something went wrong', error: error }, layout: layout
101
108
  end
102
109
  format.json do
103
110
  json code: 400, errors: ['Invalid Relation Specified']
@@ -112,7 +119,7 @@ module Ditty
112
119
  respond_to do |format|
113
120
  status 500
114
121
  format.html do
115
- haml :error, locals: { title: 'Something went wrong', error: error }
122
+ haml :error, locals: { title: 'Something went wrong', error: error }, layout: layout
116
123
  end
117
124
  format.json do
118
125
  json code: 500, errors: ['Something went wrong']
@@ -124,9 +131,23 @@ module Ditty
124
131
  ::Ditty::Services::Logger.instance.debug "Running with #{self.class}"
125
132
  if request.path =~ /.*\.json\Z/
126
133
  content_type :json
134
+ request.path_info = request.path_info.gsub(/.json$/,'')
127
135
  end
128
136
  # Ensure the accept header is set. People forget to include it in API requests
129
137
  content_type(:json) if request.accept.count.eql?(1) && request.accept.first.to_s.eql?('*/*')
130
138
  end
139
+
140
+ after do
141
+ return if params['layout'].nil?
142
+ response.body = response.body.map do |resp|
143
+ document = Oga.parse_html(resp)
144
+ document.css('a').each do |elm|
145
+ unless (href = elm.get('href')).nil?
146
+ elm.set 'href', with_layout(href)
147
+ end
148
+ end
149
+ document.to_xml
150
+ end
151
+ end
131
152
  end
132
153
  end
@@ -33,7 +33,10 @@ module Ditty
33
33
  authorize settings.model_class, :create
34
34
 
35
35
  entity = settings.model_class.new(permitted_attributes(settings.model_class, :create))
36
- haml :"#{view_location}/new", locals: { entity: entity, title: heading(:new) }
36
+ session[:redirect_to] = request.fullpath
37
+ haml :"#{view_location}/new",
38
+ locals: { entity: entity, title: heading(:new) },
39
+ layout: layout
37
40
  end
38
41
 
39
42
  # Create
@@ -63,7 +66,9 @@ module Ditty
63
66
  halt 404 unless entity
64
67
  authorize entity, :update
65
68
 
66
- haml :"#{view_location}/edit", locals: { entity: entity, title: heading(:edit) }
69
+ haml :"#{view_location}/edit",
70
+ locals: { entity: entity, title: heading(:edit) },
71
+ layout: layout
67
72
  end
68
73
 
69
74
  # Update
@@ -40,7 +40,7 @@ module Ditty
40
40
  self.current_user = user
41
41
  log_action(:identity_login, user: user)
42
42
  flash[:success] = 'Logged In'
43
- redirect "#{settings.map_path}/"
43
+ redirect env['omniauth.origin'] || "#{settings.map_path}/"
44
44
  else
45
45
  # Failed Login
46
46
  broadcast(:identity_failed_login)
@@ -8,6 +8,8 @@ require 'ditty/policies/identity_policy'
8
8
 
9
9
  module Ditty
10
10
  class Users < Ditty::Component
11
+ SEARCHABLE = %i[name surname email].freeze
12
+
11
13
  set model_class: User
12
14
  set track_actions: true
13
15
 
@@ -20,11 +22,7 @@ module Ditty
20
22
  get '/new' do
21
23
  authorize settings.model_class, :create
22
24
 
23
- locals = {
24
- title: heading(:new),
25
- entity: User.new,
26
- identity: Identity.new
27
- }
25
+ locals = { title: heading(:new), entity: User.new, identity: Identity.new }
28
26
  haml :"#{view_location}/new", locals: locals
29
27
  end
30
28
 
@@ -43,9 +41,17 @@ module Ditty
43
41
  identity = locals[:identity] = Identity.new(identity_params)
44
42
 
45
43
  DB.transaction(isolation: :serializable) do
46
- identity.save # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
44
+ begin
45
+ identity.save
46
+ rescue Sequel::ValidationFailed
47
+ raise unless request.accept? 'text/html'
48
+ status 400
49
+ locals = { title: heading(:new), entity: user, identity: identity }
50
+ return haml(:"#{view_location}/new", locals: locals)
51
+ end
47
52
  user.save
48
53
  user.add_identity identity
54
+
49
55
  if roles
50
56
  roles.each do |role_id|
51
57
  user.add_role(role_id) unless user.roles.map(&:id).include? role_id.to_i
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'ditty/models/user'
3
4
  require 'ditty/models/role'
4
5
  require 'ditty/models/identity'
@@ -7,10 +8,8 @@ module Ditty
7
8
  module Helpers
8
9
  module Authentication
9
10
  def current_user
10
- user_id = current_user_id
11
- self.current_user = anonymous_user if user_id.nil?
12
- @users ||= Hash.new { |h, k| h[k] = User[k] }
13
- @users[user_id]
11
+ return anonymous_user if current_user_id.nil?
12
+ User[current_user_id]
14
13
  end
15
14
 
16
15
  def current_user=(user)
@@ -37,25 +36,13 @@ module Ditty
37
36
  end
38
37
 
39
38
  def logout
40
- env['rack.session'].delete('user_id')
41
- end
42
-
43
- def check_basic(request)
44
- auth = Rack::Auth::Basic::Request.new(request.env)
45
- return false unless auth.provided? && auth.basic?
46
-
47
- identity = ::Ditty::Identity.find(username: auth.credentials[0])
48
- identity ||= ::Ditty::Identity.find(username: CGI.unescape(auth.credentials[0]))
49
- return false unless identity
50
- self.current_user = identity.user if identity.authenticate(auth.credentials[1])
39
+ env['rack.session'].delete('user_id') unless env['rack.session'].nil?
40
+ env.delete('omniauth.auth')
51
41
  end
52
42
 
53
43
  def anonymous_user
54
- return @anonymous_user if defined? @anonymous_user
55
- @anonymous_user ||= begin
56
- role = ::Ditty::Role.where(name: 'anonymous').first
57
- ::Ditty::User.where(roles: role).first unless role.nil?
58
- end
44
+ role = ::Ditty::Role.where(name: 'anonymous').first
45
+ ::Ditty::User.where(roles: role).first unless role.nil?
59
46
  end
60
47
  end
61
48
 
@@ -36,10 +36,6 @@ module Ditty
36
36
  settings.dehumanized || underscore(heading)
37
37
  end
38
38
 
39
- def base_path
40
- settings.base_path || "#{settings.map_path}/#{dasherize(view_location)}"
41
- end
42
-
43
39
  def filters
44
40
  self.class.const_defined?('FILTERS') ? self.class::FILTERS : []
45
41
  end
@@ -21,8 +21,9 @@ module Ditty
21
21
  'permitted_attributes'
22
22
  end
23
23
 
24
+ policy_fields = policy.public_send(method_name)
24
25
  request.params.fetch(param_key, {}).select do |key, _value|
25
- policy.public_send(method_name).include? key.to_sym
26
+ policy_fields.include? key.to_sym
26
27
  end
27
28
  end
28
29
 
@@ -9,15 +9,17 @@ module Ditty
9
9
  actions = {}
10
10
  actions["#{base_path}/new"] = "New #{heading}" if policy(settings.model_class).create?
11
11
  haml :"#{view_location}/index",
12
- locals: { list: result, title: heading(:list), actions: actions }
12
+ locals: { list: result, title: heading(:list), actions: actions },
13
+ layout: layout
13
14
  end
14
15
  format.json do
15
16
  # TODO: Add links defined by actions (New #{heading})
17
+ total = result.respond_to?(:pagination_record_count) ? result.pagination_record_count : result.count
16
18
  json(
17
19
  'items' => result.all.map(&:for_json),
18
20
  'page' => (params['page'] || 1).to_i,
19
21
  'count' => result.count,
20
- 'total' => result.pagination_record_count
22
+ 'total' => total
21
23
  )
22
24
  end
23
25
  end
@@ -27,7 +29,7 @@ module Ditty
27
29
  respond_to do |format|
28
30
  format.html do
29
31
  flash[:success] = "#{heading} Created"
30
- redirect "#{base_path}/#{entity.id}"
32
+ redirect with_layout("#{base_path}/#{entity.id}")
31
33
  end
32
34
  format.json do
33
35
  content_type :json
@@ -42,7 +44,9 @@ module Ditty
42
44
  actions = {}
43
45
  actions["#{base_path}/#{entity.id}/edit"] = "Edit #{heading}" if policy(entity).update?
44
46
  title = heading(:read) + (entity.respond_to?(:name) ? ": #{entity.name}" : '')
45
- haml :"#{view_location}/display", locals: { entity: entity, title: title, actions: actions }
47
+ haml :"#{view_location}/display",
48
+ locals: { entity: entity, title: title, actions: actions },
49
+ layout: layout
46
50
  end
47
51
  format.json do
48
52
  # TODO: Add links defined by actions (Edit #{heading})
@@ -56,7 +60,7 @@ module Ditty
56
60
  format.html do
57
61
  # TODO: Ability to customize the return path and message?
58
62
  flash[:success] = "#{heading} Updated"
59
- redirect "#{base_path}/#{entity.id}"
63
+ redirect with_layout("#{base_path}/#{entity.id}")
60
64
  end
61
65
  format.json do
62
66
  headers 'Location' => "#{base_path}/#{entity.id}"
@@ -69,11 +73,11 @@ module Ditty
69
73
  respond_to do |format|
70
74
  format.html do
71
75
  flash[:success] = "#{heading} Deleted"
72
- redirect base_path.to_s
76
+ redirect with_layout(base_path.to_s)
73
77
  end
74
78
  format.json do
75
79
  content_type :json
76
- headers 'Location' => "#{base_path}"
80
+ headers 'Location' => base_path.to_s
77
81
  status 204
78
82
  end
79
83
  end
@@ -3,6 +3,20 @@
3
3
  module Ditty
4
4
  module Helpers
5
5
  module Views
6
+ def layout
7
+ return :embedded if request.params['layout'] == 'embedded'
8
+ :layout
9
+ end
10
+
11
+ def with_layout(url)
12
+ uri = URI.parse(url)
13
+ # Don't set the layout if there's none. Don't set the layout for external URIs
14
+ return url if params['layout'].nil? || uri.host
15
+ qs = { 'layout' => params['layout'] }.merge(uri.query ? CGI.parse(uri.query) : {})
16
+ uri.query = Rack::Utils.build_query qs
17
+ uri.to_s
18
+ end
19
+
6
20
  def form_control(name, model, opts = {})
7
21
  label = opts.delete(:label) || name.to_s.titlecase
8
22
  klass = opts.delete(:class) || 'form-control' unless opts[:type] == 'file'
@@ -41,15 +55,38 @@ module Ditty
41
55
  "<div id='#{id}'>\n" + messages.join + '</div>'
42
56
  end
43
57
 
58
+ def query_string(add = {})
59
+ qs = params.clone.merge(add)
60
+ qs.delete('captures')
61
+ Rack::Utils.build_query qs
62
+ end
63
+
44
64
  def delete_form(entity, label = 'Delete')
45
65
  locals = { delete_label: label, entity: entity }
46
66
  haml :'partials/delete_form', locals: locals
47
67
  end
48
68
 
49
- def query_string(add = {})
50
- qs = params.clone.merge(add)
51
- qs.delete('captures')
52
- Rack::Utils.build_query qs
69
+ def delete_form_tag(url, options = {}, &block)
70
+ options[:form_verb] = :delete
71
+ form_tag(url, options, &block)
72
+ end
73
+
74
+ def edit_form_tag(url, options = {}, &block)
75
+ options[:form_verb] = :put
76
+ form_tag(url, options, &block)
77
+ end
78
+
79
+ def new_form_tag(url, options = {}, &block)
80
+ options[:form_verb] = :post
81
+ form_tag(url, options, &block)
82
+ end
83
+
84
+ def form_tag(url, options = {}, &block)
85
+ options[:form_verb] ||= :post
86
+ options[:attributes] ||= {}
87
+ options[:attributes] = { 'class': 'form-horizontal' }.merge options[:attributes]
88
+ options[:url] = options[:form_verb].to_sym == :get ? url : with_layout(url)
89
+ haml :'partials/form_tag', locals: options.merge(block: block)
53
90
  end
54
91
 
55
92
  def pagination(list, base_path, qp = {})
@@ -6,7 +6,7 @@ module Ditty
6
6
  module Helpers
7
7
  module Wisper
8
8
  def log_action(action, args = {})
9
- args[:user] ||= current_user
9
+ args[:user] = current_user unless args.key? :user
10
10
  broadcast(action, args)
11
11
  end
12
12
  end
@@ -21,6 +21,7 @@ module Ditty
21
21
  end
22
22
 
23
23
  def authenticate(unencrypted)
24
+ return false if crypted_password.blank?
24
25
  self if ::BCrypt::Password.new(crypted_password) == unencrypted
25
26
  end
26
27
 
@@ -17,7 +17,7 @@ module Ditty
17
17
  @loggers = []
18
18
  config.each do |values|
19
19
  klass = values['class'].constantize
20
- opts = values['options'] || nil
20
+ opts = tr(values['options']) || nil
21
21
  logger = klass.new(opts)
22
22
  if values['level']
23
23
  logger.level = klass.const_get(values['level'].to_sym)
@@ -40,6 +40,13 @@ module Ditty
40
40
  @config ||= File.exist?(CONFIG) ? YAML.load_file(CONFIG) : default
41
41
  end
42
42
 
43
+ def tr(val)
44
+ {
45
+ '$stdout' => $stdout,
46
+ '$stderr' => $stderr
47
+ }[val] || val
48
+ end
49
+
43
50
  def default
44
51
  [{ 'name' => 'default', 'class' => 'Logger' }]
45
52
  end
data/lib/ditty/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ditty
4
- VERSION = '0.4.0'.freeze
4
+ VERSION = '0.4.1'.freeze
5
5
  end
@@ -0,0 +1,42 @@
1
+ !!! 5
2
+ %html{ lang: 'en' }
3
+ %head
4
+ %meta{ charset: 'utf-8' }
5
+ %meta{ 'http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1' }
6
+ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
7
+ %meta{ name: 'theme-color', content: '#ffffff' }
8
+ %link{ rel: 'manifest', href: '/manifest.json' }
9
+ %link{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '#{request.base_url}/images/favicon-32x32.png' }
10
+ %link{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '#{request.base_url}/images/favicon-16x16.png' }
11
+ %link{ rel: 'apple-touch-icon', sizes: '76x76', href: '/images/apple-icon.png' }
12
+ %link{ rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#5bbad5' }
13
+
14
+ %title
15
+ Ditty
16
+ - if defined? title
17
+ = "- #{title}"
18
+
19
+ %meta{ name: 'description', content: '' }
20
+ %meta{ name: 'author', content: '' }
21
+
22
+ / Le styles
23
+ %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css', media: 'screen' }
24
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/css/sb-admin-2.min.css', media: 'screen' }
25
+ %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css', media: 'screen' }
26
+ /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
27
+ /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
28
+
29
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js' }
30
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/js/sb-admin-2.min.js' }
31
+ %body
32
+ .container-fluid
33
+ .row
34
+ .col-md-12
35
+ = haml :'partials/notifications'
36
+
37
+ = yield
38
+
39
+ / Placed at the end of the document so the pages load faster
40
+ %script{ type: 'text/javascript', src: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js' }
41
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.min.js' }
42
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js' }
@@ -2,12 +2,10 @@
2
2
  .col-sm-3
3
3
  .col-sm-6
4
4
  .panel.panel-default
5
- .panel-heading
6
- %h4 Login
7
5
  .panel-body
8
6
  %form{ method: 'post', action: "#{settings.map_path}/auth/identity/callback" }
9
7
  .form-group
10
- %label.control-label Username
8
+ %label.control-label Email
11
9
  %input.form-control.border-input{ name: 'username' }
12
10
  .form-group
13
11
  %label.control-label Password
@@ -2,13 +2,20 @@
2
2
  .col-md-2
3
3
  .col-md-8
4
4
  .panel.panel-default
5
- .panel-heading
6
- %h4 Register
7
5
  .panel-body
8
6
  %form.form-horizontal{ method: 'post', action: "#{settings.map_path}/auth/identity/new" }
9
- = form_control(:username, identity, label: 'Username', placeholder: 'Your username')
7
+ = form_control(:username, identity, label: 'Email', placeholder: 'your@email.com')
10
8
  = form_control(:password, identity, label: 'Password', type: :password)
11
9
  = form_control(:password_confirmation, identity, label: 'Confirm Password', type: :password)
12
10
 
13
- %button.btn.btn-primary{ type: 'submit' } Register
11
+ - if identity.errors[:password] && identity.errors[:password].include?('is not strong enough')
12
+ .alert.alert-warning
13
+ %p Make sure your password is at least 8 characters long, and including the following
14
+ %ul
15
+ %li Upper- and lowercase letters
16
+ %li Numbers
17
+ %li Special Characters
18
+
19
+ - if policy(::Ditty::Identity).register?
20
+ %button.btn.btn-primary{ type: 'submit' } Register
14
21
  .col-md-2
data/views/layout.haml CHANGED
@@ -34,10 +34,16 @@
34
34
  #wrapper
35
35
  = haml :'partials/navbar', locals: { title: (defined?(title) ? title : 'Ditty') }
36
36
  #page-wrapper
37
+ -if defined?(title) || defined?(actions)
38
+ .row
39
+ %h1.col-md-9= defined?(title) ? title : '&nbsp'
40
+ .col-md-3.text-left{ style: 'margin-top: 20px' }
41
+ = haml :'partials/actions', locals: { actions: defined?(actions) ? actions : {} }
42
+ -else
43
+ %div{ style: 'padding-top: 20px' }
44
+
37
45
  .row
38
46
  .col-md-12
39
- -if defined? title
40
- %h1.page-header= title
41
47
  = haml :'partials/notifications'
42
48
 
43
49
  = yield
@@ -0,0 +1,12 @@
1
+ - if actions.count > 1
2
+ .dropdown
3
+ %button.btn.btn-default.btn-block.dropdown-toggle{ type: 'button', id: 'actions-toggle', data: { toggle: 'dropdown' } }
4
+ Actions
5
+ %span.caret
6
+ %ul.dropdown-menu{ 'aria-labelledby': 'actions-toggle' }
7
+ -actions.each do |k, v|
8
+ %li
9
+ %a{ href: k }= v
10
+ - elsif actions.count > 0
11
+ -actions.each do |k, v|
12
+ %a.btn.btn-primary.btn-block{ href: k }= v
@@ -1,4 +1,2 @@
1
- <form method="post" action="<%= base_path %>/<%= entity.id %>">
2
- <input type="hidden" name="_method" value="DELETE">
3
- <button type="submit" class="btn btn-danger"><%= delete_label %></button>
4
- </form>
1
+ = delete_form_tag "#{base_path}/#{entity.id}" do
2
+ %button.btn.btn-danger{ type: 'submit' }= delete_label
@@ -2,4 +2,4 @@
2
2
  %hr
3
3
  .copyright
4
4
  :plain
5
- &copy; <script>document.write(new Date().getFullYear())</script>, Automation Exchange
5
+ &copy; <script>document.write(new Date().getFullYear())</script>, Ditty
@@ -0,0 +1,6 @@
1
+ %form{ { method: %i[get post].include?(form_verb.to_sym) ? form_verb : :post, action: url }.merge(attributes) }
2
+ - if form_verb.to_sym == :get && layout
3
+ %input{ name: 'layout', value: layout, type: 'hidden' }
4
+ - if %i[get post].include?(form_verb.to_sym) == false
5
+ %input{ name: '_method', value: form_verb.upcase, type: 'hidden' }
6
+ = capture_haml(&block).chomp
@@ -1,5 +1,5 @@
1
1
  - if self.class.const_defined?(:SEARCHABLE) || self.class.const_defined?(:FILTERS)
2
- %form{ method: 'get', action: base_path }
2
+ = form_tag(base_path, form_verb: :get, attributes: { class: '' }) do
3
3
  - if self.class.const_defined?(:SEARCHABLE)
4
4
  .form-group
5
5
  .input-group
@@ -3,8 +3,7 @@
3
3
  .col-md-8
4
4
  .panel.panel-default
5
5
  .panel-body
6
- %form.form-horizontal{ method: 'post', action: "#{base_path}/#{entity.id}" }
7
- %input{ name: '_method', value: 'PUT', type: 'hidden' }
6
+ = edit_form_tag "#{base_path}/#{entity.id}" do
8
7
  = haml :'roles/form', locals: { entity: entity }
9
8
  %button.btn.btn-primary{ type: 'submit' }
10
9
  Update Role
data/views/roles/new.haml CHANGED
@@ -3,7 +3,7 @@
3
3
  .col-md-8
4
4
  .panel.panel-default
5
5
  .panel-body
6
- %form.form-horizontal{ method: 'post', action: base_path }
6
+ = new_form_tag base_path do
7
7
  = haml :'roles/form', locals: { entity: entity }
8
8
  %button.btn.btn-primary{ type: 'submit' }
9
9
  Create Role
@@ -3,8 +3,7 @@
3
3
  .col-md-8
4
4
  .panel.panel-default
5
5
  .panel-body
6
- %form.form-horizontal{ method: 'post', action: "#{base_path}/#{entity.id}" }
7
- %input{ name: '_method', value: 'PUT', type: 'hidden' }
6
+ = edit_form_tag "#{base_path}/#{entity.id}" do
8
7
  = haml :'users/user', locals: { user: entity }
9
8
  %button.btn.btn-primary{ type: 'submit' }
10
9
  Update User
@@ -1,3 +1,10 @@
1
1
  = form_control(:username, identity, label: 'Email', placeholder: 'Your email address')
2
+ - if identity.errors[:password] && identity.errors[:password].include?('is not strong enough')
3
+ .alert.alert-warning
4
+ %p Make sure your password is at least 8 characters long, and including the following
5
+ %ul
6
+ %li Upper- and lowercase letters
7
+ %li Numbers
8
+ %li Special Characters
2
9
  = form_control(:password, identity, type: 'password', placeholder: 'Your password')
3
10
  = form_control(:password_confirmation, identity, type: 'password', label: 'Confirm Password', placeholder: 'Confirm your password')
@@ -1,6 +1,8 @@
1
1
  .row
2
2
  .col-md-12
3
3
  .panel.panel-default
4
+ .panel-body
5
+ = haml :'partials/search'
4
6
  %table.table.table-striped
5
7
  %thead
6
8
  %tr
data/views/users/new.haml CHANGED
@@ -3,8 +3,8 @@
3
3
  .col-md-8
4
4
  .panel.panel-default
5
5
  .panel-body
6
- %form.form-horizontal{ method: 'post', action: base_path }
7
- = haml :'users/identity', locals: { identity: Ditty::Identity.new }
6
+ = new_form_tag base_path do
7
+ = haml :'users/identity', locals: { identity: identity }
8
8
  = haml :'users/user', locals: { user: entity }
9
9
  %button.btn.btn-primary{ type: 'submit' }
10
10
  Create User
@@ -1,4 +1,4 @@
1
1
  = form_control(:name, user)
2
2
  = form_control(:surname, user)
3
3
  - if policy(user).permitted_attributes.include? :role_id
4
- = form_control(:role_id, user, type: 'select', options: Ditty::Role.as_hash(:id, :name), name: 'user[role_id][]', field: :roles, multiple: true)
4
+ = form_control(:role_id, user, type: 'select', options: Ditty::Role.order(:name).as_hash(:id, :name), name: 'user[role_id][]', field: :roles, multiple: true)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ditty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jurgens du Toit
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-03 00:00:00.000000000 Z
11
+ date: 2018-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: '1.0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: oga
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '2.14'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '2.14'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: omniauth
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -238,28 +252,28 @@ dependencies:
238
252
  name: sequel
239
253
  requirement: !ruby/object:Gem::Requirement
240
254
  requirements:
241
- - - "~>"
255
+ - - ">="
242
256
  - !ruby/object:Gem::Version
243
257
  version: '4.0'
244
258
  type: :runtime
245
259
  prerelease: false
246
260
  version_requirements: !ruby/object:Gem::Requirement
247
261
  requirements:
248
- - - "~>"
262
+ - - ">="
249
263
  - !ruby/object:Gem::Version
250
264
  version: '4.0'
251
265
  - !ruby/object:Gem::Dependency
252
266
  name: sinatra
253
267
  requirement: !ruby/object:Gem::Requirement
254
268
  requirements:
255
- - - "~>"
269
+ - - ">="
256
270
  - !ruby/object:Gem::Version
257
271
  version: '2.0'
258
272
  type: :runtime
259
273
  prerelease: false
260
274
  version_requirements: !ruby/object:Gem::Requirement
261
275
  requirements:
262
- - - "~>"
276
+ - - ">="
263
277
  - !ruby/object:Gem::Version
264
278
  version: '2.0'
265
279
  - !ruby/object:Gem::Dependency
@@ -384,15 +398,18 @@ files:
384
398
  - public/manifest.json
385
399
  - views/404.haml
386
400
  - views/audit_logs/index.haml
401
+ - views/embedded.haml
387
402
  - views/error.haml
388
403
  - views/identity/login.haml
389
404
  - views/identity/register.haml
390
405
  - views/index.haml
391
406
  - views/layout.haml
407
+ - views/partials/actions.haml
392
408
  - views/partials/delete_form.haml
393
409
  - views/partials/filter_control.haml
394
410
  - views/partials/footer.haml
395
411
  - views/partials/form_control.haml
412
+ - views/partials/form_tag.haml
396
413
  - views/partials/navbar.haml
397
414
  - views/partials/notifications.haml
398
415
  - views/partials/pager.haml
@@ -430,7 +447,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
430
447
  version: '0'
431
448
  requirements: []
432
449
  rubyforge_project:
433
- rubygems_version: 2.7.4
450
+ rubygems_version: 2.7.6
434
451
  signing_key:
435
452
  specification_version: 4
436
453
  summary: Sinatra Based Application Framework