trestle 0.8.3 → 0.8.6

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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.travis.yml +10 -1
  4. data/Gemfile +4 -0
  5. data/README.md +1 -0
  6. data/app/assets/javascripts/trestle/admin.js +24 -0
  7. data/app/assets/javascripts/trestle/components/_confirmation.js +23 -0
  8. data/app/assets/javascripts/trestle/components/_datepicker.js +22 -0
  9. data/app/assets/javascripts/trestle/components/_dialog.js +107 -0
  10. data/app/assets/javascripts/trestle/components/_errors.js +11 -0
  11. data/app/assets/javascripts/trestle/components/_form.js +48 -0
  12. data/app/assets/javascripts/trestle/components/_gallery.js +27 -0
  13. data/app/assets/javascripts/trestle/components/_select.js +11 -0
  14. data/app/assets/javascripts/trestle/components/_sidebar.js +75 -0
  15. data/app/assets/javascripts/trestle/components/_table.js +21 -0
  16. data/app/assets/javascripts/trestle/components/_tabs.js +24 -0
  17. data/app/assets/javascripts/trestle/components/_tooltips.js +3 -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/_visit.js +10 -0
  22. data/app/assets/javascripts/trestle/custom.js +4 -0
  23. data/app/assets/stylesheets/trestle/components/_alerts.scss +28 -0
  24. data/app/assets/stylesheets/trestle/components/_buttons.scss +9 -0
  25. data/app/assets/stylesheets/trestle/components/_modal.scss +92 -0
  26. data/app/assets/stylesheets/trestle/components/_navigation.scss +80 -10
  27. data/app/assets/stylesheets/trestle/components/_sidebar.scss +1 -0
  28. data/app/assets/stylesheets/trestle/components/_table.scss +6 -0
  29. data/app/assets/stylesheets/trestle/core/_defaults.scss +5 -1
  30. data/app/controllers/trestle/application_controller.rb +5 -1
  31. data/app/helpers/trestle/dialog_helper.rb +7 -0
  32. data/app/helpers/trestle/form_helper.rb +8 -1
  33. data/app/helpers/trestle/format_helper.rb +48 -0
  34. data/app/helpers/trestle/hook_helper.rb +8 -3
  35. data/app/helpers/trestle/url_helper.rb +33 -11
  36. data/app/views/layouts/trestle/admin.html.erb +4 -4
  37. data/app/views/trestle/admin/index.html.erb +1 -1
  38. data/app/views/trestle/application/_alert.html.erb +2 -0
  39. data/app/views/trestle/application/_dialog.html.erb +36 -0
  40. data/app/views/trestle/application/_flash.html.erb +18 -4
  41. data/app/views/trestle/application/_tabs.html.erb +7 -1
  42. data/app/views/trestle/dashboard/index.html.erb +2 -2
  43. data/app/views/trestle/resource/_form.html.erb +1 -1
  44. data/app/views/trestle/resource/edit.html.erb +6 -6
  45. data/app/views/trestle/resource/index.html.erb +5 -2
  46. data/app/views/trestle/resource/new.html.erb +4 -4
  47. data/app/views/trestle/resource/show.html.erb +6 -6
  48. data/app/views/trestle/shared/_sidebar.html.erb +7 -4
  49. data/bower.json +2 -2
  50. data/config/locales/en.yml +38 -18
  51. data/config/locales/fr.rb +18 -0
  52. data/config/locales/fr.yml +69 -0
  53. data/config/locales/nl.rb +18 -0
  54. data/config/locales/nl.yml +67 -0
  55. data/config/locales/pl.rb +18 -0
  56. data/config/locales/pl.yml +70 -0
  57. data/config/locales/pt-BR.rb +18 -0
  58. data/config/locales/pt-BR.yml +69 -0
  59. data/gemfiles/rails-4.2.gemfile +14 -0
  60. data/gemfiles/rails-5.0.gemfile +14 -0
  61. data/gemfiles/rails-5.1.gemfile +14 -0
  62. data/gemfiles/rails-edge.gemfile +15 -0
  63. data/lib/generators/trestle/install/install_generator.rb +3 -3
  64. data/lib/generators/trestle/install/templates/custom.js +7 -0
  65. data/lib/generators/trestle/install/templates/trestle.rb.erb +9 -2
  66. data/lib/generators/trestle/resource/templates/admin.rb.erb +1 -1
  67. data/lib/trestle/adapters/active_record_adapter.rb +46 -20
  68. data/lib/trestle/adapters/adapter.rb +176 -0
  69. data/lib/trestle/adapters/sequel_adapter.rb +85 -0
  70. data/lib/trestle/adapters.rb +7 -64
  71. data/lib/trestle/admin/builder.rb +10 -2
  72. data/lib/trestle/admin.rb +14 -1
  73. data/lib/trestle/attribute.rb +14 -37
  74. data/lib/trestle/breadcrumb.rb +19 -0
  75. data/lib/trestle/configuration.rb +14 -3
  76. data/lib/trestle/display.rb +1 -1
  77. data/lib/trestle/form/automatic.rb +29 -21
  78. data/lib/trestle/form/builder.rb +4 -0
  79. data/lib/trestle/form/field.rb +2 -2
  80. data/lib/trestle/form/fields/check_box.rb +1 -1
  81. data/lib/trestle/form/fields/collection_select.rb +1 -1
  82. data/lib/trestle/form/fields/date_select.rb +1 -1
  83. data/lib/trestle/form/fields/datetime_select.rb +1 -1
  84. data/lib/trestle/form/fields/grouped_collection_select.rb +1 -1
  85. data/lib/trestle/form/fields/select.rb +2 -2
  86. data/lib/trestle/form/fields/tag_select.rb +1 -2
  87. data/lib/trestle/form/fields/time_select.rb +1 -1
  88. data/lib/trestle/form/fields/time_zone_select.rb +1 -1
  89. data/lib/trestle/form.rb +7 -3
  90. data/lib/trestle/model_name.rb +64 -0
  91. data/lib/trestle/navigation/group.rb +8 -0
  92. data/lib/trestle/navigation/item.rb +1 -1
  93. data/lib/trestle/options.rb +7 -5
  94. data/lib/trestle/reloader.rb +1 -1
  95. data/lib/trestle/resource/builder.rb +17 -1
  96. data/lib/trestle/resource/controller.rb +80 -18
  97. data/lib/trestle/resource.rb +26 -16
  98. data/lib/trestle/scope.rb +1 -1
  99. data/lib/trestle/table/actions_column.rb +21 -16
  100. data/lib/trestle/table/automatic.rb +5 -12
  101. data/lib/trestle/table/builder.rb +3 -2
  102. data/lib/trestle/table/column.rb +27 -45
  103. data/lib/trestle/table/row.rb +8 -2
  104. data/lib/trestle/table.rb +7 -1
  105. data/lib/trestle/version.rb +1 -1
  106. data/lib/trestle.rb +1 -0
  107. data/trestle.gemspec +5 -5
  108. data/vendor/assets/bower_components/trestle/flatpickr/dist/flatpickr.css +51 -26
  109. data/vendor/assets/bower_components/trestle/flatpickr/dist/flatpickr.js +349 -299
  110. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/ar.js +5 -5
  111. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/bg.js +5 -5
  112. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/bn.js +5 -5
  113. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/cat.js +7 -7
  114. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/cs.js +7 -7
  115. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/cy.js +7 -7
  116. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/da.js +6 -6
  117. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/de.js +10 -10
  118. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/eo.js +11 -11
  119. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/es.js +7 -7
  120. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/et.js +11 -11
  121. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/fa.js +6 -6
  122. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/fi.js +7 -7
  123. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/fr.js +11 -11
  124. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/gr.js +13 -13
  125. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/he.js +5 -5
  126. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/hi.js +5 -5
  127. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/hr.js +6 -6
  128. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/hu.js +10 -10
  129. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/id.js +7 -7
  130. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/index.js +97 -0
  131. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/it.js +10 -10
  132. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/ja.js +5 -5
  133. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/ko.js +6 -6
  134. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/lt.js +10 -10
  135. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/lv.js +6 -6
  136. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/mk.js +8 -8
  137. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/ms.js +7 -7
  138. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/my.js +7 -7
  139. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/nl.js +11 -11
  140. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/no.js +11 -11
  141. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/pa.js +5 -5
  142. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/pl.js +6 -6
  143. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/pt.js +6 -8
  144. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/ro.js +7 -9
  145. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/ru.js +6 -8
  146. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/si.js +5 -7
  147. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/sk.js +8 -8
  148. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/sl.js +8 -10
  149. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/sq.js +5 -7
  150. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/sr.js +9 -10
  151. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/sv.js +8 -10
  152. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/th.js +10 -11
  153. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/tr.js +6 -8
  154. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/uk.js +6 -7
  155. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/vn.js +6 -8
  156. data/vendor/assets/bower_components/trestle/flatpickr/dist/l10n/zh.js +9 -11
  157. data/vendor/assets/bower_components/trestle/select2/dist/js/select2.full.js +90 -69
  158. metadata +58 -33
  159. data/app/assets/javascripts/trestle/_confirmation.js.coffee +0 -18
  160. data/app/assets/javascripts/trestle/_datepicker.js.coffee +0 -18
  161. data/app/assets/javascripts/trestle/_errors.js.coffee +0 -8
  162. data/app/assets/javascripts/trestle/_form.js.coffee +0 -4
  163. data/app/assets/javascripts/trestle/_gallery.js.coffee +0 -21
  164. data/app/assets/javascripts/trestle/_select.js.coffee +0 -7
  165. data/app/assets/javascripts/trestle/_sidebar.js.coffee +0 -45
  166. data/app/assets/javascripts/trestle/_table.js.coffee +0 -16
  167. data/app/assets/javascripts/trestle/_tabs.js.coffee +0 -9
  168. data/app/assets/javascripts/trestle/_tooltips.js.coffee +0 -2
  169. data/app/assets/javascripts/trestle/admin.js.coffee +0 -22
  170. data/app/assets/javascripts/trestle/custom.js.coffee +0 -4
  171. data/lib/generators/trestle/install/templates/custom.js.coffee +0 -7
