tb_core 1.4.3.1 → 1.4.4

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -54
  3. data/app/assets/javascripts/admin/core/application.js +8 -4
  4. data/app/assets/javascripts/admin/core/dashboard.js +2 -3
  5. data/app/assets/javascripts/admin/core/date_picker.js +2 -2
  6. data/app/assets/javascripts/admin/core/editor.js +2 -2
  7. data/app/assets/javascripts/admin/core/modal.js +1 -1
  8. data/app/assets/javascripts/admin/core/users.js +5 -5
  9. data/app/assets/javascripts/tb_core.js +8 -0
  10. data/app/assets/libs/sortable/sortable.js +152 -91
  11. data/app/controllers/admin/application_controller.rb +1 -2
  12. data/app/controllers/admin/dashboard_controller.rb +2 -2
  13. data/app/controllers/admin/settings_controller.rb +2 -2
  14. data/app/controllers/admin/users_controller.rb +4 -7
  15. data/app/controllers/concerns/tb_core/error_handling.rb +16 -7
  16. data/app/controllers/concerns/tb_core/user_authentication.rb +5 -5
  17. data/app/controllers/tb_core/application_controller.rb +1 -1
  18. data/app/controllers/user_sessions_controller.rb +2 -2
  19. data/app/helpers/tb_core/application_helper.rb +4 -36
  20. data/app/mailers/tb_core_mailer.rb +3 -3
  21. data/app/models/concerns/tb_core/user_model.rb +131 -0
  22. data/app/models/spud/spud_user_model.rb +6 -138
  23. data/app/models/spud_permission.rb +4 -4
  24. data/app/models/spud_role.rb +1 -1
  25. data/app/models/spud_user.rb +2 -1
  26. data/app/views/admin/dashboard/index.html.erb +1 -1
  27. data/app/views/admin/settings/_form.html.erb +1 -1
  28. data/app/views/admin/settings/edit.html.erb +1 -1
  29. data/app/views/admin/setup/new.html.erb +1 -1
  30. data/app/views/admin/users/_form.html.erb +1 -1
  31. data/app/views/admin/users/index.html.erb +2 -2
  32. data/app/views/admin/users/show.html.erb +1 -1
  33. data/app/views/layouts/admin/application.html.erb +6 -6
  34. data/app/views/layouts/admin/login.html.erb +1 -1
  35. data/app/views/tb_core_mailer/forgot_password_notification.html.erb +2 -2
  36. data/app/views/tb_core_mailer/user_credentials.html.erb +2 -2
  37. data/app/views/user_sessions/_form.html.erb +1 -1
  38. data/app/views/user_sessions/new.html.erb +1 -1
  39. data/lib/generators/spud/module_generator.rb +7 -7
  40. data/lib/generators/spud/setup_generator.rb +6 -7
  41. data/lib/generators/spud/templates/assets/admin/application.js +1 -1
  42. data/lib/generators/spud/templates/assets/application.js +3 -4
  43. data/lib/generators/spud/templates/controller_spec.rb.erb +5 -5
  44. data/lib/generators/spud/templates/views/admin/_form.html.erb +1 -1
  45. data/lib/generators/spud/templates/views/admin/index.html.erb +1 -1
  46. data/lib/generators/spud/templates/views/admin/show.html.erb +1 -1
  47. data/lib/generators/spud/templates/views/frontend/index.html.erb +1 -1
  48. data/lib/generators/spud/templates/views/frontend/show.html.erb +1 -1
  49. data/lib/tb_core.rb +3 -5
  50. data/lib/tb_core/belongs_to_app.rb +47 -46
  51. data/lib/tb_core/catch_all_route.rb +3 -0
  52. data/lib/tb_core/configuration.rb +28 -0
  53. data/lib/tb_core/engine.rb +61 -0
  54. data/lib/tb_core/errors.rb +50 -0
  55. data/lib/tb_core/responder.rb +0 -26
  56. data/lib/{spud_core → tb_core}/searchable.rb +4 -1
  57. data/lib/tb_core/spud_core.rb +23 -0
  58. data/lib/{spud_core → tb_core}/test_files.rb +0 -0
  59. data/lib/tb_core/test_helper.rb +3 -3
  60. data/lib/tb_core/version.rb +3 -0
  61. data/spec/controllers/admin/application_controller_spec.rb +5 -5
  62. data/spec/controllers/admin/dashboard_controller_spec.rb +5 -5
  63. data/spec/controllers/admin/password_reset_controller_spec.rb +1 -1
  64. data/spec/controllers/admin/settings_controller_spec.rb +1 -1
  65. data/spec/controllers/admin/setup_controller_spec.rb +2 -2
  66. data/spec/controllers/admin/user_sessions_controller_spec.rb +2 -2
  67. data/spec/controllers/admin/users_controller_spec.rb +17 -17
  68. data/spec/dummy/app/assets/javascripts/admin/application.js +1 -1
  69. data/spec/dummy/app/assets/javascripts/application.js +1 -1
  70. data/spec/dummy/config/application.rb +1 -1
  71. data/spec/factories/spud_admin_permission_factories.rb +2 -2
  72. data/spec/factories/spud_role_factories.rb +2 -2
  73. data/spec/factories/spud_user_factories.rb +6 -6
  74. data/spec/helpers/spud/admin/application_helper_spec.rb +1 -1
  75. data/spec/helpers/tb_core/application_helper_spec.rb +0 -35
  76. data/spec/lib/spud_core/configuration_spec.rb +2 -2
  77. data/spec/lib/tb_core/belongs_to_app_spec.rb +4 -3
  78. data/spec/models/spud_role_spec.rb +2 -2
  79. data/spec/models/spud_user_spec.rb +13 -13
  80. data/spec/rails_helper.rb +1 -1
  81. metadata +59 -67
  82. data/app/assets/javascripts/tb_core.js.erb +0 -9
  83. data/app/assets/javascripts/tb_core/configuration.js.erb +0 -9
  84. data/app/assets/javascripts/tb_core/console.js +0 -12
  85. data/app/assets/javascripts/tb_core/remote.js +0 -237
  86. data/app/assets/javascripts/tb_core/utility.js +0 -55
  87. data/app/controllers/concerns/tb_core/sortable_params.rb +0 -80
  88. data/app/controllers/spud/application_controller.rb +0 -10
  89. data/lib/generators/spud/templates/assets/bootstrap.js +0 -14
  90. data/lib/spud_core/catch_all_route.rb +0 -3
  91. data/lib/spud_core/configuration.rb +0 -56
  92. data/lib/spud_core/engine.rb +0 -76
  93. data/lib/spud_core/errors.rb +0 -47
  94. data/lib/spud_core/version.rb +0 -5
  95. data/lib/tb_core/table_header.rb +0 -92
  96. data/spec/controllers/tb_core/sortable_params_spec.rb +0 -64
