ditty 0.9.1 → 0.10.1

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.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.env.test +2 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +23 -4
  5. data/.travis.yml +2 -7
  6. data/Gemfile.ci +0 -15
  7. data/ditty.gemspec +19 -15
  8. data/lib/ditty.rb +2 -2
  9. data/lib/ditty/cli.rb +5 -0
  10. data/lib/ditty/components/ditty.rb +4 -7
  11. data/lib/ditty/controllers/application_controller.rb +6 -5
  12. data/lib/ditty/controllers/audit_logs_controller.rb +2 -0
  13. data/lib/ditty/controllers/auth_controller.rb +5 -2
  14. data/lib/ditty/controllers/component_controller.rb +3 -3
  15. data/lib/ditty/controllers/user_login_traits_controller.rb +28 -1
  16. data/lib/ditty/controllers/users_controller.rb +3 -2
  17. data/lib/ditty/db.rb +4 -3
  18. data/lib/ditty/emails/base.rb +32 -30
  19. data/lib/ditty/generators/crud_generator.rb +51 -41
  20. data/lib/ditty/generators/project_generator.rb +1 -0
  21. data/lib/ditty/helpers/pundit.rb +4 -4
  22. data/lib/ditty/helpers/response.rb +6 -11
  23. data/lib/ditty/helpers/views.rb +21 -3
  24. data/lib/ditty/listener.rb +1 -1
  25. data/lib/ditty/models/base.rb +5 -0
  26. data/lib/ditty/models/identity.rb +7 -7
  27. data/lib/ditty/models/user.rb +9 -1
  28. data/lib/ditty/policies/user_policy.rb +1 -1
  29. data/lib/ditty/services/authentication.rb +19 -9
  30. data/lib/ditty/services/email.rb +13 -13
  31. data/lib/ditty/services/logger.rb +26 -20
  32. data/lib/ditty/services/pagination_wrapper.rb +7 -5
  33. data/lib/ditty/services/settings.rb +7 -6
  34. data/lib/ditty/tasks/ditty.rake +2 -1
  35. data/lib/ditty/templates/application.rb +1 -1
  36. data/lib/ditty/templates/config.ru +2 -2
  37. data/lib/ditty/templates/controller.rb.erb +7 -1
  38. data/{public → lib/ditty/templates/public}/browserconfig.xml +0 -0
  39. data/{public → lib/ditty/templates/public}/css/styles.css +0 -0
  40. data/lib/ditty/templates/public/favicon.ico +0 -0
  41. data/{public → lib/ditty/templates/public}/images/apple-icon.png +0 -0
  42. data/{public → lib/ditty/templates/public}/images/favicon-16x16.png +0 -0
  43. data/{public → lib/ditty/templates/public}/images/favicon-32x32.png +0 -0
  44. data/{public → lib/ditty/templates/public}/images/launcher-icon-1x.png +0 -0
  45. data/{public → lib/ditty/templates/public}/images/launcher-icon-2x.png +0 -0
  46. data/{public → lib/ditty/templates/public}/images/launcher-icon-4x.png +0 -0
  47. data/{public → lib/ditty/templates/public}/images/mstile-150x150.png +0 -0
  48. data/{public → lib/ditty/templates/public}/images/safari-pinned-tab.svg +0 -0
  49. data/{public → lib/ditty/templates/public}/js/scripts.js +0 -0
  50. data/{public/manifest.json → lib/ditty/templates/public/manifest.json.erb} +2 -2
  51. data/lib/ditty/templates/settings.yml.erb +1 -0
  52. data/lib/ditty/templates/spec_helper.rb +1 -1
  53. data/lib/ditty/templates/views/display.haml.tt +1 -1
  54. data/lib/ditty/templates/views/edit.haml.tt +1 -1
  55. data/lib/ditty/templates/views/index.haml.tt +1 -1
  56. data/lib/ditty/templates/views/new.haml.tt +1 -1
  57. data/lib/ditty/version.rb +1 -1
  58. data/spec/ditty/api_spec.rb +1 -1
  59. data/spec/ditty/emails/base_spec.rb +3 -3
  60. data/spec/ditty/emails/forgot_password_spec.rb +3 -2
  61. data/spec/ditty/models/user_spec.rb +3 -3
  62. data/spec/ditty/services/logger_spec.rb +7 -6
  63. data/spec/ditty/services/settings_spec.rb +2 -2
  64. data/spec/factories.rb +4 -4
  65. data/spec/spec_helper.rb +5 -1
  66. data/views/403.haml +1 -1
  67. data/views/500.haml +11 -0
  68. data/views/audit_logs/index.haml +12 -11
  69. data/views/auth/forgot_password.haml +29 -24
  70. data/views/auth/ldap.haml +1 -1
  71. data/views/auth/login.haml +3 -2
  72. data/views/auth/register.haml +3 -2
  73. data/views/auth/reset_password.haml +36 -19
  74. data/views/blank.haml +1 -0
  75. data/views/embedded.haml +17 -11
  76. data/views/layout.haml +16 -8
  77. data/views/partials/actions.haml +15 -14
  78. data/views/partials/filter_control.haml +1 -1
  79. data/views/partials/footer.haml +10 -2
  80. data/views/partials/form_tag.haml +1 -1
  81. data/views/partials/navitems.haml +25 -27
  82. data/views/partials/pager.haml +44 -25
  83. data/views/partials/search.haml +14 -9
  84. data/views/partials/sidebar.haml +2 -2
  85. data/views/partials/sort_ui.haml +2 -0
  86. data/views/partials/timespan_selector.haml +64 -0
  87. data/views/partials/topbar.haml +0 -15
  88. data/views/partials/user_associations.haml +32 -0
  89. data/views/quick_start.haml +23 -0
  90. data/views/roles/display.haml +3 -3
  91. data/views/roles/edit.haml +1 -1
  92. data/views/roles/index.haml +2 -2
  93. data/views/roles/new.haml +1 -1
  94. data/views/user_login_traits/display.haml +1 -1
  95. data/views/user_login_traits/edit.haml +1 -1
  96. data/views/user_login_traits/index.haml +23 -25
  97. data/views/user_login_traits/new.haml +1 -1
  98. data/views/users/display.haml +5 -5
  99. data/views/users/edit.haml +1 -1
  100. data/views/users/index.haml +5 -5
  101. data/views/users/login_traits.haml +2 -2
  102. data/views/users/new.haml +1 -1
  103. data/views/users/profile.haml +4 -4
  104. data/views/users/user.haml +1 -1
  105. metadata +116 -54