@@ -2,19 +2,19 @@ module Trestle
2
2
  module Adapters
3
3
  module ActiveRecordAdapter
4
4
  def collection(params={})
5
- admin.model.all
5
+ model.all
6
6
  end
7
7
 
8
8
  def find_instance(params)
9
- admin.model.find(params[:id])
9
+ model.find(params[:id])
10
10
  end
11
11
 
12
- def build_instance(params={})
13
- admin.model.new(params)
12
+ def build_instance(attrs={}, params={})
13
+ model.new(attrs)
14
14
  end
15
15
 
16
- def update_instance(instance, params)
17
- instance.assign_attributes(params)
16
+ def update_instance(instance, attrs, params={})
17
+ instance.assign_attributes(attrs)
18
18
  end
19
19
 
20
20
  def save_instance(instance)
@@ -25,10 +25,6 @@ module Trestle
25
25
  instance.destroy
26
26
  end
27
27
 
28
- def to_param(instance)
29
- instance
30
- end
31
-
32
28
  def unscope(scope)
33
29
  scope.unscoped
34
30
  end
@@ -37,27 +33,57 @@ module Trestle
37
33
  scope.merge(other)
38
34
  end
39
35
 
36
+ def count(collection)
37
+ collection.count
38
+ end
39
+
40
40
  def sort(collection, field, order)