@@ -1,9 +0,0 @@
1
- //= require_self
2
- //= require_directory ./tb_core
3
-
4
- (function(){
5
- window.tb = {
6
-
7
- };
8
-
9
- })();
@@ -1,9 +0,0 @@
1
- (function(){
2
-
3
- tb.config = {
4
- env: '<%= Rails.env %>',
5
- siteName: '<%= Spud::Core.config.site_name.gsub("'"){"\\'"} %>',
6
- subUri: '<%= Rails.configuration.relative_url_root %>'
7
- };
8
-
9
- })();
@@ -1,12 +0,0 @@
1
- (function(){
2
-
3
- if(typeof(console) == 'undefined'){
4
- var noop = function(){};
5
- window.console = {
6
- log: noop,
7
- warn: noop,
8
- error: noop
9
- };
10
- }
11
-
12
- })();
@@ -1,237 +0,0 @@
1
- //= require rails-ujs
2
- //= require_self
3
-
4
- (function(){
5
-
6
- /*
7
- * jQuery UJS Handlers
8
- *
9
- * These handlers are designed to work with the Rails/jQuery UJS adapter. This applies
10
- * to things like data-remote forms, data-method links, etc.
11
- *
12
- * For more info see the following lnks:
13
- * - https://github.com/rails/jquery-ujs/wiki/ajax
14
- * - http://guides.rubyonrails.org/working_with_javascript_in_rails.html
15
- *
16
- */
17
- tb.remote = {
18
- init: function(){
19
- Rails.delegate(document, 'form', 'ajax:before', onRemoteFormBefore);
20
- Rails.delegate(document, 'form[data-success], a[data-success]', 'ajax:success', onRemoteFormSuccess);
21
- Rails.delegate(document, 'form[data-errors]', 'ajax:error', onRemoteFormErrors);
22
- Rails.delegate(document, 'tr a[data-method=delete][data-remote=true]', 'ajax:success', onRemoteDeleteTableRow);
23
- }
24
- };
25
-
26
- /*
27
- * UJS likes to request application/javascript by default, but Twice Baked prefers JSON.
28
- */
29
- var onRemoteFormBefore = function(event){
30
- if(this.getAttribute('data-type') === null){
31
- this.setAttribute('data-type', 'json');
32
- }
33
- };
34
-
35
- /*
36
- * Called when a remote form is submitted with a data-success configured
37
- * Simply redirects to the configured path, or reloads the page if "reload" is passed
38
- *
39
- * ie:
40
- * <form action="..." data-remote="true" data-success="/path/for/success/:id">
41
- */
42
- var onRemoteFormSuccess = function(event){
43
- var successPath = this.getAttribute('data-success');
44
- if(successPath === 'reload'){
45
- window.location.reload(true);
46
- }
47
- else{
48
- var json = event.detail[0];
49
- window.location = buildSuccessPath(successPath, json);
50
- }
51
- };
52
-
53
- /*
54
- * Build a path for redirection.
55
- *
56
- * - Adds a timestamp GET param for cache busting
57
- * - Inserts an ID attribute if necessary
58
- *
59
- */
60
- var buildSuccessPath = function(path, json){
61
- var timestamp = (new Date()).valueOf();
62
- if(path.indexOf('?') > -1){
63
- path += ('&t=' + timestamp);
64
- }
65
- else{
66
- path += ('?t=' + timestamp);
67
- }
68
- if(json && json.id){
69
- path = path.replace(':id', json.id);
70
- }
71
- return path;
72
- };
73
-
74
- /*
75
- * Called when a remote form is submitted and the server has returned an error, only
76
- * if the form has a data-errors attribute
77
- *
78
- * ie:
79
- * <form action="..." data-remote="true" data-errors="inline">
80
- * - OR -
81
- * <form action="..." data-remote="true" data-errors="alert">
82
- */
83
- var onRemoteFormErrors = function(event){
84
- var json = event.detail[0];
85
- var xhr = event.detail[2];
86
- var form = this;
87
- var errorType = form.getAttribute('data-errors');
88
-
89
- if(xhr.status === 422){
90
- if(errorType === 'inline'){
91
- tb.remote.displayErrorsInline(form, json);
92
- }
93
- else{
94
- tb.remote.displayErrorsAlert(json);
95
- }
96
- }
97
- else if(xhr.status === 401 || xhr.status === 403){
98
- window.alert('Error: Access denied.');
99
- }
100
- else if(json.location) {
101
- window.location = json.location;
102
- }
103
- else{
104
- var errorThrown = event.detail[1];
105
- window.alert('An unexpected error occurred: ' + errorThrown);
106
- }
107
- };
108
-
109
- function removeElement(element) {
110
- element.parentElement.removeChild(element);
111
- }
112
-
113
- function createErrorParagraph(text, base) {
114
- var p = document.createElement('p');
115
- p.classList.add('form-error');
116
- p.classList.add(base ? 'form-error-base' : 'form-error-inline');
117
- p.innerText = text;
118
- return p;
119
- }
120
-
121
- function querySelectorArray(parent, selector) {
122
- var query = parent.querySelectorAll(selector);
123
- var array = Array.prototype.slice.call(query);
124
- return array;
125
- }
126
-
127
- /*
128
- * Take a single error message and format it as if it were
129
- * a json error coming from the server
130
- */
131
- function formatStringAsErrors(errorString) {
132
- return {base: {messages: [errorString] }};
133
- }
134
-
135
- /*
136
- * Remove inline error messages
137
- */
138
- tb.remote.clearErrors = function(form) {
139
- querySelectorArray(form, '.form-error-inline, .form-error-base').forEach(removeElement);
140
- };
141
-
142
- /*
143
- * Append error text to form input fields and scroll to the first error
144
- */
145
- tb.remote.displayErrorsInline = function(form, errors){
146
- if(typeof errors === 'string') {
147
- errors = formatStringAsErrors(errors);
148
- }
149
-
150
- tb.remote.clearErrors(form);
151
-
152
- for(var key in errors){
153
- var messages = errors[key].messages;
154
- if(key === 'base'){
155
- for(var i=0; i<messages.length; i++){
156
- var p = createErrorParagraph(messages[i], true);
157
- form.insertBefore(p, form.firstChild);
158
- }
159
- }
160
- else{
161
- var inputName, input;
162
-
163
- keyArr = key.split('.');
164
-
165
- if(keyArr.length === 2){
166
- keyArr[0] += '_attributes';
167
- input = form.querySelector("[name$='[" + keyArr.join('][') + "]']");
168
- }
169
- else{
170
- var array = querySelectorArray(form, "[name$='[" + key + "]']");
171
- input = array.filter(function(input){
172
- return !input.name.match(/\]\[/); // Filter out nested attribute inputs ie model[a][b]
173
- })[0];
174
- if(input === null && !key.match(/_id$/)){
175
- input = form.querySelector("[name$='[" + key + "_id]']");
176
- }
177
- }
178
-
179
- var message = messages[0];
180
- if(input){
181
- input.parentNode.appendChild(createErrorParagraph(message));
182
- }
183
- else{
184
- console.warn('Missing input field for key:', key);
185
- form.insertBefore(
186
- createErrorParagraph(key+' '+message), form.firstChild
187
- );
188
- }
189
- }
190
- }
191
-
192
- var firstError = form.querySelector('.form-error-inline, .form-error-base')
193
- if(firstError &&
194
- !tb.util.elementIsInView(firstError) &&
195
- typeof(firstError.scrollIntoView) === 'function'){
196
- firstError.parentElement.scrollIntoView();
197
- }
198
- };
199
-
200
- /*
201
- * Display errors in a standard window.alert dialog
202
- */
203
- tb.remote.displayErrorsAlert = function(errors){
204
- if(typeof errors === 'string') {
205
- errors = formatStringAsErrors(errors);
206
- }
207
- var text = "Please correct the following errors:\n";
208
- var label, message;
209
- var index = 0;
210
- for(var key in errors){
211
- label = errors[key].label;
212
- message = errors[key].messages[0];
213
- if(key === 'base'){
214
- text += ' - ' + message + "\n"
215
- }
216
- else{
217
- text += ' - ' + label + ' ' + message + "\n"
218
- }
219
- index++;
220
- }
221
- window.alert(text);
222
- };
223
-
224
- /*
225
- * Used for deleting table rows
226
- * Add a link with data-remote=true and data-method=delete to your table to trigger this callback
227
- */
228
- var onRemoteDeleteTableRow = function(){
229
- var row = $(this).parents('tr');
230
- row.fadeOut(200, function(){
231
- row.remove();
232
- });
233
- };
234
-
235
- tb.remote.init();
236
-
237
- })();
@@ -1,55 +0,0 @@
1
- (function(){
2
-
3
- tb.util = {
4
- domainWithPort: function(){
5
- if(window.location.origin){
6
- return window.location.origin;
7
- }
8
- else{
9
- return location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '');
10
- }
11
- },
12
-
13
- pathFor: function(){
14
- var args = Array.prototype.slice.call(arguments, 0);
15
- var parts = [''];
16
- if(tb.config.subUri){
17
- parts.push(tb.config.subUri);
18
- }
19
- return parts.concat(args).join('/');
20
- },
21
-
22
- urlFor: function(){
23
- var args = Array.prototype.slice.call(arguments, 0);
24
- var parts = [tb.util.domainWithPort()];
25
- if(tb.config.subUri){
26
- parts.push(tb.config.subUri);
27
- }
28
- return parts.concat(args).join('/');
29
- },
30
-
31
- getCsrfToken: function(){
32
- console.warn('Call to tb.util.getCsrfToken() will be removed in the future; Use Rails.csrfToken() instead.')
33
- return Rails.csrfToken();
34
- },
35
-
36
- /*
37
- * Use top/bottom offsets to determine if the given jQuery element is within the current scroll view
38
- *
39
- * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport/7557433#7557433
40
- */
41
- elementIsInView: function(el){
42
- if (typeof jQuery === "function" && el instanceof jQuery) {
43
- el = el[0];
44
- }
45
- var rect = el.getBoundingClientRect();
46
- return (
47
- rect.top >= 0 &&
48
- rect.left >= 0 &&
49
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
50
- rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
51
- );
52
- }
53
- };
54
-
55
- })();
@@ -1,80 +0,0 @@
1
- module TbCore
2
- module SortableParams
3
- extend ActiveSupport::Concern
4
-
5
- module ClassMethods
6
- attr_accessor :default_sort_attribute
7
- attr_accessor :sortable_mapping
8
-
9
- # Define attributes you wish to sort by
10
- #
11
- # This optional configuration method tells the the sortable
12
- # helpers how they should map attributes to queries. For
13
- # example a "name" sort might need to sort on first and last
14
- # name attributes
15
- #
16
- # The `default` argument, if passed, will determine the
17
- # attribute used when no sort param has been passed
18
- #
19
- # Usage:
20
- #
21
- # sortable_by :email, name: [:last_name, :first_name]
22
- # default: :email,
23
- #
24
- #
25
- def sortable_by(*attributes, **options)
26
- @default_sort_attribute = options.delete(:default)
27
- @sortable_mapping = options.merge(attributes.map { |att| [att, att] }.to_h)
28
- end
29
- end
30
-
31
- # Return the current sort direction
32
- #
33
- # Defaults to :asc for all values other than "desc"
34
- #
35
- def sort_direction
36
- if params[:dir].present? && params[:dir] == 'desc'
37
- :desc
38
- else
39
- :asc
40
- end
41
- end
42
-
43
- # Return the current sort param
44
- #
45
- def sort_attribute
46
- params[:sort].try(:to_sym) || self.class.default_sort_attribute
47
- end
48
-
49
- # Return a hash that can be used in an ActiveRecord order query
50
- #
51
- # Example:
52
- # MyModel.where(...).order(sortable_query).paginate(...)
53
- #
54
- def sortable_query
55
- return unless sort_attribute
56
- normalize_sort_value(
57
- sort_attribute,
58
- sort_direction
59
- )
60
- end
61
-
62
- # Translate the sort_by and direction into a sortable hash
63
- #
64
- def normalize_sort_value(sort_by, direction)
65
- mapping = self.class.sortable_mapping[sort_by]
66
- if mapping.is_a? Symbol
67
- { mapping => sort_direction }
68
- elsif mapping.is_a? String
69
- mapping.gsub(':dir', sort_direction.to_s)
70
- elsif mapping.is_a? Array
71
- mapping.map { |att| [att, direction] }.to_h
72
- elsif mapping.is_a? Proc
73
- mapping.call(direction)
74
- else
75
- logger.debug("WARNING: Sort attribute '#{sort_by}' is not recognized. Did you mean to pass it to sortable_by?")
76
- nil
77
- end
78
- end
79
- end
80
- end
@@ -1,10 +0,0 @@
1
- class Spud::ApplicationController < TbCore::ApplicationController
2
- def initialize
3
- ActiveSupport::Deprecation.warn(
4
- 'Spud::ApplicationController is deprecated and may be removed from future releases,
5
- use TbCore::ApplicationController instead.',
6
- caller
7
- )
8
- super
9
- end
10
- end
@@ -1,14 +0,0 @@
1
- // Use this file to comment out any Bootstrap JavaScript files you are not using
2
- //
3
- //= require bootstrap/affix
4
- //= require bootstrap/alert
5
- //= require bootstrap/button
6
- //= require bootstrap/carousel
7
- //= require bootstrap/collapse
8
- //= require bootstrap/dropdown
9
- //= require bootstrap/modal
10
- //= require bootstrap/scrollspy
11
- //= require bootstrap/tab
12
- //= require bootstrap/transition
13
- //= require bootstrap/tooltip
14
- //= require bootstrap/popover
@@ -1,3 +0,0 @@
1
- Rails.application.routes.draw do
2
- get '*any' => 'spud/application#not_found'
3
- end
@@ -1,56 +0,0 @@
1
- module Spud
2
- module Core
3
- include ActiveSupport::Configurable
4
- config_accessor :site_name, :admin_applications, :from_address, :site_id, :short_name,
5
- :javascripts, :stylesheets, :admin_javascripts, :admin_stylesheets,
6
- :permissions, :production_alert_domain, :use_email_as_login
7
- self.admin_applications = []
8
- self.site_name = 'Company Name'
9
- self.site_id = 0
10
- self.short_name = 'default'
11
- self.javascripts = []
12
- self.stylesheets = []
13
- self.from_address = 'no-reply@companyname.com'
14
- self.permissions = []
15
- self.admin_javascripts = ['admin/core/application', 'admin/application']
16
- self.admin_stylesheets = ['admin/core/application', 'admin/application']
17
- self.production_alert_domain = nil
18
- self.use_email_as_login = false
19
-
20
- def self.site_config_for_host(host)
21
- ActiveSupport::Deprecation.warn 'Spud::Core.site_config_for_host is deprecated and will be removed in the future'
22
- return default_site_config
23
- end
24
-
25
- def self.site_config_for_id(id)
26
- ActiveSupport::Deprecation.warn 'Spud::Core.site_config_for_id is deprecated and will be removed in the future'
27
- return default_site_config
28
- end
29
-
30
- def self.default_site_config
31
- ActiveSupport::Deprecation.warn 'Spud::Core.default_site_config is deprecated and will be removed in the future'
32
- return {
33
- site_id: Spud::Core.config.site_id,
34
- site_name: Spud::Core.config.site_name,
35
- short_name: Spud::Core.config.short_name
36
- }
37
- end
38
-
39
- def self.append_admin_javascripts(*args)
40
- if args[0].class == Array
41
- Spud::Core.config.admin_javascripts += args[0]
42
- else
43
- Spud::Core.config.admin_javascripts += args
44
- end
45
- end
46
-
47
- def self.append_admin_stylesheets(*args)
48
- if args[0].class == Array
49
- Spud::Core.config.admin_stylesheets += args[0]
50
- else
51
- Spud::Core.config.admin_stylesheets += args
52
- end
53
- end
54
-
55
- end
56
- end