trestle 0.8.4 → 0.8.11

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 (164) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -0
  3. data/.travis.yml +14 -1
  4. data/Gemfile +6 -0
  5. data/README.md +15 -9
  6. data/app/assets/javascripts/trestle/admin.js +11 -10
  7. data/app/assets/javascripts/trestle/components/_confirmation.js +40 -0
  8. data/app/assets/javascripts/trestle/components/_datepicker.js +37 -0
  9. data/app/assets/javascripts/trestle/components/_dialog.js +136 -0
  10. data/app/assets/javascripts/trestle/{_errors.js → components/_errors.js} +2 -2
  11. data/app/assets/javascripts/trestle/components/_form.js +75 -0
  12. data/app/assets/javascripts/trestle/{_gallery.js → components/_gallery.js} +3 -3
  13. data/app/assets/javascripts/trestle/{_select.js → components/_select.js} +2 -2
  14. data/app/assets/javascripts/trestle/components/_sidebar.js +77 -0
  15. data/app/assets/javascripts/trestle/{_table.js → components/_table.js} +1 -1
  16. data/app/assets/javascripts/trestle/components/_tabs.js +25 -0
  17. data/app/assets/javascripts/trestle/components/_tooltips.js +19 -0
  18. data/app/assets/javascripts/trestle/core/_contexts.js +13 -0
  19. data/app/assets/javascripts/trestle/core/_cookies.js +24 -0
  20. data/app/assets/javascripts/trestle/core/_events.js +39 -0
  21. data/app/assets/javascripts/trestle/core/_l10n.js +23 -0
  22. data/app/assets/javascripts/trestle/core/_visit.js +10 -0
  23. data/app/assets/stylesheets/trestle/components/_alerts.scss +28 -0
  24. data/app/assets/stylesheets/trestle/components/_avatar.scss +23 -1
  25. data/app/assets/stylesheets/trestle/components/_breadcrumbs.scss +12 -0
  26. data/app/assets/stylesheets/trestle/components/_buttons.scss +111 -1
  27. data/app/assets/stylesheets/trestle/components/_content.scss +53 -21
  28. data/app/assets/stylesheets/trestle/components/_datepicker.scss +28 -0
  29. data/app/assets/stylesheets/trestle/components/_fields.scss +17 -0
  30. data/app/assets/stylesheets/trestle/components/_input-group.scss +26 -1
  31. data/app/assets/stylesheets/trestle/components/_modal.scss +108 -0
  32. data/app/assets/stylesheets/trestle/components/_navigation.scss +104 -14
  33. data/app/assets/stylesheets/trestle/components/_pagination.scss +3 -3
  34. data/app/assets/stylesheets/trestle/components/_scopes.scss +11 -2
  35. data/app/assets/stylesheets/trestle/components/_sidebar.scss +3 -2
  36. data/app/assets/stylesheets/trestle/components/_table.scss +34 -10
  37. data/app/assets/stylesheets/trestle/components/_tags.scss +9 -0
  38. data/app/assets/stylesheets/trestle/components/_timestamp.scss +4 -1
  39. data/app/assets/stylesheets/trestle/components/_toolbars.scss +55 -0
  40. data/app/assets/stylesheets/trestle/components/_wells.scss +9 -1
  41. data/app/assets/stylesheets/trestle/core/_defaults.scss +21 -6
  42. data/app/assets/stylesheets/trestle/core/_layout.scss +8 -0
  43. data/app/assets/stylesheets/trestle/core/_mixins.scss +11 -0
  44. data/app/assets/stylesheets/trestle/core/_typography.scss +39 -0
  45. data/app/controllers/concerns/trestle/controller/breadcrumbs.rb +21 -0
  46. data/app/controllers/concerns/trestle/controller/callbacks.rb +21 -0
  47. data/app/controllers/concerns/trestle/controller/dialog.rb +16 -0
  48. data/app/controllers/concerns/trestle/controller/helpers.rb +18 -0
  49. data/app/controllers/concerns/trestle/controller/layout.rb +16 -0
  50. data/app/controllers/concerns/trestle/controller/location.rb +15 -0
  51. data/app/controllers/trestle/application_controller.rb +6 -30
  52. data/app/helpers/trestle/avatar_helper.rb +9 -2
  53. data/app/helpers/trestle/flash_helper.rb +15 -0
  54. data/app/helpers/trestle/form_helper.rb +8 -5
  55. data/app/helpers/trestle/format_helper.rb +7 -3
  56. data/app/helpers/trestle/headings_helper.rb +27 -0
  57. data/app/helpers/trestle/hook_helper.rb +13 -4
  58. data/app/helpers/trestle/i18n_helper.rb +14 -0
  59. data/app/helpers/trestle/panel_helper.rb +24 -0
  60. data/app/helpers/trestle/tab_helper.rb +2 -2
  61. data/app/helpers/trestle/table_helper.rb +41 -2
  62. data/app/helpers/trestle/timestamp_helper.rb +49 -7
  63. data/app/helpers/trestle/toolbars_helper.rb +34 -0
  64. data/app/helpers/trestle/url_helper.rb +72 -8
  65. data/app/views/layouts/trestle/admin.html.erb +3 -5
  66. data/app/views/trestle/application/_dialog.html.erb +34 -0
  67. data/app/views/trestle/application/_header.html.erb +22 -20
  68. data/app/views/trestle/application/_layout.html.erb +1 -1
  69. data/app/views/trestle/application/_tabs.html.erb +8 -2
  70. data/app/views/trestle/{application → flash}/_alert.html.erb +7 -2
  71. data/app/views/trestle/flash/_debug.html.erb +8 -0
  72. data/app/views/trestle/flash/_flash.html.erb +7 -0
  73. data/app/views/trestle/resource/_scopes.html.erb +1 -1
  74. data/app/views/trestle/resource/edit.html.erb +8 -10
  75. data/app/views/trestle/resource/index.html.erb +16 -4
  76. data/app/views/trestle/resource/new.html.erb +6 -6
  77. data/app/views/trestle/resource/show.html.erb +8 -10
  78. data/app/views/trestle/shared/_sidebar.html.erb +10 -7
  79. data/app/views/trestle/shared/_title.html.erb +14 -0
  80. data/app/views/trestle/table/_table.html.erb +4 -6
  81. data/bower.json +1 -1
  82. data/config/locales/cs.rb +18 -0
  83. data/config/locales/cs.yml +95 -0
  84. data/config/locales/en.yml +65 -36
  85. data/config/locales/es-MX.yml +94 -0
  86. data/config/locales/es.yml +94 -0
  87. data/config/locales/fr.rb +18 -0
  88. data/config/locales/fr.yml +84 -0
  89. data/config/locales/lv.rb +18 -0
  90. data/config/locales/lv.yml +94 -0
  91. data/config/locales/nl.rb +18 -0
  92. data/config/locales/nl.yml +82 -0
  93. data/config/locales/pl.rb +18 -0
  94. data/config/locales/pl.yml +85 -0
  95. data/config/locales/pt-BR.rb +18 -0
  96. data/config/locales/pt-BR.yml +84 -0
  97. data/config/locales/zh-CN.rb +18 -0
  98. data/config/locales/zh-CN.yml +94 -0
  99. data/gemfiles/rails-4.2.gemfile +16 -0
  100. data/gemfiles/rails-5.0.gemfile +16 -0
  101. data/gemfiles/rails-5.1.gemfile +16 -0
  102. data/gemfiles/rails-5.2.gemfile +16 -0
  103. data/gemfiles/rails-edge.gemfile +17 -0
  104. data/lib/generators/trestle/install/templates/trestle.rb.erb +18 -2
  105. data/lib/generators/trestle/resource/templates/admin.rb.erb +2 -2
  106. data/lib/trestle/adapters/active_record_adapter.rb +2 -6
  107. data/lib/trestle/adapters/adapter.rb +40 -15
  108. data/lib/trestle/adapters/sequel_adapter.rb +2 -6
  109. data/lib/trestle/admin/builder.rb +42 -12
  110. data/lib/trestle/admin/controller.rb +3 -1
  111. data/lib/trestle/admin.rb +84 -3
  112. data/lib/trestle/breadcrumb.rb +16 -1
  113. data/lib/trestle/configurable.rb +6 -0
  114. data/lib/trestle/configuration.rb +28 -5
  115. data/lib/trestle/display.rb +1 -1
  116. data/lib/trestle/engine.rb +10 -4
  117. data/lib/trestle/form/automatic.rb +6 -3
  118. data/lib/trestle/form/builder.rb +5 -1
  119. data/lib/trestle/form/field.rb +1 -1
  120. data/lib/trestle/form/fields/date_picker.rb +2 -2
  121. data/lib/trestle/form/fields/form_control.rb +14 -4
  122. data/lib/trestle/form/fields/form_group.rb +14 -3
  123. data/lib/trestle/form/fields/select.rb +5 -1
  124. data/lib/trestle/form/fields/tag_select.rb +1 -1
  125. data/lib/trestle/form/renderer.rb +2 -2
  126. data/lib/trestle/form.rb +7 -3
  127. data/lib/trestle/hook.rb +27 -0
  128. data/lib/trestle/model_name.rb +64 -0
  129. data/lib/trestle/navigation/group.rb +16 -0
  130. data/lib/trestle/navigation/item.rb +11 -1
  131. data/lib/trestle/navigation.rb +24 -6
  132. data/lib/trestle/options.rb +7 -5
  133. data/lib/trestle/reloader.rb +1 -1
  134. data/lib/trestle/resource/adapter_methods.rb +62 -0
  135. data/lib/trestle/resource/builder.rb +43 -17
  136. data/lib/trestle/resource/collection.rb +68 -0
  137. data/lib/trestle/resource/controller.rb +70 -33
  138. data/lib/trestle/resource.rb +97 -105
  139. data/lib/trestle/scope.rb +13 -3
  140. data/lib/trestle/tab.rb +4 -0
  141. data/lib/trestle/table/actions_column.rb +41 -31
  142. data/lib/trestle/table/automatic.rb +1 -2
  143. data/lib/trestle/table/builder.rb +2 -2
  144. data/lib/trestle/table/column.rb +40 -9
  145. data/lib/trestle/table/row.rb +18 -2
  146. data/lib/trestle/table.rb +18 -2
  147. data/lib/trestle/toolbar/builder.rb +52 -0
  148. data/lib/trestle/toolbar/context.rb +39 -0
  149. data/lib/trestle/toolbar.rb +43 -0
  150. data/lib/trestle/version.rb +1 -1
  151. data/lib/trestle.rb +9 -4
  152. data/trestle.gemspec +5 -3
  153. data/vendor/assets/bower_components/trestle/select2/dist/js/select2.full.js +90 -69
  154. data/vendor/assets/javascripts/trestle/flatpickr.js.erb +2 -0
  155. data/vendor/assets/stylesheets/trestle/magnific-popup.scss +13 -1
  156. metadata +99 -22
  157. data/app/assets/javascripts/trestle/_confirmation.js +0 -23
  158. data/app/assets/javascripts/trestle/_datepicker.js +0 -22
  159. data/app/assets/javascripts/trestle/_form.js +0 -6
  160. data/app/assets/javascripts/trestle/_sidebar.js +0 -52
  161. data/app/assets/javascripts/trestle/_tabs.js +0 -13
  162. data/app/assets/javascripts/trestle/_tooltips.js +0 -3
  163. data/app/helpers/trestle/title_helper.rb +0 -26
  164. data/app/views/trestle/application/_flash.html.erb +0 -11