41
41
  collection.reorder(field => order)
42
42
  end
43
43
 
44
- def paginate(collection, params)
45
- collection = Kaminari.paginate_array(collection) unless collection.respond_to?(:page)
46
- collection.page(params[:page])
44
+ def human_attribute_name(attribute, options={})
45
+ model.human_attribute_name(attribute, options)
47
46
  end
48
47
 
49
- def count(collection)
50
- collection.count
48
+ def default_table_attributes
49
+ default_attributes.reject do |attribute|
50
+ inheritance_column?(attribute) || counter_cache_column?(attribute)
51
+ end
51
52
  end
52
53
 
54
+ def default_form_attributes
55
+ default_attributes.reject do |attribute|
56
+ primary_key?(attribute) || inheritance_column?(attribute) || counter_cache_column?(attribute)
57
+ end
58
+ end
59
+
60
+ protected
53
61
  def default_attributes
54
- admin.model.columns.map do |column|
55
- if column.name.end_with?("_id") && (reflection = admin.model.reflections[column.name.sub(/_id$/, '')])
56
- Attribute::Association.new(admin, column.name, reflection.klass)
62
+ model.columns.map do |column|
63
+ if column.name.end_with?("_id") && (name = column.name.sub(/_id$/, '')) && (reflection = model.reflections[name])
64
+ Attribute::Association.new(column.name, class: -> { reflection.klass }, name: name, polymorphic: reflection.polymorphic?, type_column: reflection.foreign_type)
65
+ elsif column.name.end_with?("_type") && (name = column.name.sub(/_type$/, '')) && (reflection = model.reflections[name])
66
+ # Ignore type columns for polymorphic associations
57
67
  else