@@ -30,40 +30,46 @@ module Ditty
30
30
  end
31
31
  end
32
32
 
33
+ # TODO: REfac this so that you can log something like ES to a separate logger
34
+
33
35
  def method_missing(method, *args, &block)
34
36
  loggers.each { |logger| logger.send(method, *args, &block) }
35
37
  end
36
38
 
37
39
  def respond_to_missing?(method, _include_private = false)
38
- loggers.any? { |logger| logger.respond_to?(method) }
40
+ return true if loggers.any? { |logger| logger.respond_to?(method) }
41
+
42
+ super
39
43
  end
40
44
 
41
45
  private
42
46
 
43
- def config
44
- default.merge ::Ditty::Services::Settings.values(:logger) || {}
45
- end
46
-
47
- def tr(val)
48
- {
49
- '$stdout' => $stdout,
50
- '$stderr' => $stderr
51
- }[val] || val
52
- end
47
+ def config
48
+ default.merge ::Ditty::Services::Settings.values(:logger) || {}
49
+ end
53
50
 
54
- def default
55
- { loggers: [{ name: 'default', class: 'Logger' }] }
56
- end
51
+ def tr(val)
52
+ {
53
+ '$stdout' => $stdout,
54
+ '$stderr' => $stderr
55
+ }[val] || val
56
+ end
57
57
 