@@ -1,69 +1,78 @@
1
1
  module Trestle
2
2
  class Resource
3
3
  class Controller < Admin::Controller
4
- def index
5
- self.collection = admin.prepare_collection(params)
4
+ before_action :load_collection, only: [:index]
5
+ before_action :load_instance, only: [:show, :edit, :update, :destroy]
6
6
 
7
+ def index
7
8
  respond_to do |format|
8
9
  format.html
9
10
  format.json { render json: collection }
11
+ format.js
10
12
  end
11
13
  end
12
14
 
13
15
  def new
14
- self.instance = admin.build_instance({}, params)
16
+ self.instance = admin.build_instance(params.key?(admin.parameter_name) ? admin.permitted_params(params) : {}, params)
15
17
 
16
18
  respond_to do |format|
17
19
  format.html
18
20
  format.json { render json: instance }
21
+ format.js
19
22
  end
20
23
  end
21
24
 
22
25
  def create
23
26
  self.instance = admin.build_instance(admin.permitted_params(params), params)
24
27
 
25
- if admin.save_instance(instance)
28
+ if admin.save_instance(instance, params)
26
29
  respond_to do |format|
27
30
  format.html do
28
- flash[:message] = flash_message("success.create", default: "The %{model_name} was successfully created.")
29
- redirect_to action: :show, id: admin.to_param(instance)
31
+ flash[:message] = flash_message("create.success", title: "Success!", message: "The %{lowercase_model_name} was successfully created.")
32
+ redirect_to_return_location(:create, instance, default: admin.instance_path(instance))
30
33
  end