58
- Attribute.new(admin, column.name, column.type)
68
+ Attribute.new(column.name, column.type, array_column?(column) ? { array: true } : {})
59
69
  end
60
- end
70
+ end.compact
71
+ end
72
+
73
+ def primary_key?(attribute)
74
+ attribute.name.to_s == model.primary_key
75
+ end
76
+
77
+ def inheritance_column?(attribute)
78
+ attribute.name.to_s == model.inheritance_column
79
+ end
80
+
81
+ def counter_cache_column?(attribute)
82
+ attribute.name.to_s.end_with?("_count")
83
+ end
84
+
85
+ def array_column?(column)
86
+ column.respond_to?(:array?) && column.array?
61
87
  end
62
88
  end
63
89
  end
@@ -0,0 +1,176 @@
1
+ module Trestle
2
+ module Adapters
3
+ class Adapter
4
+ attr_reader :admin
5
+ delegate :model, to: :admin
6
+
7
+ def initialize(admin)
8
+ @admin = admin
9
+ end
10
+
11
+ # Loads the initial collection for use by the index action.
12
+ #
13
+ # params - Unfiltered params hash from the controller
14
+ #
15
+ # Returns a scope object that can be chained with other methods (e.g. sort, paginate, count, etc).
16
+ def collection(params={})
17
+ raise NotImplementedError
18
+ end
19
+
20
+ # Finds (and returns) an individual instance for use by the show, edit, update, destroy actions.
21
+ #
22
+ # params - Unfiltered params hash from the controller
23
+ def find_instance(params)
24
+ raise NotImplementedError
25
+ end
26
+
27
+ # Builds (and returns) a new instance for new/create actions.
28
+ #
29
+ # attrs - Permitted attributes to set on the new instance
30
+ # params - Unfiltered params hash from the controller
31
+ def build_instance(attrs={}, params={})
32
+ raise NotImplementedError
33
+ end
34
+
35
+ # Updates (but does not save) a given resource's attributes.
36
+ #
37
+ # instance - The instance to update
38
+ # attrs - Permitted attributes to update on the instance
39
+ # params - Unfiltered params hash from the controller
40
+ #
41
+ # The return value is ignored.
42
+ def update_instance(instance, attrs, params={})
43
+ raise NotImplementedError
44
+ end
45
+
46
+ # Saves an instance (used by the create and update actions).
47
+ #
48
+ # instance - The instance to save
49
+ #
50
+ # Returns a boolean indicating the success/fail status of the save.
51
+ def save_instance(instance)
52
+ raise NotImplementedError
53
+ end
54
+
55
+ # Deletes an instance (used by the destroy action).
56
+ #
57
+ # instance - The instance to delete
58
+ #
59
+ # Returns a boolean indicating the success/fail status of the deletion.
60
+ def delete_instance(instance)
61
+ raise NotImplementedError
62
+ end
63
+
64
+ # Decorates a collection for rendering by the index view.
65
+ # Decorating is the final step in preparing the collection for the view.
66
+ #
67
+ # collection - The collection to decorate
68
+ #
69
+ # Returns an enumerable collection of instances.
70
+ def decorate_collection(collection)
71
+ collection
72
+ end
73
+
74
+ # Converts an instance to a URL parameter. The result of this method is passed to the #find_instance
75
+ # adapter method as params[:id]. It is recommended to simply use the instance's #id, as other potential options
76
+ # such as a permalink/slug could potentially be changed during editing.
77
+ #
78
+ # instance - The instance to convert
79
+ #
80
+ # Returns the URL representation of the instance.
81
+ def to_param(instance)
82
+ instance.id
83
+ end
84
+
85
+ # Unscopes a collection so that it can be merged with other scopes without duplication or interference.
86
+ #
87
+ # scope - The scope to unscope
88
+ #
89
+ # Returns a scope object.
90
+ def unscope(scope)
91
+ scope
92
+ end
93
+
94
+ # Merges scopes together for Trestle scope application and counting.
95
+ #
96
+ # scope - The first scope
97
+ # other - The second scope
98
+ #
99
+ # Returns a scope object representing the combination of the two given scopes.
100
+ def merge_scopes(scope, other)
101
+ raise NotImplementedError
102
+ end
103
+
104
+ # Counts the number of objects in a collection for use by scope links.
105
+ #
106
+ # collection - The collection to count
107
+ #
108
+ # Returns the total number (integer) of objects in the collection.
109
+ def count(collection)
110
+ raise NotImplementedError
111
+ end
112
+
113
+ # Sorts the collection by the given field and order.
114
+ # This method is called when an explicit sort column for the given field is not defined.
115
+ #
116
+ # collection - The collection to sort
117
+ # field - The field to sort by
118
+ # order - Symbol (:asc or :desc) representing the sort order (ascending or descending)
119
+ #
120
+ # Returns a scope object
121
+ def sort(collection, field, order)
122
+ raise NotImplementedError
123
+ end
124
+
125
+ # Paginates a collection for use by the index action.
126
+ #
127
+ # collection - The collection to paginate
128
+ # params - Unfiltered params hash from the controller:
129
+ # :page - current page number
130
+ #
131
+ # Returns a Kaminari-compatible scope corresponding to a single page.
132
+ def paginate(collection, params)
133
+ collection = Kaminari.paginate_array(collection.to_a) unless collection.respond_to?(:page)
134
+ collection.page(params[:page])
135
+ end
136
+
137
+ # Filters the submitted form parameters and returns a whitelisted attributes 'hash'
138
+ # that can be set or updated on a model instance.
139
+ #
140
+ # IMPORTANT: By default, all params are permitted, which assumes a trusted administrator. If this is not the
141
+ # case, a `params` block should be individually declared for each admin with the set of permitted parameters.
142
+ #
143
+ # params - Unfiltered params hash from the controller
144
+ #
145
+ # Returns the permitted set of parameters as a ActionController::Parameters object.
146
+ def permitted_params(params)
147
+ params.require(admin.admin_name.singularize).permit!
148
+ end
149
+
150
+ # Produces a human-readable name for a given attribute, applying I18n where appropriate.
151
+ # See ActiveModel::Translation for an implementation of this method.
152
+ #
153
+ # attribute - Attribute name (Symbol)
154
+ # options - Hash of options [not currently used]
155
+ #
156
+ # Returns the human-readable name of the given attribute as a String.
157
+ def human_attribute_name(attribute, options={})
158
+ attribute.to_s.titleize
159
+ end
160
+
161
+ # Generates a list of attributes that should be rendered by the index (table) view.
162
+ #
163
+ # Returns an Array of Trestle::Attribute and/or Trestle::Attribute::Association objects.
164
+ def default_table_attributes
165
+ raise NotImplementedError
166
+ end
167
+
168
+ # Generates a list of attributes that should be rendered by the new/show/edit (form) views.
169
+ #
170
+ # Returns an Array of Trestle::Attribute and/or Trestle::Attribute::Association objects.
171
+ def default_form_attributes
172
+ raise NotImplementedError
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,85 @@
1
+ begin
2
+ require "sequel"
3
+ rescue LoadError
4
+ $stderr.puts "You don't have sequel installed in your application. Please add it to your Gemfile and run bundle install"
5
+ raise
6
+ end
7
+
8
+ Sequel::Model.plugin :active_model
9
+
10
+ module Trestle
11
+ module Adapters
12
+ module SequelAdapter
13
+ def collection(params={})
14
+ model.dataset
15
+ end
16
+
17
+ def find_instance(params)
18
+ model[params[:id]]
19
+ end
20
+
21
+ def build_instance(attrs={}, params={})
22
+ model.new(attrs)
23
+ end
24
+
25
+ def update_instance(instance, attrs, params={})
26
+ instance.set(attrs)
27
+ end
28
+
29
+ def save_instance(instance)
30
+ instance.save
31
+ end
32
+
33
+ def delete_instance(instance)
34
+ instance.destroy
35
+ end
36
+
37
+ def unscope(scope)
38
+ scope.unfiltered
39
+ end
40
+
41
+ def merge_scopes(scope, other)
42
+ scope.intersect(other)
43
+ end
44
+
45
+ def count(collection)
46
+ collection.count
47
+ end
48
+
49
+ def sort(collection, field, order)
50
+ collection.order(Sequel.send(order, field))
51
+ end
52
+
53
+ def default_table_attributes
54
+ default_attributes.reject do |attribute|
55
+ inheritance_column?(attribute)
56
+ end
57
+ end
58
+
59
+ def default_form_attributes
60
+ default_attributes.reject do |attribute|
61
+ primary_key?(attribute) || inheritance_column?(attribute)
62
+ end
63
+ end
64
+
65
+ protected
66
+ def default_attributes
67
+ model.db_schema.map do |column_name, column_attrs|
68
+ if column_name.to_s.end_with?("_id") && (name = column_name.to_s.sub(/_id$/, '')) && (reflection = model.association_reflection(name.to_sym))
69
+ Attribute::Association.new(column_name, class: -> { reflection.associated_class }, name: name)
70
+ else
71
+ Attribute.new(column_name, column_attrs[:type])
72
+ end
73
+ end
74
+ end
75
+
76
+ def primary_key?(attribute)
77
+ attribute.name.to_s == model.primary_key.to_s
78
+ end
79
+
80
+ def inheritance_column?(attribute)
81
+ model.respond_to?(:sti_key) && attribute.name.to_s == model.sti_key.to_s
82
+ end
83
+ end
84
+ end
85
+ end
@@ -2,73 +2,16 @@ module Trestle
2
2
  module Adapters