58
- class << self
59
- def method_missing(method, *args, &block)
60
- instance.send(method, *args, &block)
58
+ def default
59
+ { loggers: [{ name: 'default', class: 'Logger' }] }
61
60
  end
62
61
 
63
- def respond_to_missing?(method, _include_private)
64
- instance.respond_to? method
62
+ class << self
63
+ def method_missing(method, *args, &block)
64
+ instance.send(method, *args, &block)
65
+ end
66
+
67
+ def respond_to_missing?(method, _include_private)
68
+ return true if instance.respond_to?(method)
69
+
70
+ super
71
+ end
65
72
  end
66
- end
67
73
  end
68
74
  end
69
75
  end
@@ -10,18 +10,18 @@ module Ditty
10
10
  end
11
11
 
12
12
  def last_page?
13
- if list.respond_to? :'last_page?'
13
+ if list.respond_to? :last_page?
14
14
  list.last_page?
15
15
  else
16
- list.current_page == list.total_pages
16
+ list.next_page.nil?
17
17
  end
18
18
  end
19
19
 
20
20
  def first_page?
21
- if list.respond_to? :'first_page?'
21
+ if list.respond_to? :first_page?
22
22
  list.first_page?
23
23
  else
24
- list.current_page == 0
24
+ list.previous_page.nil?
25
25
  end
26
26
  end
27
27
 
@@ -64,7 +64,9 @@ module Ditty
64
64
  end
65
65
 
66
66
  def respond_to_missing?(method, _include_private = false)
67
- list.respond_to? method
67
+ return true if list.respond_to?(method)
68
+
69
+ super
68
70
  end
69
71
 
70
72
  def current_page_record_range
@@ -25,20 +25,21 @@ module Ditty
25
25
  from = values
26
26
  if keys.count > 1 && scope?(keys.first)
27
27
  from = values(keys.first)
28
- keys = keys[1..-1]
28
+ keys = keys[1..]
29
29
  key = keys.join('.')
30
30
  end
31
- from[key.to_sym] || from.dig(*keys)
31
+ key = key.to_sym if key.respond_to?(:to_sym)
32
+ from[key] || from.dig(*keys)
32
33
  end
33
34
 
34
35
  def values(scope = :settings)
35
36
  @values ||= begin
36
37
  v = Hash.new do |h, k|
37
38
  h[k] = if File.file?("#{CONFIG_FOLDER}/#{k}.yml")
38
- read("#{CONFIG_FOLDER}/#{k}.yml")
39
- elsif k != :settings && h[:settings].key?(k)
40
- h[:settings][k]
41
- end
39
+ read("#{CONFIG_FOLDER}/#{k}.yml")
40
+ elsif k != :settings && h[:settings].key?(k)
41
+ h[:settings][k]
42
+ end
42
43
  h[k]
43
44
  end
44
45
  v[:settings] = File.file?(CONFIG_FILE) ? read(CONFIG_FILE) : {}
@@ -38,6 +38,7 @@ namespace :ditty do
38
38
  task :folders do
39
39
  puts 'Prepare the Ditty folders'
40
40
  Dir.mkdir 'pids' unless File.exist?('pids')
41
+ Dir.mkdir 'logs' unless File.exist?('logs')
41
42
  end
42
43
 
43
44
  desc 'Check that the public folder is present and populated'
@@ -60,7 +61,7 @@ namespace :ditty do
60
61
  FileUtils.cp_r path, 'migrations' unless File.expand_path(path).eql? File.expand_path('migrations')
61
62
  end
62
63
  puts 'Migrations added:'
63
- Dir.foreach('migrations').sort.each { |x| puts x if File.file?("migrations/#{x}") && x[-3..-1] == '.rb' }
64
+ Dir.foreach('migrations').sort.each { |x| puts x if File.file?("migrations/#{x}") && x[-3..] == '.rb' }
64
65
  end
65
66
  end
66
67
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- libdir = File.expand_path(File.dirname(__FILE__) + '/lib')
3
+ libdir = File.expand_path("#{File.dirname(__FILE__)}/lib")
4
4
  $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