31
- format.json { render json: instance, status: :created, location: { action: :show, id: admin.to_param(instance) } }
34
+ format.json { render json: instance, status: :created, location: admin.instance_path(instance) }
32
35
  format.js
33
36
  end
34
37
  else
35
38
  respond_to do |format|
36
39
  format.html do
37
- flash.now[:error] = flash_message("failure.create", default: "Please correct the errors below.")
38
- render "new"
40
+ flash.now[:error] = flash_message("create.failure", title: "Warning!", message: "Please correct the errors below.")
41
+ render "new", status: :unprocessable_entity
39
42
  end
40
43
  format.json { render json: instance.errors, status: :unprocessable_entity }
44
+ format.js
41
45
  end
42
46
  end
43
47
  end
44
48
 
45
49
  def show
46
- self.instance = admin.find_instance(params)
47
-
48
- respond_to do |format|
49
- format.html
50
- format.json { render json: instance }
50
+ if admin.singular? && instance.nil?
51
+ respond_to do |format|
52
+ format.html { redirect_to action: :new }
53
+ format.json { head :not_found }
54
+ format.js
55
+ end
56
+ else
57
+ respond_to do |format|
58
+ format.html
59
+ format.json { render json: instance }
60
+ format.js
61
+ end
51
62
  end
52
63
  end