3
3
  extend ActiveSupport::Autoload
4
4
 
5
+ autoload :Adapter
6
+
5
7
  autoload :ActiveRecordAdapter
6
8
  autoload :DraperAdapter
9
+ autoload :SequelAdapter
7
10
 
8
- class Adapter
9
- attr_reader :admin
10
-
11
- def initialize(admin)
12
- @admin = admin
13
- end
14
-
15
- def collection(params={})
16
- raise NotImplementedError
17
- end
18
-
19
- def find_instance(params)
20
- raise NotImplementedError
21
- end
22
-
23
- def build_instance(params={})
24
- raise NotImplementedError
25
- end
26
-
27
- def update_instance(instance, params)
28
- raise NotImplementedError
29
- end
30
-
31
- def save_instance(instance)
32
- raise NotImplementedError
33
- end
34
-
35
- def delete_instance(instance)
36
- raise NotImplementedError
37
- end
38
-
39
- def decorate_collection(collection)
40
- collection
41
- end
42
-
43
- def merge_scopes(scope, other)
44
- raise NotImplementedError
45
- end
46
-
47
- def sort(collection, params)
48
- raise NotImplementedError
49
- end
50
-
51
- def paginate(collection, params)
52
- raise NotImplementedError
53
- end
54
-
55
- def count(collection)
56
- raise NotImplementedError
57
- end
58
-
59
- def permitted_params(params)
60
- params.require(admin.admin_name.singularize).permit!
61
- end
62
-
63
- def default_attributes
64
- raise NotImplementedError
65
- end
66
-
67
- # Creates a new Adapter class with the given modules mixed in
68
- def self.compose(*modules)
69
- Class.new(self) do
70
- modules.each { |mod| include(mod) }
71
- end
11
+ # Creates a new Adapter class with the given modules mixed in
12
+ def self.compose(*modules)
13
+ Class.new(Adapter) do
14
+ modules.each { |mod| include(mod) }
72
15
  end