5
5
 
6
6
  require 'ditty/components/ditty'
@@ -19,8 +19,8 @@ use Rack::Session::Cookie,
19
19
  require './application'
20
20
  require 'ditty/services/authentication'
21
21
  use OmniAuth::Builder do
22
- ::Ditty::Services::Authentication.config.each do |prov, config|
23
- provider prov, *config[:arguments]
22
+ ::Ditty::Services::Authentication.providers.each do |prov|
23
+ provider prov, *::Ditty::Services::Authentication.config[prov][:arguments]
24
24
  end
25
25
  end
26
26
 
@@ -14,7 +14,7 @@ module <%= namespace %>
14
14
  # Add field filter definitions here
15
15
  FILTERS = [
16
16
  <%- many_to_ones.each do |key| -%>
17
- { name: :<%= key[:table].to_s.singularize %>, field: :<%= key[:key] || 'id' %> },
17
+ { name: :<%= key[:table].to_s.singularize %>, field: :<%= key[:columns].first || 'id' %> },
18
18
  <%- end -%>
19
19
  ].freeze
20
20
 
@@ -45,6 +45,12 @@ module <%= namespace %>
45
45
  <%- end -%>
46
46
  <%- if many_to_ones.count.positive? -%>
47
47
 
48
+ before '/' do
49
+ <%- many_to_ones.each do |key| -%>
50
+ param(:<%= key[:table].to_s.singularize %>, Integer) unless params[:<%= key[:table].to_s.singularize %>].blank?
51
+ <%- end -%>
52
+ end
53
+
48
54
  before '*', provides: 'html' do
49
55
  <%- many_to_ones.each do |key| -%>
50
56
  if <%= key[:table].to_s.classify %>.count.zero?