53
64
 
54
65
  def edit
55
- self.instance = admin.find_instance(params)
56
66
  end
57
67
 
58
68
  def update
59
- self.instance = admin.find_instance(params)
60
69
  admin.update_instance(instance, admin.permitted_params(params), params)
61
70
 
62
- if admin.save_instance(instance)
71
+ if admin.save_instance(instance, params)
63
72
  respond_to do |format|
64
73
  format.html do
65
- flash[:message] = flash_message("success.update", default: "The %{model_name} was successfully updated.")
66
- redirect_to action: :show, id: admin.to_param(instance)
74
+ flash[:message] = flash_message("update.success", title: "Success!", message: "The %{lowercase_model_name} was successfully updated.")
75
+ redirect_to_return_location(:update, instance, default: admin.instance_path(instance))
67
76
  end
68
77
  format.json { render json: instance, status: :ok }
69
78
  format.js
@@ -71,27 +80,32 @@ module Trestle
71
80
  else
72
81
  respond_to do |format|
73
82
  format.html do
74
- flash.now[:error] = flash_message("failure.update", default: "Please correct the errors below.")
75
- render "show"
83
+ flash.now[:error] = flash_message("update.failure", title: "Warning!", message: "Please correct the errors below.")
84
+ render "show", status: :unprocessable_entity
76
85
  end
77
86
  format.json { render json: instance.errors, status: :unprocessable_entity }
87
+ format.js
78
88
  end
79
89
  end
80
90
  end
81
91
 
82
92
  def destroy
83
- self.instance = admin.find_instance(params)
84
- success = admin.delete_instance(instance)
93
+ success = admin.delete_instance(instance, params)
85
94
 
86
95
  respond_to do |format|
87
96
  format.html do
88
97
  if success
89
- flash[:message] = flash_message("success.destroy", default: "The %{model_name} was successfully deleted.")
98
+ flash[:message] = flash_message("destroy.success", title: "Success!", message: "The %{lowercase_model_name} was successfully deleted.")
99
+ redirect_to_return_location(:destroy, instance, default: admin.path(:index))
90
100
  else