73
16
  end
74
17
  end
@@ -42,8 +42,8 @@ module Trestle
42
42
  admin.table = Table::Builder.build(options.reverse_merge(admin: admin, sortable: true), &block)
43
43
  end
44
44
 
45
- def form(&block)
46
- admin.form = Form.new(&block)
45
+ def form(options={}, &block)
46
+ admin.form = Form.new(options, &block)
47
47
  end
48
48
 
49
49
  def admin(&block)
@@ -59,6 +59,14 @@ module Trestle
59
59
  def routes(&block)
60
60
  @admin.additional_routes = block
61
61
  end
62
+
63
+ def breadcrumb(label=nil, path=nil, &block)
64
+ if block_given?
65
+ @admin.breadcrumb = block
66
+ else
67
+ @admin.breadcrumb = -> { Breadcrumb.new(label, path) }
68
+ end
69
+ end
62
70
  end
63
71
  end
64
72
  end
data/lib/trestle/admin.rb CHANGED
@@ -14,6 +14,7 @@ module Trestle
14
14
  attr_accessor :additional_routes
15
15
 
16
16
  attr_writer :options
17
+ attr_writer :breadcrumb
17
18
 
18
19
  def options
19
20
  @options ||= {}
@@ -24,7 +25,15 @@ module Trestle
24
25
  end
