ditty 0.9.1 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
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 }