91
- flash[:message] = flash_message("failure.destroy", default: "Could not delete %{model_name}.")
92
- end
101
+ flash[:error] = flash_message("destroy.failure", title: "Warning!", message: "Could not delete %{lowercase_model_name}.")
93
102
 
94
- redirect_to action: :index
103
+ if self.instance = admin.find_instance(params)
104
+ redirect_to_return_location(:update, instance, default: admin.instance_path(instance))
105
+ else
106
+ redirect_to_return_location(:destroy, instance, default: admin.path(:index))
107
+ end
108
+ end
95
109
  end
96
110
  format.json { head :no_content }
97
111
  format.js
@@ -99,14 +113,37 @@ module Trestle
99
113
  end
100
114
 
101
115
  protected
102
- attr_accessor :collection
103
- helper_method :collection
116
+ def load_instance
117
+ self.instance = admin.find_instance(params)
118
+ end
104
119
 
105
- attr_accessor :instance
106
- helper_method :instance
120
+ def load_collection
121
+ self.collection = admin.prepare_collection(params)
122
+ end
123
+
124
+ attr_accessor :instance, :collection
125
+ helper_method :instance, :collection
126
+
127
+ def flash_message(type, title:, message:)
128
+ {
129
+ title: admin.t("flash.#{type}.title", default: title),
130
+ message: admin.t("flash.#{type}.message", default: message)
131
+ }
132
+ end
107
133
 
108
- def flash_message(type, options={})
109
- t("trestle.flash.#{type}", options.merge(model_name: admin.model_name.underscore.humanize(capitalize: false)))
134
+ def redirect_to_return_location(action, instance, default:)
135
+ if admin.return_locations[action] && !dialog_request?
136
+ location = instance_exec(instance, &admin.return_locations[action])
137
+
138
+ case location
139
+ when :back
140
+ redirect_back fallback_location: default, turbolinks: false
141
+ else
142
+ redirect_to location, turbolinks: false
143
+ end
144
+ else
145
+ redirect_to default, turbolinks: false
146
+ end
110
147
  end
111
148
  end
112
149
  end
@@ -2,110 +2,68 @@ module Trestle
2
2
  class Resource < Admin
3
3
  extend ActiveSupport::Autoload
4
4
 
5
+ autoload :AdapterMethods
5
6
  autoload :Builder
7
+ autoload :Collection
6
8
  autoload :Controller
7
9
 
8
- class << self
9
- def adapter
10
- @adapter ||= Trestle.config.default_adapter.new(self)
11
- end
12
-
13
- def adapter=(klass)
14
- @adapter = klass.new(self)
15
- end
16
-
17
- # Defines a method that can be overridden with a custom block,
18
- # but is otherwise delegated to the adapter instance.
19
- def self.adapter_method(name)
20
- attr_writer name
21
-
22
- define_method(name) do |*args|
23
- if override = instance_variable_get("@#{name}")
24
- instance_exec(*args, &override)
25
- else
26
- adapter.public_send(name, *args)
27
- end
28
- end
29
- end
30
-
31
- adapter_method :collection
32
- adapter_method :find_instance
33
- adapter_method :build_instance
34
- adapter_method :update_instance
35
- adapter_method :save_instance
36
- adapter_method :delete_instance
37
- adapter_method :to_param
38
- adapter_method :permitted_params
39
- adapter_method :decorate_collection
40
- adapter_method :unscope
41
- adapter_method :merge_scopes
42
- adapter_method :count
43
- adapter_method :sort
44
- adapter_method :paginate
45
- adapter_method :human_attribute_name
46
- adapter_method :default_table_attributes
47
- adapter_method :default_form_attributes
48
-
49
- attr_accessor :decorator
50
-
51
- def prepare_collection(params)
52
- collection = initialize_collection(params)
53
- collection = apply_scopes(collection, params)
54
- collection = apply_sorting(collection, params)
55
- collection = paginate(collection, params)
56
- collection = decorate_collection(collection)
57
- collection
58
- end
10
+ include AdapterMethods
11
+
12
+ RESOURCE_ACTIONS = [:index, :show, :new, :create, :edit, :update, :destroy]
13
+ READONLY_ACTIONS = [:index, :show]
14
+
15
+ class_attribute :decorator
16
+
17
+ class_attribute :pagination_options
18
+ self.pagination_options = {}
19
+
20
+ # Collection-focused adapter methods
21
+ adapter_method :collection
22
+ adapter_method :merge_scopes
23
+ adapter_method :sort
24
+ adapter_method :paginate
25
+ adapter_method :finalize_collection
26
+ adapter_method :decorate_collection
27
+ adapter_method :count
28
+
29
+ # Instance-focused adapter methods
30
+ adapter_method :find_instance
31
+ adapter_method :build_instance
32
+ adapter_method :update_instance
33
+ adapter_method :save_instance
34
+ adapter_method :delete_instance
35
+ adapter_method :permitted_params
36
+
37
+ # Common adapter methods
38
+ adapter_method :to_param
39
+ adapter_method :human_attribute_name
40
+
41
+ # Automatic tables and forms adapter methods
42
+ adapter_method :default_table_attributes
43
+ adapter_method :default_form_attributes
44
+
45
+ # Prepares a collection for use in the resource controller's index action.
46
+ #
47
+ # Applies scopes, sorts, pagination, finalization and decorators according
48
+ # to the admin's adapter and any admin-specific adapter methods.
49
+ def prepare_collection(params, options={})
50
+ Collection.new(self, options).prepare(params)
51
+ end
59
52
 