25
26
 
26
27
  def breadcrumb
27
- Breadcrumb.new(admin_name.titleize.pluralize, path)
28
+ if @breadcrumb
29
+ Breadcrumb.cast(@breadcrumb.call)
30
+ else
31
+ default_breadcrumb
32
+ end
33
+ end
34
+
35
+ def default_breadcrumb
36
+ Breadcrumb.new(I18n.t("admin.breadcrumbs.#{admin_name}", default: admin_name.titleize), path)
28
37
  end
29
38
 
30
39
  def admin_name
@@ -47,6 +56,10 @@ module Trestle
47
56
  Engine.routes.url_for(options.merge(controller: controller_namespace, action: action, only_path: true))
48
57
  end
49
58
 
59
+ def actions
60
+ [:index]
61
+ end
62
+
50
63
  def routes
51
64
  admin = self
52
65
 
@@ -1,53 +1,30 @@
1
1
  module Trestle
2
2
  class Attribute
3
- attr_reader :admin, :name, :type
3
+ attr_reader :name, :type, :options
4
4
 
5
- def initialize(admin, name, type)
6
- @admin, @name, @type = admin, name.to_sym, type
5
+ def initialize(name, type, options={})
6
+ @name, @type, @options = name.to_sym, type, options
7
7
  end
8
8
 
9
- def association?
10
- type == :association
11
- end
12
-
13
- def boolean?
14
- type == :boolean
15
- end
16
-
17
- def text?
18
- type == :text
19
- end
20
-
21
- def datetime?
22
- [:datetime, :time, :date].include?(type)
23
- end
24
-
25
- def primary_key?
26
- name.to_s == admin.model.primary_key
27
- end
28
-
29
- def inheritance_column?
30
- name.to_s == admin.model.inheritance_column
31
- end
32
-
33
- def counter_cache?
34
- name.to_s.end_with?("_count")
9
+ def array?
10
+ options[:array] == true
35
11
  end
