tb_core 1.4.5 → 1.5.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 (46) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +2 -2
  3. data/Rakefile +1 -1
  4. data/app/controllers/admin/password_resets_controller.rb +1 -0
  5. data/app/controllers/admin/roles_controller.rb +1 -3
  6. data/app/controllers/admin/settings_controller.rb +2 -2
  7. data/app/controllers/admin/setup_controller.rb +1 -1
  8. data/app/controllers/admin/users_controller.rb +4 -4
  9. data/app/controllers/concerns/tb_core/error_handling.rb +5 -4
  10. data/app/controllers/concerns/tb_core/redirection.rb +1 -0
  11. data/app/controllers/concerns/tb_core/user_authentication.rb +5 -0
  12. data/app/controllers/user_sessions_controller.rb +2 -2
  13. data/app/helpers/forgot_password_mailer_helper.rb +9 -0
  14. data/app/helpers/tb_core/application_helper.rb +17 -0
  15. data/app/mailers/tb_core_mailer.rb +2 -0
  16. data/app/models/concerns/tb_core/user_model.rb +55 -8
  17. data/app/models/spud_role.rb +2 -7
  18. data/app/views/layouts/admin/application.html.erb +1 -1
  19. data/app/views/tb_core_mailer/forgot_password_notification.html.erb +5 -1
  20. data/lib/generators/spud/controller_spec_generator.rb +1 -1
  21. data/lib/generators/spud/module_generator.rb +4 -4
  22. data/lib/generators/spud/setup_generator.rb +3 -5
  23. data/lib/tb_core/belongs_to_app.rb +2 -1
  24. data/lib/tb_core/engine.rb +1 -3
  25. data/lib/tb_core/form_builder.rb +1 -1
  26. data/lib/tb_core/test_files.rb +3 -3
  27. data/lib/tb_core/test_helper.rb +25 -25
  28. data/lib/tb_core/version.rb +1 -1
  29. data/spec/controllers/admin/application_controller_spec.rb +3 -3
  30. data/spec/controllers/admin/dashboard_controller_spec.rb +1 -1
  31. data/spec/controllers/admin/password_reset_controller_spec.rb +2 -2
  32. data/spec/controllers/admin/settings_controller_spec.rb +1 -1
  33. data/spec/controllers/admin/setup_controller_spec.rb +1 -1
  34. data/spec/controllers/admin/user_sessions_controller_spec.rb +1 -1
  35. data/spec/controllers/admin/users_controller_spec.rb +3 -3
  36. data/spec/dummy/app/assets/config/manifest.js +3 -0
  37. data/spec/dummy/config/application.rb +1 -46
  38. data/spec/dummy/config/initializers/secret_token.rb +0 -1
  39. data/spec/factories/spud_admin_permission_factories.rb +1 -1
  40. data/spec/factories/spud_user_factories.rb +2 -2
  41. data/spec/models/spud_user_spec.rb +2 -2
  42. data/spec/rails_helper.rb +1 -1
  43. data/vendor/assets/rails-validator/rails-validator.js +534 -0
  44. data/vendor/assets/rails-validator/rails-validator.js.map +1 -0
  45. data/vendor/assets/rails-validator/rails-validator.min.js +3 -0
  46. metadata +44 -11
@@ -5,7 +5,7 @@ class Spud::ModuleGenerator < ::Rails::Generators::Base
5
5
  argument :module_name, type: :string
6
6
  argument :attributes, type: :array, default: [], banner: 'field[:type][:index] field[:type][:index]'
7
7
 
8
- source_root File.expand_path('../templates', __FILE__)
8
+ source_root File.expand_path('templates', __dir__)
9
9
 
10
10
  def create_module
11
11
  install_templates
@@ -79,7 +79,7 @@ class Spud::ModuleGenerator < ::Rails::Generators::Base
79
79
  resources :#{module_name_formatted}
80
80
  end
81
81
  resources :#{module_name_formatted}, :only => [:index, :show]
82
- RUBY
82
+ RUBY
83
83
  end
84
84
 
85
85
  def create_specs
@@ -136,7 +136,7 @@ RUBY
136
136
  inject_into_file "app/models/#{module_name.singularize.underscore}.rb", after: "ApplicationRecord\n" do <<-RUBY