60
- def initialize_collection(params)
61
- collection(params)
53
+ class << self
54
+ # Deprecated: use instance method instead
55
+ def prepare_collection(params, options={})
56
+ Collection.new(self, options).prepare(params)
62
57
  end
63
58
 
64
59
  def scopes
65
60
  @scopes ||= {}
66
61
  end
67
62
 
68
- def apply_scopes(collection, params)
69
- unscoped = unscope(collection)
70
-
71
- scopes_for(params).each do |scope|
72
- collection = merge_scopes(collection, scope.apply(unscoped))
73
- end
74
-
75
- collection
76
- end
77
-
78
- def scopes_for(params)
79
- result = []
80
-
81
- if params[:scope] && scopes.has_key?(params[:scope].to_sym)
82
- result << scopes[params[:scope].to_sym]
83
- end
84
-
85
- if result.empty? && default_scope = scopes.values.find(&:default?)
86
- result << default_scope
87
- end
88
-
89
- result
90
- end
91
-
92
63
  def column_sorts
93
64
  @column_sorts ||= {}
94
65
  end
95
66
 
96
- def apply_sorting(collection, params)
97
- return collection unless params[:sort]
98
-
99
- field = params[:sort].to_sym
100
- order = params[:order].to_s.downcase == "desc" ? :desc : :asc
101
-
102
- if column_sorts.has_key?(field)
103
- instance_exec(collection, order, &column_sorts[field])
104
- else
105
- sort(collection, field, order)
106
- end
107
- end
108
-
109
67
  def table
110
68
  super || Table::Automatic.new(self)
111
69
  end
@@ -119,29 +77,72 @@ module Trestle
119
77
  end
120
78
 
121
79
  def model_name
122
- options[:as] || default_model_name
80
+ @model_name ||= Trestle::ModelName.new(model)
81
+ end
82
+
83
+ def actions
84
+ @actions ||= (readonly? ? READONLY_ACTIONS : RESOURCE_ACTIONS).dup
85
+ end
86
+
87
+ def root_action
88
+ singular? ? :show : :index
123
89
  end
124
90
 
125
91
  def readonly?
126
92
  options[:readonly]
127
93
  end
128
94
 
129
- def breadcrumb
130
- Breadcrumb.new(model_name.pluralize, path)
95
+ def singular?
96
+ options[:singular]
97
+ end
98
+
99
+ def translate(key, options={})
100
+ super(key, options.merge({
101
+ model_name: model_name.titleize,
102
+ lowercase_model_name: model_name.downcase,
103
+ pluralized_model_name: model_name.plural.titleize
104
+ }))
105
+ end
106
+ alias t translate
107
+
108
+ def instance_path(instance, options={})
109
+ action = options.fetch(:action) { :show }
110
+ options = options.merge(id: to_param(instance)) unless singular?
111
+
112
+ path(action, options)
131
113
  end