36
12
 
37
13
  class Association < Attribute
38
- attr_reader :association_class
39
-
40
- def initialize(admin, name, association_class)
41
- super(admin, name, :association)
42
- @association_class = association_class
14
+ def initialize(name, options={})
15
+ super(name, :association, options)
43
16
  end
44
17
 
45
18
  def association_name
46
- name.to_s.sub(/_id$/, "")
19
+ options[:name] || name.to_s.sub(/_id$/, "")
20
+ end
21
+
22
+ def association_class
23
+ options[:class].respond_to?(:call) ? options[:class].call : options[:class]
47
24
  end
48
25
 
49
- def association_admin
50
- Trestle.admins[association_class.name.underscore.pluralize]
26
+ def polymorphic?
27
+ options[:polymorphic] == true
51
28
  end
52
29
  end
53
30
  end
@@ -10,11 +10,30 @@ module Trestle
10
10
  label == other.label && path == other.path
11
11
  end
12
12
 
13
+ def self.cast(obj)
14
+ case obj
15
+ when Breadcrumb
16
+ obj
17
+ when String
18
+ new(obj)
19
+ when Array
20
+ new(*obj)
21
+ else
22
+ raise ArgumentError, "Unable to cast #{obj.inspect} to Breadcrumb"
23
+ end
24
+ end
25
+
13
26
  class Trail
27
+ include Enumerable
28
+
14
29
  def initialize(breadcrumbs=[])
15
30
  @breadcrumbs = Array(breadcrumbs)
16
31
  end
17
32
 
33
+ def ==(other)
34
+ to_a == other.to_a
35
+ end
36
+
18
37
  def dup
19
38
  self.class.new(@breadcrumbs.dup)
20
39
  end
@@ -64,10 +64,10 @@ module Trestle
64
64
  option :persistent_params, [:sort, :order, :scope]
65
65
 
66
66
  # List of methods to try calling on an instance when displayed by the `display` helper
67
- option :display_methods, [:display_name, :full_name, :name, :title, :username, :login, :email, :to_s]
67
+ option :display_methods, [:display_name, :full_name, :name, :title, :username, :login, :email]
68
68
 
69
69
  # Default adapter class used by all admin resources
70
- option :default_adapter, Adapters::Adapter.compose(Adapters::ActiveRecordAdapter, Adapters::DraperAdapter)
70
+ option :default_adapter, Adapters.compose(Adapters::ActiveRecordAdapter, Adapters::DraperAdapter)
71
71
 
72
72
  # Register a custom form field class
73
73
  def form_field(name, klass)
@@ -83,7 +83,18 @@ module Trestle
83
83
  end
84
84
 
85
85
  # List of i18n keys to pass into the Trestle.i18n JavaScript object
86
- option :javascript_i18n_keys, ["admin.confirmation.title", "admin.confirmation.delete", "admin.confirmation.cancel"]
86
+ option :javascript_i18n_keys, ["admin.confirmation.title", "admin.confirmation.delete", "admin.confirmation.cancel", "trestle.dialog.error", "admin.buttons.ok"]
87
+
88
+ # List of load paths for where to find admin definitions
89
+ option :load_paths, [
90
+ -> { ActiveSupport::Dependencies.autoload_paths.grep(/\/app\/admin\Z/) }
91
+ ]
92
+
93
+
94
+ ## Debugging
95
+
96
+ # Enable debugging of form errors
97
+ option :debug_form_errors, Rails.env.development?
87
98
 
88
99
 
89
100
  ## Callbacks
@@ -14,7 +14,7 @@ module Trestle
14
14
 
15
15
  private
16
16
  def display_method
17
- @display_method ||= Trestle.config.display_methods.find { |m| @instance.respond_to?(m) }
17
+ @display_method ||= Trestle.config.display_methods.find { |m| @instance.respond_to?(m) } || :to_s
18
18
  end
19
19
  end
20
20
  end