@@ -1,6 +1,6 @@
1
1
  {
2
- "short_name": "Ditty",
3
- "name": "Ditty",
2
+ "short_name": "<%= name %>",
3
+ "name": "<%= name %>",
4
4
  "icons": [
5
5
  {
6
6
  "src": "images/launcher-icon-1x.png",
@@ -1,5 +1,6 @@
1
1
  ditty:
2
2
  title: <%= name %>
3
+ brand-icon: 'fa-music'
3
4
  logger:
4
5
  loggers:
5
6
  - name: files
@@ -13,7 +13,7 @@ require 'factory_bot'
13
13
  require 'database_cleaner'
14
14
 
15
15
  if ENV['DATABASE_URL'] == 'sqlite::memory:'
16
- folder = File.expand_path(File.dirname(__FILE__) + '/../migrate')
16
+ folder = File.expand_path("#{File.dirname(__FILE__)}/../migrate")
17
17
  Sequel.extension :migration
18
18
  Sequel::Migrator.apply(DB, folder)
19
19
 
@@ -1,7 +1,7 @@
1
1
  .row
2
2
  .col-md-2
3
3
  .col-md-8
4
- .card
4
+ .card.card-default.shadow
5
5
  .card-body
6
6
  <%- columns.each do |col| -%>
7
7
  %p.description
@@ -1,7 +1,7 @@
1
1
  .row
2
2
  .col-md-2
3
3
  .col-md-8
4
- .card
4
+ .card.card-default.shadow
5
5
  .card-body
6
6
  = edit_form_tag "#{base_path}/#{entity.display_id}" do
7
7
  = haml :'<%= model_name.pluralize.underscore %>/form', locals: { entity: entity }
@@ -26,4 +26,4 @@
26
26
  %td.text-center{ colspan: <%= columns.count + 1 %> } No <%= model_name.pluralize %>
27
27
 
28
28
  - if list.count > 0
29
- =pagination(list, base_path)
29
+ = pagination(list, base_path)
@@ -1,7 +1,7 @@
1
1
  .row
2
2
  .col-md-2
3
3
  .col-md-8
4
- .card
4
+ .card.card-default.shadow
5
5
  .card-body
6
6
  = new_form_tag base_path do
7
7
  = haml :'<%= model_name.pluralize.underscore %>/form', locals: { entity: entity }
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.9.1'
4
+ VERSION = '0.10.1'
5
5
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
- Dir.glob('./lib/ditty/controllers/*.rb').each { |f| require f }
4
+ Dir.glob('./lib/ditty/controllers/*.rb').sort.each { |f| require f }
5
5
  require 'support/api_shared_examples'
6
6
 
7
7
  describe ::Ditty::RolesController, type: :controller do
@@ -11,7 +11,7 @@ describe ::Ditty::Emails::Base do
11
11
  mail
12
12
  end
13
13
 
14
- context '.new' do
14
+ describe '.new' do
15
15
  it 'defaults to base options' do
16
16
  expect(subject.options).to include subject: '(No Subject)', from: 'no-reply@ditty.io', view: :base
17
17
  end
@@ -24,7 +24,7 @@ describe ::Ditty::Emails::Base do
24
24
  end
25
25
  end
26
26
 
27
- context '.deliver!' do
27
+ describe '.deliver!' do
28
28
  it 'delivers the email to the specified email address' do
29
29
  expect(mail).to receive(:to).with('test@email.com')
30
30
  expect(mail).to receive(:deliver!)
@@ -44,7 +44,7 @@ describe ::Ditty::Emails::Base do
44
44
  end
45
45
  end
46
46
 
47
- context '#deliver!' do
47
+ describe '#deliver!' do
48
48
  it 'delivers the email to the specified email address' do
49
49
  expect(mail).to receive(:to).with('test2@email.com')
50
50
  base = described_class.new(mail: mail)
@@ -11,9 +11,10 @@ describe ::Ditty::Emails::ForgotPassword do
11
11
  mail
12
12
  end
13
13
 
14
- context '.new' do
14
+ describe '.new' do
15
15
  it 'defaults to base options' do
16
- expect(subject.options).to include subject: 'Request to reset password', from: 'no-reply@ditty.io', view: :forgot_password
16
+ expect(subject.options).to include subject: 'Request to reset password', from: 'no-reply@ditty.io',
17
+ view: :forgot_password
17
18
  end
18
19
  end
19
20
  end
@@ -15,12 +15,12 @@ describe ::Ditty::User, type: :model do
15
15
  describe '#role?(check)' do
16
16
  context 'when a user has a role without a parent' do
17
17
  it 'returns true only for specific role' do
18
- expect(user.role?('user')).to be_truthy
18
+ expect(user).to be_role('user')
19
19
  end
20
20
 
21
21
  it 'returns false for other roles' do
22
22
  %w[admin super_admin].each do |role|
23
- expect(user.role?(role)).to be_falsy
23
+ expect(user).not_to be_role(role)
24
24
  end
25
25
  end
26
26
  end
@@ -28,7 +28,7 @@ describe ::Ditty::User, type: :model do
28
28
  context 'when a user has a role with descendants' do
29
29
  it 'returns true for all descendants' do
30
30
  %w[user admin super_admin].each do |role|
31
- expect(super_admin.role?(role)).to be_truthy
31
+ expect(super_admin).to be_role(role)
32
32
  end
33
33
  end
34
34
  end
@@ -7,6 +7,7 @@ require 'ditty/services/logger'
7
7
  class TestLogger
8
8
  WARN = 2
9
9
  attr_accessor :level
10
+
10
11
  def initialize(options = {})
11
12
  @options = options
12
13
  end
@@ -27,8 +28,8 @@ describe ::Ditty::Services::Logger, type: :service do
27
28
 
28
29
  it 'reads config from file and creates an array of loggers' do
29
30
  ::Ditty::Services::Settings.values = nil
30
- allow(File).to receive(:'file?').and_return(false)
31
- allow(File).to receive(:'file?').with('./config/logger.yml').and_return(true)
31
+ allow(File).to receive(:file?).and_return(false)
32
+ allow(File).to receive(:file?).with('./config/logger.yml').and_return(true)
32
33
  allow(File).to receive(:read).and_return(config_file)
33
34
 
34
35
  expect(subject.instance.loggers.size).to eq 4
@@ -38,8 +39,8 @@ describe ::Ditty::Services::Logger, type: :service do
38
39
 
39
40
  it 'sets the correct logging level' do
40
41
  ::Ditty::Services::Settings.values = nil
41
- allow(File).to receive(:'file?').and_return(false)
42
- allow(File).to receive(:'file?').with('./config/logger.yml').and_return(true)
42
+ allow(File).to receive(:file?).and_return(false)
43
+ allow(File).to receive(:file?).with('./config/logger.yml').and_return(true)
43
44
  allow(File).to receive(:read).and_return(config_file)
44
45
  expect(subject.instance.loggers[0].level).to eq Logger::DEBUG
45
46
  expect(subject.instance.loggers[2].level).to eq Logger::INFO
@@ -50,8 +51,8 @@ describe ::Ditty::Services::Logger, type: :service do
50
51
  context 'send messages' do
51
52
  it 'receives message and passes it to the loggers' do
52
53
  ::Ditty::Services::Settings.values = nil
53
- allow(File).to receive(:'file?').and_return(false)
54
- allow(File).to receive(:'file?').with('./config/logger.yml').and_return(true)
54
+ allow(File).to receive(:file?).and_return(false)
55
+ allow(File).to receive(:file?).with('./config/logger.yml').and_return(true)
55
56
  allow(File).to receive(:read).and_return(config_file)
56
57
  allow(Logger).to receive(:warn).with('Some message')
57
58
  allow(TestLogger).to receive(:warn).with('Some message')
@@ -16,7 +16,7 @@ describe ::Ditty::Services::Settings do
16
16
  allow(File).to receive(:read).with('./config/section.yml').and_return(section)
17
17
  end
18
18
 
19
- context '#[]' do
19
+ describe '#[]' do
20
20
  before do
21
21
  setup_files
22
22
  described_class.values = nil
@@ -35,7 +35,7 @@ describe ::Ditty::Services::Settings do
35
35
  end
36
36
  end
37
37
 
38
- context '#values' do
38
+ describe '#values' do
39
39
  context 'uses the global file' do
40
40
  before do
41
41
  setup_files
data/spec/factories.rb CHANGED
@@ -12,7 +12,7 @@ FactoryBot.define do
12
12
  sequence(:email) { |n| "person-#{n}@example.com" }
13
13
  sequence(:name) { |n| "Name-#{n}" }
14
14
 
15
- factory :user, class: Ditty::User, aliases: [:'Ditty::User'] do
15
+ factory :user, class: 'Ditty::User', aliases: [:'Ditty::User'] do
16
16
  email
17
17
 
18
18
  after(:create) do |user, _evaluator|
@@ -26,17 +26,17 @@ FactoryBot.define do
26
26
  end
27
27
  end
28
28
 
29
- factory :identity, class: Ditty::Identity, aliases: [:'Ditty::Identity'] do
29
+ factory :identity, class: 'Ditty::Identity', aliases: [:'Ditty::Identity'] do
30
30
  username { generate :email }
31
31
  crypted_password { 'som3Password!' }
32
32
  end
33
33
 
34
- factory :role, class: Ditty::Role, aliases: [:'Ditty::Role'] do
34
+ factory :role, class: 'Ditty::Role', aliases: [:'Ditty::Role'] do
35
35
  name { "Role #{generate(:name)}" }
36
36
  parent_id { nil }
37
37
  end
38
38
 
39
- factory :user_login_trait, class: Ditty::UserLoginTrait, aliases: [:'Ditty::UserLoginTrait'] do
39
+ factory :user_login_trait, class: 'Ditty::UserLoginTrait', aliases: [:'Ditty::UserLoginTrait'] do
40
40
  association :user, strategy: :create, factory: :user
41
41
  ip_address { Faker::Internet.ip_v4_address }
42
42
  platform { Faker::Device.platform }