132
114
 
133
115
  def routes
134
116
  admin = self
135
117
 
118
+ resource_method = singular? ? :resource : :resources
119
+ resource_name = admin_name
120
+ resource_options = {
121
+ controller: controller_namespace,
122
+ as: route_name,
123
+ path: options[:path],
124
+ except: (RESOURCE_ACTIONS - actions)
125
+ }
126
+
136
127
  Proc.new do
137
- resources admin.admin_name, controller: admin.controller_namespace, as: admin.route_name, path: admin.options[:path], except: admin.disabled_routes do
128
+ public_send(resource_method, resource_name, resource_options) do
138
129
  instance_exec(&admin.additional_routes) if admin.additional_routes
139
130
  end
140
131
  end
141
132
  end
142
133
 
143
- def disabled_routes
144
- readonly? ? [:new, :create, :edit, :update, :destroy] : []
134
+ def return_locations
135
+ @return_locations ||= {}
136
+ end
137
+
138
+ def build(&block)
139
+ Resource::Builder.build(self, &block)
140
+ end
141
+
142
+ def validate!
143
+ if singular? && !adapter_methods.method_defined?(:find_instance)
144
+ raise NotImplementedError, "Singular resources must define an instance block."
145
+ end
145
146
  end
146
147
 
147
148
  private
@@ -150,15 +151,6 @@ module Trestle
150
151
  rescue NameError
151
152
  raise NameError, "Unable to find model #{admin_name.classify}. Specify a different model using Trestle.resource(:#{admin_name}, model: MyModel)"
152
153
  end
153
-
154
- def default_model_name
155
- if model.respond_to?(:model_name)
156
- model_name = model.model_name
157
- model_name.respond_to?(:human) ? model_name.human : model_name.to_s.titleize
158
- else
159
- model.name.titleize
160
- end
161
- end
162
154
  end
163
155
  end
164
156
  end
data/lib/trestle/scope.rb CHANGED
@@ -20,18 +20,28 @@ module Trestle
20
20
 
21
21
  def apply(collection)
22
22
  if @block
23
- @admin.instance_exec(&@block)
23
+ if @block.arity == 1
24
+ @admin.instance_exec(collection, &@block)
25
+ else
26
+ @admin.instance_exec(&@block)
27
+ end
24
28
  else
25
29
  collection.public_send(name)
26
30
  end
27
31
  end
28
32
 
29
33
  def count(collection)
30
- @admin.count(@admin.merge_scopes(collection, apply(@admin.unscope(collection))))
34
+ @admin.count(@admin.merge_scopes(collection, apply(collection)))
31
35
  end
32
36
 
33
37
  def active?(params)
34
- @admin.scopes_for(params).include?(self)
38
+ active_scopes = Array(params[:scope])
39
+
40
+ if active_scopes.any?
41
+ active_scopes.include?(to_param.to_s)
42
+ else
43
+ default?
44
+ end
35
45
  end
36
46
  end
37
47
  end
data/lib/trestle/tab.rb CHANGED
@@ -8,6 +8,10 @@ module Trestle
8
8
  @name, @options = name, options
9
9
  end
10
10
 
11
+ def id(tag=nil)
12
+ ["tab", tag, name].compact.join("-")
13
+ end
14
+
11
15
  def label
12
16
  safe_join([options[:label] || I18n.t("admin.tabs.#{name}", default: name.to_s.titleize), badge].compact, " ")
13
17
  end
@@ -1,11 +1,17 @@
1
1
  module Trestle
2
2
  class Table
3
3
  class ActionsColumn
4
- attr_reader :table, :block
4
+ attr_reader :table, :toolbar, :options
5
5
 
6
- def initialize(table, &block)
7
- @table = table
8
- @block = block_given? ? block : default_actions
6
+ def initialize(table, options={}, &block)
7
+ @table, @options = table, options
8
+ @toolbar = Toolbar.new(ActionsBuilder)
9
+
10
+ if block_given?
11
+ @toolbar.append(&block)
12
+ else
13
+ @toolbar.append(&default_actions)
14
+ end
9
15
  end