137
137
  scope :ordered, -> { order(#{sort_field}: :desc) }
138
138
  scope :search, ->(term) { where('#{search_field} LIKE ?', "%\#{term}%") }
139
- RUBY
139
+ RUBY
140
140
  end
141
141
  end
142
142
 
@@ -156,7 +156,7 @@ RUBY
156
156
 
157
157
  validates #{string_attrs_as_symbols}, :length => {:maximum => 255}
158
158
 
159
- RUBY
159
+ RUBY
160
160
  end
161
161
  end
162
162
  end
@@ -1,6 +1,6 @@
1
1
  class Spud::SetupGenerator < ::Rails::Generators::Base
2
2
  desc 'Base setup for a new TB application'
3
- source_root File.expand_path('../templates', __FILE__)
3
+ source_root File.expand_path('templates', __dir__)
4
4
 
5
5
  def setup
6
6
  if ask(
@@ -16,9 +16,7 @@ class Spud::SetupGenerator < ::Rails::Generators::Base
16
16
  environment(spud_core_configs())
17
17
  rake('railties:install:migrations')
18
18
 
19
- if ask('Migrate the database?', limited_to: ['y', 'n']).downcase == 'y'
20
- rake('db:migrate')
21
- end
19
+ rake('db:migrate') if ask('Migrate the database?', limited_to: ['y', 'n']).downcase == 'y'
22
20
  end
23
21
 
24
22
  private
@@ -49,7 +47,7 @@ private
49
47
  config.site_name = "#{site_name}"
50
48
  config.from_address = "no-reply@#{domain_name}.com"
51
49
  end
52
- RUBY
50
+ RUBY
53
51
  end
54
52
 
55
53
  def application_name
@@ -18,7 +18,7 @@ module TbCore
18
18
  # only: An array of controller actions you want this to apply to (optional)
19
19
  #
20
20
  def belongs_to_app(name, page_title: nil, only: nil)
21
- before_action ->(){
21
+ before_action ->{
22
22
  act_as_app(name, page_title: page_title)
23
23
  }, only: only
24
24
  end
@@ -38,6 +38,7 @@ module TbCore
38
38
  elsif !current_user.can_view_app?(@page_application)
39
39
  raise AccessDeniedError.new(item: 'module', template: '/layouts/admin/error_page')
40
40
  end
41
+
41
42
  @page_thumbnail = @page_application[:thumbnail]
42
43
  @page_name = determine_page_name(page_title || @page_application[:name], action_name)
43
44
  end
@@ -49,9 +49,7 @@ module TbCore
49
49
 
50
50
  initializer 'tb_core.catch_all_route' do |config|
51
51
  # Handle 404 errors if Spud::Cms is not installed
52
- unless defined?(Spud::Cms)
53
- config.routes_reloader.paths << File.expand_path('../catch_all_route.rb', __FILE__)
54
- end
52
+ config.routes_reloader.paths << File.expand_path('catch_all_route.rb', __dir__) unless defined?(Spud::Cms)
55
53
  end
56
54
 
57
55
  initializer 'tb_core.assets' do
@@ -219,7 +219,7 @@ class TbCore::FormBuilder < ActionView::Helpers::FormBuilder
219
219
  # Builds a file field group
220
220
  #
221
221
  def tb_file_field(attribute, options={})
222
- tb_input_field(attribute) do
222
+ tb_input_field(attribute, nil, options) do
223
223
  file_field(attribute)
224
224
  end
225
225
  end
@@ -9,15 +9,15 @@ module Spud
9
9
  end
10
10
 
11
11
  def load_specs
12
- Dir[File.join(File.expand_path('../../../', __FILE__), 'spec/**/*_spec.rb')].each {|f| require f}
12
+ Dir[File.join(File.expand_path('../..', __dir__), 'spec/**/*_spec.rb')].each {|f| require f}
13
13
  end
14
14
 
15
15
  def load_factories
16
- Dir[File.join(File.expand_path('../../../', __FILE__), 'factories/*')].each {|f| require f}
16
+ Dir[File.join(File.expand_path('../..', __dir__), 'factories/*')].each {|f| require f}
17
17
  end
18
18
 
19
19
  def load_support
20
- Dir[File.join(File.expand_path('../../../', __FILE__), 'spec/support/**/*.rb')].each {|f| require f}
20
+ Dir[File.join(File.expand_path('../..', __dir__), 'spec/support/**/*.rb')].each {|f| require f}
21
21
  end
22
22
  end
23
23
  end
@@ -1,29 +1,31 @@
1
1
  require 'authlogic/test_case'
2
- include Authlogic::TestCase
3
2
 
4
- module TbCore::SessionHelper
3
+ module TbCore
4
+ module TestHelper
5
+ include Authlogic::TestCase
5
6
 
6
- # Use this helper in controller specs to establish a login session
7
- # - admin: Set to true to create a super_admin
8
- # - permissions: One or more permissions you want to assign to the user (a role will be auto generated)
9
- #
10
- def activate_session(admin: false, permissions: nil)
11
- activate_authlogic()
12
- if permissions
13
- permissions = [permissions] unless permissions.is_a?(Array)
14
- role = SpudRole.create(name: 'New Role', permission_tags: permissions)
15
- else
16
- role = nil
7
+ # Use this helper in controller specs to establish a login session
8
+ # - admin: Set to true to create a super_admin
9
+ # - permissions: One or more permissions you want to assign to the user (a role will be auto generated)
10
+ #
11
+ def activate_session(admin: false, permissions: nil)
12
+ activate_authlogic()
13
+ if permissions
14
+ permissions = [permissions] unless permissions.is_a?(Array)
15
+ role = SpudRole.create(name: 'New Role', permission_tags: permissions)
16
+ else
17
+ role = nil
18
+ end
19
+ @user = FactoryBot.create(:spud_user, super_admin: admin, role: role)
20
+ SpudUserSession.create(@user)
21
+ return @user
17
22
  end
18
- @user = FactoryBot.create(:spud_user, super_admin: admin, role: role)
19
- SpudUserSession.create(@user)
20
- return @user
21
- end
22
23
 
23
- # Returns the current user
24
- #
25
- def current_user
26
- return @user
24
+ # Returns the current user
25
+ #
26
+ def current_user
27
+ return @user
28
+ end
27
29
  end
28
30
  end
29
31
 
@@ -31,12 +33,10 @@ end
31
33
  #
32
34
  if defined?(RSpec)
33
35
  RSpec.configure do |config|
34
- config.include TbCore::SessionHelper
36
+ config.include TbCore::TestHelper
35
37
  end
36
38
  end
37
39
 
38
40
  # Auto load the spud_user factory if FactoryBot is in use
39
41
  #
40
- if defined?(FactoryBot)
41
- Dir[TbCore::Engine.root.join('spec/factories/spud_user_factories.rb')].each{ |f| require f }
42
- end
42
+ Dir[TbCore::Engine.root.join('spec/factories/spud_user_factories.rb')].each{ |f| require f } if defined?(FactoryBot)
@@ -1,3 +1,3 @@
1
1
  module TbCore
2
- VERSION = '1.4.5'.freeze
2
+ VERSION = '1.5.1'.freeze
3
3
  end
@@ -17,9 +17,9 @@ RSpec.describe Admin::ApplicationController, type: :controller do
17
17
  end
18
18
 
19
19
  it 'should respond successfully if the current user is a super admin' do
20
- @user.update_attributes(super_admin: true)
20
+ @user.update(super_admin: true)
21
21
  get :index
22
- expect(response).to be_success
22
+ expect(response).to be_successful
23
23
  end
24
24
 
25
25
  it 'should respond successfully if the current user has admin permissions' do
@@ -28,7 +28,7 @@ RSpec.describe Admin::ApplicationController, type: :controller do
28
28
  @user.role = @role
29
29
  @user.save
30
30
  get :index
31
- expect(response).to be_success
31
+ expect(response).to be_successful
32
32
  end
33
33
 
34
34
  it 'should redirect to the login if the current user is not logged in' do
@@ -69,7 +69,7 @@ RSpec.describe Admin::DashboardController, type: :controller do
69
69
  it 'should contain data array in reponse' do
70
70
  get :badges
71
71
  json = JSON.parse(response.body)
72
- expect(response.content_type).to eq('application/json')
72
+ expect(response.media_type).to eq('application/json')
73
73
  expect(json).to have_key('data')
74
74
  end
75
75
 
@@ -8,7 +8,7 @@ describe Admin::PasswordResetsController, type: :controller do
8
8
  describe 'index' do
9
9
  it 'should return success' do
10
10
  get :index
11
- expect(response).to be_success
11
+ expect(response).to be_successful
12
12
  end
13
13
  end
14
14
 
@@ -17,7 +17,7 @@ describe Admin::PasswordResetsController, type: :controller do
17
17
  it 'should render the edit form' do
18
18
  allow(SpudUser).to receive(:find_using_perishable_token).and_return(user)
19
19
  get :show, params: { id: 1 }
20
- expect(response).to be_success
20
+ expect(response).to be_successful
21
21
  end
22
22
  end
23
23
 
@@ -8,7 +8,7 @@ describe Admin::SettingsController, type: :controller do
8
8
  describe 'edit' do
9
9
  it 'should respond with success' do
10
10
  get :edit
11
- expect(response).to be_success
11
+ expect(response).to be_successful
12
12
  end
13
13
  end
14
14
 
@@ -6,7 +6,7 @@ describe Admin::SetupController, type: :controller do
6
6
  it 'should be successful' do
7
7
  get :new
8
8
 
9
- expect(response).to be_success
9
+ expect(response).to be_successful
10
10
  end
11
11
 
12
12
  it 'should redirect to the admin login form when there is already a user' do
@@ -16,7 +16,7 @@ describe Admin::UserSessionsController, type: :controller do
16
16
  u = FactoryBot.create(:spud_user)
17
17
  u.save
18
18
  get :new
19
- expect(response).to be_success
19
+ expect(response).to be_successful
20
20
  end
21
21
  end
22
22
 
@@ -39,7 +39,7 @@ describe Admin::UsersController, type: :controller do
39
39
  SpudUserSession.create(u)
40
40
  get :index
41
41
 
42
- expect(response).to be_success
42
+ expect(response).to be_successful
43
43
  end
44
44
 
45
45
  it 'should not allow access to users without a role,
@@ -82,14 +82,14 @@ describe Admin::UsersController, type: :controller do
82
82
  it 'should respond successfully' do
83
83
  user = FactoryBot.create(:spud_user)
84
84
  get :show, params: { id: user.id }
85
- expect(response).to be_success
85
+ expect(response).to be_successful
86
86
  end
87
87
  end
88
88
 
89
89
  describe 'new' do
90
90
  it 'should render the form' do
91
91
  get :new, format: :html
92
- expect(response).to be_success
92
+ expect(response).to be_successful
93
93
  end
94
94
  end
95
95
 
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css
@@ -12,51 +12,6 @@ module Dummy
12
12
  config.from_address = 'no-reply@dummy.com'
13
13
  end
14
14
 
15
- # Settings in config/environments/* take precedence over those specified here.
16
- # Application configuration should go into files in config/initializers
17
- # -- all .rb files in that directory are automatically loaded.
18
-
19
- # Custom directories with classes and modules you want to be autoloadable.
20
- # config.autoload_paths += %W(#{config.root}/extras)
21
-
22
- # Only load the plugins named here, in the order given (default is alphabetical).
23
- # :all can be used as a placeholder for all plugins not explicitly named.
24
- # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
25
-
26
- # Activate observers that should always be running.
27
- # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
28
-
29
- # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
30
- # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
31
- # config.time_zone = 'Central Time (US & Canada)'
32
-
33
- # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
34
- # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
35
- # config.i18n.default_locale = :de
36
- config.i18n.enforce_available_locales = true
37
-
38
- # Configure the default encoding used in templates for Ruby 1.9.
39
- config.encoding = 'utf-8'
40
-
41
- # Configure sensitive parameters which will be filtered from the log file.
42
- config.filter_parameters += [:password]
43
-
44
- # Use SQL instead of Active Record's schema dumper when creating the database.
45
- # This is necessary if your schema can't be completely dumped by the schema dumper,
46
- # like if you have constraints or database-specific column types
47
- # config.active_record.schema_format = :sql
48
-
49
- # Enforce whitelist mode for mass assignment.
50
- # This will create an empty whitelist of attributes available for mass-assignment for all models
51
- # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
52
- # parameters by using an attr_accessible or attr_protected declaration.
53
- # config.active_record.whitelist_attributes = true
54
-
55
- # Enable the asset pipeline
56
- # config.assets.enabled = true
57
-
58
- # Version of your assets, change this if you want to expire all your assets
59
- # config.assets.version = '1.0'
60
-
15
+ config.load_defaults '6.0'
61
16
  end
62
17
  end
@@ -4,5 +4,4 @@
4
4
  # If you change this key, all old signed cookies will become invalid!
5
5
  # Make sure the secret is at least 30 characters and all random,
6
6
  # no regular words or you'll be exposed to dictionary attacks.
7
- Dummy::Application.config.secret_token = 'f89bbe176924c011473e15af21c4e8b72c3fd12f887f26014c24ea32e1e3d2bf0250a671a30232047d5c5431e67c7361aca07b10d847405b99fce4543589e117'
8
7
  Dummy::Application.config.secret_key_base = 'b43711419c807b9c9efe9365dd79f87ebe31e9f72e1aa6744c1ecac68aace664c0a3a4f791d92bd2fa324fce1d6d09701a54b88453f131b7c460eeb9c530bd72'
@@ -5,6 +5,6 @@ FactoryBot.define do
5
5
 
6
6
  factory :spud_admin_permission do
7
7
  name { FactoryBot.generate(:permission_name) }
8
- access true
8
+ access { true }
9
9
  end
10
10
  end
@@ -24,8 +24,8 @@ FactoryBot.define do
24
24
  last_name { FactoryBot.generate(:last_name) }
25
25
  login { FactoryBot.generate(:login) }
26
26
  email { FactoryBot.generate(:email) }
27
- password 'password'
28
- password_confirmation 'password'
27
+ password { 'password' }
28
+ password_confirmation { 'password' }
29
29
  single_access_token { FactoryBot.generate(:single_access_token) }
30
30
  end
31
31
  end
@@ -66,14 +66,14 @@ describe SpudUser, type: :model do
66
66
  it 'should not unset the requires_password_change flag' do
67
67
  u = FactoryBot.create(:spud_user, requires_password_change: true)
68
68
  expect{
69
- u.update_attributes(first_name: 'updated')
69
+ u.update(first_name: 'updated')
70
70
  }.to_not change(u, :requires_password_change)
71
71
  end
72
72
 
73
73
  it 'should unset the requires_password_change flag' do
74
74
  u = FactoryBot.create(:spud_user, requires_password_change: true)
75
75
  expect{
76
- u.update_attributes(password: 'new password', password_confirmation: 'new password')
76
+ u.update(password: 'new password', password_confirmation: 'new password')
77
77
  u.reload
78
78
  }.to change(u, :requires_password_change).from(true).to(false)
79
79
  end
@@ -4,7 +4,7 @@ SimpleCov.start 'rails'
4
4
  # This file is copied to spec/ when you run 'rails generate rspec:install'
5
5
  ENV['RAILS_ENV'] ||= 'test'
6
6
 
7
- require File.expand_path('../dummy/config/environment.rb', __FILE__)
7
+ require File.expand_path('dummy/config/environment.rb', __dir__)
8
8
  require 'spec_helper'
9
9
  require 'tb_core/test_helper'
10
10
  require 'rails-controller-testing'
@@ -0,0 +1,534 @@
1
+ /******/ (function(modules) { // webpackBootstrap
2
+ /******/ // The module cache
3
+ /******/ var installedModules = {};
4
+ /******/
5
+ /******/ // The require function
6
+ /******/ function __webpack_require__(moduleId) {
7
+ /******/
8
+ /******/ // Check if module is in cache
9
+ /******/ if(installedModules[moduleId]) {
10
+ /******/ return installedModules[moduleId].exports;
11
+ /******/ }
12
+ /******/ // Create a new module (and put it into the cache)
13
+ /******/ var module = installedModules[moduleId] = {
14
+ /******/ i: moduleId,
15
+ /******/ l: false,
16
+ /******/ exports: {}
17
+ /******/ };
18
+ /******/
19
+ /******/ // Execute the module function
20
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21
+ /******/
22
+ /******/ // Flag the module as loaded
23
+ /******/ module.l = true;
24
+ /******/
25
+ /******/ // Return the exports of the module
26
+ /******/ return module.exports;
27
+ /******/ }
28
+ /******/
29
+ /******/
30
+ /******/ // expose the modules object (__webpack_modules__)
31
+ /******/ __webpack_require__.m = modules;
32
+ /******/
33
+ /******/ // expose the module cache
34
+ /******/ __webpack_require__.c = installedModules;
35
+ /******/
36
+ /******/ // define getter function for harmony exports
37
+ /******/ __webpack_require__.d = function(exports, name, getter) {
38
+ /******/ if(!__webpack_require__.o(exports, name)) {
39
+ /******/ Object.defineProperty(exports, name, {
40
+ /******/ configurable: false,
41
+ /******/ enumerable: true,
42
+ /******/ get: getter
43
+ /******/ });
44
+ /******/ }
45
+ /******/ };
46
+ /******/
47
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
48
+ /******/ __webpack_require__.n = function(module) {
49
+ /******/ var getter = module && module.__esModule ?
50
+ /******/ function getDefault() { return module['default']; } :
51
+ /******/ function getModuleExports() { return module; };
52
+ /******/ __webpack_require__.d(getter, 'a', getter);
53
+ /******/ return getter;
54
+ /******/ };
55
+ /******/
56
+ /******/ // Object.prototype.hasOwnProperty.call
57
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
58
+ /******/
59
+ /******/ // __webpack_public_path__
60
+ /******/ __webpack_require__.p = "";
61
+ /******/
62
+ /******/ // Load entry module and return exports
63
+ /******/ return __webpack_require__(__webpack_require__.s = 1);
64
+ /******/ })
65
+ /************************************************************************/
66
+ /******/ ([
67
+ /* 0 */
68
+ /***/ (function(module, exports, __webpack_require__) {
69
+
70
+ "use strict";
71
+
72
+
73
+ Object.defineProperty(exports, "__esModule", {
74
+ value: true
75
+ });
76
+ /*
77
+ * Build a path for redirection.
78
+ *
79
+ * - Adds a timestamp GET param for cache busting
80
+ * - Inserts an ID attribute if necessary
81
+ */
82
+ var buildSuccessPath = exports.buildSuccessPath = function buildSuccessPath(path, json) {
83
+ var timestamp = new Date().valueOf();
84
+ if (path.indexOf('?') > -1) {
85
+ path += '&t=' + timestamp;
86
+ } else {
87
+ path += '?t=' + timestamp;
88
+ }
89
+ if (json && json.id) {
90
+ path = path.replace(':id', json.id);
91
+ }
92
+ return path;
93
+ };
94
+
95
+ /*
96
+ * Remote element
97
+ */
98
+ var removeElement = exports.removeElement = function removeElement(element) {
99
+ element.parentElement.removeChild(element);
100
+ };
101
+
102
+ /*
103
+ * Create an error element
104
+ */
105
+ var createErrorParagraph = exports.createErrorParagraph = function createErrorParagraph(text, base) {
106
+ var p = document.createElement('p');
107
+ p.classList.add('form-error');
108
+ p.classList.add(base ? 'form-error-base' : 'form-error-inline');
109
+ p.innerText = text;
110
+ return p;
111
+ };
112
+
113
+ /*
114
+ * Convert a query into an array of elements
115
+ */
116
+ var querySelectorArray = exports.querySelectorArray = function querySelectorArray(parent, selector) {
117
+ var query = parent.querySelectorAll(selector);
118
+ var array = Array.prototype.slice.call(query);
119
+ return array;
120
+ };
121
+
122
+ /*
123
+ * Take a single error message and format it as if it were
124
+ * a json error coming from the server
125
+ */
126
+ var formatStringAsErrors = exports.formatStringAsErrors = function formatStringAsErrors(errorString) {
127
+ return { base: [errorString] };
128
+ };
129
+
130
+ /*
131
+ * Return true if the element is in view
132
+ */
133
+ var elementIsInView = exports.elementIsInView = function elementIsInView(el) {
134
+ var rect = el.getBoundingClientRect();
135
+ var w = window,
136
+ d = document;
137
+ return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (w.innerHeight || d.documentElement.clientHeight) && rect.right <= (w.innerWidth || d.documentElement.clientWidth);
138
+ };
139
+
140
+ /***/ }),
141
+ /* 1 */
142
+ /***/ (function(module, exports, __webpack_require__) {
143
+
144
+ "use strict";
145
+
146
+
147
+ var _handlers = __webpack_require__(2);
148
+
149
+ var handlers = _interopRequireWildcard(_handlers);
150
+
151
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
152
+
153
+ var MODULE_LOADED = '_rails_validator_loaded';
154
+
155
+ var init = function init() {
156
+ window[MODULE_LOADED] = true;
157
+ addListener('form', 'ajax:before', handlers.onRemoteFormBefore);
158
+ addListener('form[data-success], a[data-success]', 'ajax:success', handlers.onRemoteFormSuccess);
159
+ addListener('form[data-errors]', 'ajax:error', handlers.onRemoteFormErrors);
160
+ };
161
+
162
+ var addListener = function addListener(selector, event, fn) {
163
+ document.addEventListener(event, function (e) {
164
+ if (e.target.matches(selector)) {
165
+ e.stopPropagation();
166
+ fn.call(e.target, e);
167
+ }
168
+ });
169
+ };
170
+
171
+ if (!window[MODULE_LOADED]) {
172
+ init();
173
+ }
174
+
175
+ // https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
176
+ if (!Element.prototype.matches) {
177
+ Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
178
+ }
179
+
180
+ /***/ }),
181
+ /* 2 */
182
+ /***/ (function(module, exports, __webpack_require__) {
183
+
184
+ "use strict";
185
+
186
+
187
+ Object.defineProperty(exports, "__esModule", {
188
+ value: true
189
+ });
190
+ exports.onRemoteFormErrors = exports.onRemoteFormSuccess = exports.onRemoteFormBefore = undefined;
191
+
192
+ var _errors = __webpack_require__(3);
193
+
194
+ var errors = _interopRequireWildcard(_errors);
195
+
196
+ var _helpers = __webpack_require__(0);
197
+
198
+ var helpers = _interopRequireWildcard(_helpers);
199
+
200
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
201
+
202
+ /*
203
+ * Tell the server we want a json response
204
+ */
205
+ var onRemoteFormBefore = exports.onRemoteFormBefore = function onRemoteFormBefore(event) {
206
+ var form = event.target;
207
+ if (form.getAttribute('data-type') === null) {
208
+ form.setAttribute('data-type', 'json');
209
+ }
210
+ };
211
+
212
+ /*
213
+ * Called when a remote form is submitted with a data-success configured
214
+ * Redirects to the configured path, or reloads the page if "reload" is passed
215
+ *
216
+ * ie:
217
+ * <form action="..." data-remote="true" data-success="/path/for/success/:id">
218
+ */
219
+ var onRemoteFormSuccess = exports.onRemoteFormSuccess = function onRemoteFormSuccess(event) {
220
+ var form = event.target;
221
+ var successPath = form.getAttribute('data-success');
222
+ if (successPath === 'reload') {
223
+ window.location.reload(true);
224
+ } else {
225
+ var json = event.detail[0];
226
+ window.location = helpers.buildSuccessPath(successPath, json);
227
+ }
228
+ };
229
+
230
+ /*
231
+ * Called when a remote form is submitted and the server has returned an
232
+ * error, only if the form has a data-errors attribute
233
+ *
234
+ * ie:
235
+ * <form action="..." data-remote="true" data-errors="inline">
236
+ * - OR -
237
+ * <form action="..." data-remote="true" data-errors="alert">
238
+ */
239
+ var onRemoteFormErrors = exports.onRemoteFormErrors = function onRemoteFormErrors(event) {
240
+ var json = event.detail[0];
241
+ var xhr = event.detail[2];
242
+ var form = event.target;
243
+ var errorType = form.getAttribute('data-errors');
244
+
245
+ if (xhr.status === 422) {
246
+ if (errorType === 'inline') {
247
+ errors.displayErrorsInline(form, json);
248
+ } else {
249
+ errors.displayErrorsAlert(json);
250
+ }
251
+ } else if (xhr.status === 401 || xhr.status === 403) {
252
+ window.alert('Error: Access denied.');
253
+ } else {
254
+ var errorThrown = event.detail[1];
255
+ window.alert('An unexpected error occurred: ' + errorThrown);
256
+ }
257
+ };
258
+
259
+ /***/ }),
260
+ /* 3 */
261
+ /***/ (function(module, exports, __webpack_require__) {
262
+
263
+ "use strict";
264
+
265
+
266
+ Object.defineProperty(exports, "__esModule", {
267
+ value: true
268
+ });
269
+ exports.displayErrorsAlert = exports.displayErrorsInline = exports.clearErrors = undefined;
270
+
271
+ var _helpers = __webpack_require__(0);
272
+
273
+ var helpers = _interopRequireWildcard(_helpers);
274
+
275
+ var _case = __webpack_require__(4);
276
+
277
+ var _case2 = _interopRequireDefault(_case);
278
+
279
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
280
+
281
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
282
+
283
+ /*
284
+ * Remove inline error messages
285
+ */
286
+ var clearErrors = exports.clearErrors = function clearErrors(form) {
287
+ helpers.querySelectorArray(form, '.form-error-inline, .form-error-base').forEach(helpers.removeElement);
288
+ };
289
+
290
+ /*
291
+ * Append error text to form input fields and scroll to the first error
292
+ */
293
+ var displayErrorsInline = exports.displayErrorsInline = function displayErrorsInline(form, json) {
294
+ if (typeof json === 'string') {
295
+ json = helpers.formatStringAsErrors(json);
296
+ }
297
+
298
+ clearErrors(form);
299
+ var errors = json.errors;
300
+
301
+ for (var key in errors) {
302
+ var messages = errors[key];
303
+ if (key === 'base') {
304
+ for (var i = 0; i < messages.length; i++) {
305
+ var p = helpers.createErrorParagraph(messages[i], true);
306
+ form.insertBefore(p, form.firstChild);
307
+ }
308
+ } else {
309
+ var input = void 0,
310
+ keyArr = void 0;
311
+ keyArr = key.split('.');
312
+
313
+ if (keyArr.length === 2) {
314
+ keyArr[0] += '_attributes';
315
+ input = form.querySelector('[name$=\'[' + keyArr.join('][') + ']\']');
316
+ } else {
317
+ var array = helpers.querySelectorArray(form, '[name$=\'[' + key + ']\']');
318
+ input = array.filter(function (input) {
319
+ // Filter out nested attribute inputs ie model[a][b]
320
+ return !input.name.match(/\]\[/);
321
+ })[0];
322
+ if (input === null && !key.match(/_id$/)) {
323
+ input = form.querySelector('[name$=\'[' + key + '_id]\']');
324
+ }
325
+ }
326
+
327
+ var message = messages[0];
328
+ if (input) {
329
+ input.parentNode.appendChild(helpers.createErrorParagraph(message));
330
+ } else {
331
+ // eslint-disable-next-line no-console
332
+ console.warn('Missing input field for key:', key);
333
+ form.insertBefore(helpers.createErrorParagraph(_case2.default.title(key) + ' ' + message), form.firstChild);
334
+ }
335
+ }
336
+ }
337
+
338
+ var firstError = form.querySelector('.form-error-inline, .form-error-base');
339
+ if (firstError && !helpers.elementIsInView(firstError) && typeof firstError.scrollIntoView === 'function') {
340
+ firstError.parentElement.scrollIntoView();
341
+ }
342
+ };
343
+
344
+ /*
345
+ * Display errors in a standard window.alert dialog
346
+ */
347
+ var displayErrorsAlert = exports.displayErrorsAlert = function displayErrorsAlert(json) {
348
+ if (typeof json === 'string') {
349
+ json = helpers.formatStringAsErrors(json);
350
+ }
351
+ var text = 'Please correct the following errors:\n';
352
+ var message = void 0,
353
+ errors = json.errors;
354
+ for (var key in errors) {
355
+ message = errors[key][0];
356
+ if (key === 'base') {
357
+ text += ' - ' + message + '\n';
358
+ } else {
359
+ text += ' - ' + _case2.default.title(key) + ' ' + message + '\n';
360
+ }
361
+ }
362
+ window.alert(text);
363
+ };
364
+
365
+ /***/ }),
366
+ /* 4 */
367
+ /***/ (function(module, exports) {
368
+
369
+ /*! Case - v1.5.3 - 2017-07-11
370
+ * Copyright (c) 2017 Nathan Bubna; Licensed MIT, GPL */
371
+ (function() {
372
+ "use strict";
373
+ var unicodes = function(s, prefix) {
374
+ prefix = prefix || '';
375
+ return s.replace(/(^|-)/g, '$1\\u'+prefix).replace(/,/g, '\\u'+prefix);
376
+ },
377
+ basicSymbols = unicodes('20-26,28-2F,3A-40,5B-60,7B-7E,A0-BF,D7,F7', '00'),
378
+ baseLowerCase = 'a-z'+unicodes('DF-F6,F8-FF', '00'),
379
+ baseUpperCase = 'A-Z'+unicodes('C0-D6,D8-DE', '00'),
380
+ improperInTitle = 'A|An|And|As|At|But|By|En|For|If|In|Of|On|Or|The|To|Vs?\\.?|Via',
381
+ regexps = function(symbols, lowers, uppers, impropers) {
382
+ symbols = symbols || basicSymbols;
383
+ lowers = lowers || baseLowerCase;
384
+ uppers = uppers || baseUpperCase;
385
+ impropers = impropers || improperInTitle;
386
+ return {
387
+ capitalize: new RegExp('(^|['+symbols+'])(['+lowers+'])', 'g'),
388
+ pascal: new RegExp('(^|['+symbols+'])+(['+lowers+uppers+'])', 'g'),
389
+ fill: new RegExp('['+symbols+']+(.|$)','g'),
390
+ sentence: new RegExp('(^\\s*|[\\?\\!\\.]+"?\\s+"?|,\\s+")(['+lowers+'])', 'g'),
391
+ improper: new RegExp('\\b('+impropers+')\\b', 'g'),
392
+ relax: new RegExp('([^'+uppers+'])(['+uppers+']*)(['+uppers+'])(?=['+lowers+']|$)', 'g'),
393
+ upper: new RegExp('^[^'+lowers+']+$'),
394
+ hole: /[^\s]\s[^\s]/,
395
+ apostrophe: /'/g,
396
+ room: new RegExp('['+symbols+']')
397
+ };
398
+ },
399
+ re = regexps(),
400
+ _ = {
401
+ re: re,
402
+ unicodes: unicodes,
403
+ regexps: regexps,
404
+ types: [],
405
+ up: String.prototype.toUpperCase,
406
+ low: String.prototype.toLowerCase,
407
+ cap: function(s) {
408
+ return _.up.call(s.charAt(0))+s.slice(1);
409
+ },
410
+ decap: function(s) {
411
+ return _.low.call(s.charAt(0))+s.slice(1);
412
+ },
413
+ deapostrophe: function(s) {
414
+ return s.replace(re.apostrophe, '');
415
+ },
416
+ fill: function(s, fill, deapostrophe) {
417
+ if (fill != null) {
418
+ s = s.replace(re.fill, function(m, next) {
419
+ return next ? fill + next : '';
420
+ });
421
+ }
422
+ if (deapostrophe) {
423
+ s = _.deapostrophe(s);
424
+ }
425
+ return s;
426
+ },
427
+ prep: function(s, fill, pascal, upper) {
428
+ s = s == null ? '' : s + '';// force to string
429
+ if (!upper && re.upper.test(s)) {
430
+ s = _.low.call(s);
431
+ }
432
+ if (!fill && !re.hole.test(s)) {
433
+ var holey = _.fill(s, ' ');
434
+ if (re.hole.test(holey)) {
435
+ s = holey;
436
+ }
437
+ }
438
+ if (!pascal && !re.room.test(s)) {
439
+ s = s.replace(re.relax, _.relax);
440
+ }
441
+ return s;
442
+ },
443
+ relax: function(m, before, acronym, caps) {
444
+ return before + ' ' + (acronym ? acronym+' ' : '') + caps;
445
+ }
446
+ },
447
+ Case = {
448
+ _: _,
449
+ of: function(s) {
450
+ for (var i=0,m=_.types.length; i<m; i++) {
451
+ if (Case[_.types[i]].apply(Case, arguments) === s){ return _.types[i]; }
452
+ }
453
+ },
454
+ flip: function(s) {
455
+ return s.replace(/\w/g, function(l) {
456
+ return (l == _.up.call(l) ? _.low : _.up).call(l);
457
+ });
458
+ },
459
+ random: function(s) {
460
+ return s.replace(/\w/g, function(l) {
461
+ return (Math.round(Math.random()) ? _.up : _.low).call(l);
462
+ });
463
+ },
464
+ type: function(type, fn) {
465
+ Case[type] = fn;
466
+ _.types.push(type);
467
+ }
468
+ },
469
+ types = {
470
+ lower: function(s, fill, deapostrophe) {
471
+ return _.fill(_.low.call(_.prep(s, fill)), fill, deapostrophe);
472
+ },
473
+ snake: function(s) {
474
+ return Case.lower(s, '_', true);
475
+ },
476
+ constant: function(s) {
477
+ return Case.upper(s, '_', true);
478
+ },
479
+ camel: function(s) {
480
+ return _.decap(Case.pascal(s));
481
+ },
482
+ kebab: function(s) {
483
+ return Case.lower(s, '-', true);
484
+ },
485
+ header: function(s) {
486
+ return Case.capital(s, '-', true);
487
+ },
488
+ upper: function(s, fill, deapostrophe) {
489
+ return _.fill(_.up.call(_.prep(s, fill, false, true)), fill, deapostrophe);
490
+ },
491
+ capital: function(s, fill, deapostrophe) {
492
+ return _.fill(_.prep(s).replace(re.capitalize, function(m, border, letter) {
493
+ return border+_.up.call(letter);
494
+ }), fill, deapostrophe);
495
+ },
496
+ pascal: function(s) {
497
+ return _.fill(_.prep(s, false, true).replace(re.pascal, function(m, border, letter) {
498
+ return _.up.call(letter);
499
+ }), '', true);
500
+ },
501
+ title: function(s) {
502
+ return Case.capital(s).replace(re.improper, function(small, p, i, s) {
503
+ return i > 0 && i < s.lastIndexOf(' ') ? _.low.call(small) : small;
504
+ });
505
+ },
506
+ sentence: function(s, names) {
507
+ s = Case.lower(s).replace(re.sentence, function(m, prelude, letter) {
508
+ return prelude + _.up.call(letter);
509
+ });
510
+ if (names) {
511
+ names.forEach(function(name) {
512
+ s = s.replace(new RegExp('\\b'+Case.lower(name)+'\\b', "g"), _.cap);
513
+ });
514
+ }
515
+ return s;
516
+ }
517
+ };
518
+
519
+ // TODO: Remove "squish" in a future breaking release.
520
+ types.squish = types.pascal;
521
+
522
+ for (var type in types) {
523
+ Case.type(type, types[type]);
524
+ }
525
+ // export Case (AMD, commonjs, or global)
526
+ var define = typeof define === "function" ? define : function(){};
527
+ define(typeof module === "object" && module.exports ? module.exports = Case : this.Case = Case);
528
+
529
+ }).call(this);
530
+
531
+
532
+ /***/ })
533
+ /******/ ]);
534
+ //# sourceMappingURL=rails-validator.js.map