10
16
 
11
17
  def renderer(template)
@@ -13,55 +19,59 @@ module Trestle
13
19
  end
14
20
 
15
21
  def default_actions
16
- ->(action) do
17
- action.delete
22
+ ->(toolbar, instance, admin) do
23
+ toolbar.delete if admin && admin.actions.include?(:destroy)
18
24
  end
19
25
  end
20
26
 
21
- class ActionsBuilder
22
- attr_reader :instance
27
+ class ActionsBuilder < Toolbar::Builder
28
+ attr_reader :instance, :admin
23
29
 
24
- delegate :table, to: :@column
30
+ def initialize(template, instance, admin)
31
+ super(template)
32
+
33
+ @instance, @admin = instance, admin
34
+ end
25
35
 
26
- def initialize(column, template, instance)
27
- @column, @template, @instance = column, template, instance
36
+ def show
37
+ link(t("buttons.show", default: "Show"), instance, admin: admin, action: :show, icon: "fa fa-info", style: :info)
38
+ end
39
+
40
+ def edit
41
+ link(t("buttons.edit", default: "Edit"), instance, admin: admin, action: :edit, icon: "fa fa-pencil", style: :warning)
28
42
  end
29
43
 
30
44
  def delete
31
- button(@template.icon("fa fa-trash"), @template.admin_url_for(instance, admin: table.options[:admin], action: :destroy), method: :delete, class: "btn-danger", data: { toggle: "confirm-delete", placement: "left" })
45
+ link(t("buttons.delete", default: "Delete"), instance, admin: admin, action: :destroy, method: :delete, icon: "fa fa-trash", style: :danger, data: { toggle: "confirm-delete", placement: "left" })
32
46
  end
33
47
 
34
- def button(content, url, options={})
35
- options[:class] = Array(options[:class])
36
- options[:class] << "btn" unless options[:class].include?("btn")
48
+ builder_method :show, :edit, :delete
49
+
50
+ # Disallow button tags within the actions toolbar. Alias to link for backwards compatibility.
51
+ alias button link
37
52
 
38
- @template.concat @template.link_to(content, url, options)
53
+ private
54
+ def translate(key, options={})
55
+ if admin
56
+ admin.translate(key, options)
57
+ else
58
+ I18n.t(:"admin.#{key}", options)
59
+ end
39
60
  end
40
- alias_method :link, :button
61
+ alias t translate
41
62
  end
42
63
 
43
64
  class Renderer < Column::Renderer
44
65
  def header
66
+ options[:header]
45
67
  end
46
68
 
47
69
  def classes
48
- "actions"
49
- end
50
-
51
- def options
52
- {}
53
- end
54
-
55
- def data
56
- {}
70
+ super + ["actions"]
57
71
  end
58
72
 
59
73
  def content(instance)
60
- builder = ActionsBuilder.new(@column, @template, instance)
61
-
62
- @template.with_output_buffer do
63
- @template.instance_exec(builder, &@column.block)
64
- end
74
+ @template.render_toolbar(@column.toolbar, instance, table.admin)
65
75
  end
66
76
  end
67
77
  end
@@ -3,7 +3,6 @@ module Trestle
3
3
  class Automatic < Table
4
4
  def initialize(admin)
5
5
  super(sortable: true, admin: admin)
6
- @admin = admin
7
6
  end
8
7
 
9
8
  def columns
@@ -11,7 +10,7 @@ module Trestle
11
10
  end
12
11
 
13
12
  def content_columns
14
- @admin.default_table_attributes.map.with_index do |attribute, index|
13
+ admin.default_table_attributes.map.with_index do |attribute, index|
15
14
  case attribute.type
16
15
  when :association
17
16
  Column.new(self, attribute.association_name, sort: false)
@@ -25,8 +25,8 @@ module Trestle
25
25
  table.columns << Column.new(table, field, options, &(proc || block))
26
26
  end
27
27
 
28
- def actions(&block)
29
- table.columns << ActionsColumn.new(table, &block)
28
+ def actions(options={}, &block)
29
+ table.columns << ActionsColumn.new(table, options, &block)
30
30
  end
31
31
  end
32
32
  end