administrate 0.17.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -2
  3. data/app/assets/javascripts/administrate/application.js +0 -2
  4. data/app/assets/javascripts/administrate/components/select.js +3 -0
  5. data/app/assets/stylesheets/administrate/application.scss +0 -1
  6. data/app/assets/stylesheets/administrate/base/_forms.scss +1 -1
  7. data/app/assets/stylesheets/administrate/components/_buttons.scss +9 -0
  8. data/app/assets/stylesheets/administrate/components/_field-unit.scss +7 -0
  9. data/app/assets/stylesheets/administrate/components/_flashes.scss +2 -2
  10. data/app/assets/stylesheets/administrate/library/_variables.scss +1 -1
  11. data/app/controllers/administrate/application_controller.rb +85 -17
  12. data/app/controllers/concerns/administrate/punditize.rb +44 -20
  13. data/app/helpers/administrate/application_helper.rb +24 -6
  14. data/app/views/administrate/application/_collection.html.erb +5 -6
  15. data/app/views/administrate/application/_collection_header_actions.html.erb +2 -2
  16. data/app/views/administrate/application/_collection_item_actions.html.erb +4 -4
  17. data/app/views/administrate/application/_form.html.erb +19 -4
  18. data/app/views/administrate/application/_index_header.html.erb +2 -2
  19. data/app/views/administrate/application/_navigation.html.erb +2 -2
  20. data/app/views/administrate/application/_pagination.html.erb +1 -0
  21. data/app/views/administrate/application/edit.html.erb +2 -2
  22. data/app/views/administrate/application/index.html.erb +1 -1
  23. data/app/views/administrate/application/new.html.erb +1 -1
  24. data/app/views/administrate/application/show.html.erb +28 -12
  25. data/app/views/fields/belongs_to/_index.html.erb +1 -1
  26. data/app/views/fields/belongs_to/_show.html.erb +1 -1
  27. data/app/views/fields/date/_form.html.erb +1 -3
  28. data/app/views/fields/date_time/_form.html.erb +1 -3
  29. data/app/views/fields/has_many/_index.html.erb +1 -1
  30. data/app/views/fields/has_many/_show.html.erb +2 -1
  31. data/app/views/fields/has_one/_form.html.erb +15 -5
  32. data/app/views/fields/has_one/_index.html.erb +2 -1
  33. data/app/views/fields/has_one/_show.html.erb +20 -13
  34. data/app/views/fields/polymorphic/_index.html.erb +2 -1
  35. data/app/views/fields/polymorphic/_show.html.erb +1 -1
  36. data/app/views/fields/select/_form.html.erb +5 -18
  37. data/app/views/fields/time/_form.html.erb +2 -3
  38. data/app/views/fields/url/_index.html.erb +1 -1
  39. data/app/views/fields/url/_show.html.erb +1 -1
  40. data/app/views/layouts/administrate/application.html.erb +1 -1
  41. data/config/locales/administrate.de.yml +2 -2
  42. data/config/locales/administrate.ja.yml +5 -5
  43. data/config/locales/administrate.sl.yml +30 -0
  44. data/docs/adding_controllers_without_related_model.md +2 -2
  45. data/docs/authorization.md +43 -20
  46. data/docs/customizing_controller_actions.md +25 -6
  47. data/docs/customizing_dashboards.md +88 -10
  48. data/docs/getting_started.md +1 -1
  49. data/docs/guides/customising_search.md +149 -0
  50. data/docs/guides/hiding_dashboards_from_sidebar.md +4 -2
  51. data/docs/guides/scoping_has_many_relations.md +27 -0
  52. data/docs/guides.md +3 -1
  53. data/lib/administrate/base_dashboard.rb +39 -3
  54. data/lib/administrate/engine.rb +2 -2
  55. data/lib/administrate/field/associative.rb +18 -8
  56. data/lib/administrate/field/base.rb +4 -0
  57. data/lib/administrate/field/belongs_to.rb +9 -2
  58. data/lib/administrate/field/deferred.rb +5 -1
  59. data/lib/administrate/field/has_many.rb +20 -6
  60. data/lib/administrate/field/has_one.rb +4 -0
  61. data/lib/administrate/field/number.rb +2 -8
  62. data/lib/administrate/field/polymorphic.rb +2 -1
  63. data/lib/administrate/field/select.rb +19 -9
  64. data/lib/administrate/field/url.rb +4 -0
  65. data/lib/administrate/not_authorized_error.rb +20 -0
  66. data/lib/administrate/order.rb +73 -17
  67. data/lib/administrate/page/base.rb +4 -0
  68. data/lib/administrate/page/collection.rb +1 -0
  69. data/lib/administrate/page/form.rb +9 -8
  70. data/lib/administrate/page/show.rb +10 -2
  71. data/lib/administrate/resource_resolver.rb +2 -1
  72. data/lib/administrate/search.rb +1 -1
  73. data/lib/administrate/version.rb +1 -1
  74. data/lib/administrate/view_generator.rb +6 -1
  75. data/lib/administrate.rb +24 -2
  76. data/lib/generators/administrate/dashboard/dashboard_generator.rb +20 -2
  77. data/lib/generators/administrate/dashboard/templates/controller.rb.erb +2 -2
  78. data/lib/generators/administrate/install/install_generator.rb +6 -1
  79. data/lib/generators/administrate/routes/routes_generator.rb +11 -2
  80. data/lib/generators/administrate/test_record.rb +21 -0
  81. metadata +39 -46
  82. data/app/assets/javascripts/administrate/components/date_time_picker.js +0 -14
  83. data/config/i18n-tasks.yml +0 -18
  84. data/config/routes.rb +0 -2
  85. data/config/unicorn.rb +0 -25
@@ -0,0 +1,149 @@
1
+ ---
2
+ title: Customising the search
3
+ ---
4
+
5
+ Administrate dashboards provide a search function, but it is quite basic.
6
+ Things like search across complex associations, inside JSON columns, or outside
7
+ the database (eg: an Elasticsearch index) are not possible out of the box.
8
+
9
+ Fortunately, Administrate is just Rails, so you can use your existing Rails
10
+ knowledge to customize the search feature. Let's look into that.
11
+
12
+ ## In short
13
+
14
+ Override the `filter_resources` method in your admin controllers in order
15
+ to customize the search.
16
+
17
+ It has two parameters:
18
+
19
+ * `resources`: an ActiveRecord relation for the model on whose dashboard the
20
+ search originated.
21
+ * `search_term:`: a string representing the search query entered by the user.
22
+
23
+ Return an ActiveRecord relation for the same model as `resources`, matching
24
+ the desired search results.
25
+
26
+ ## In more detail
27
+
28
+ When you install Administrate in your application, it generates an admin
29
+ controller for each of your ActiveRecord models, as well as a base controller
30
+ that all of these inherit from.
31
+
32
+ For example, if you have two ActiveRecord models: `Person` and `Address`,
33
+ running `rails generate administrate:install` will get you the following
34
+ files (plus others that are not relevant here):
35
+
36
+ * `app/controllers/admin/people_controller.rb`
37
+ * `app/controllers/admin/addresses_controller.rb`
38
+ * `app/controllers/admin/application_controller.rb`
39
+
40
+ By default, searches are handled by the `index` action of the controller that
41
+ the user was visiting when they performed the search. For example, if a user
42
+ is visiting the People dashboard and submits a search, the user is sent to
43
+ the path `/admin/people?search=<search query>`. This is routed to
44
+ `Admin::PeopleController#index`, where the search query can be read as
45
+ `params[:search]`.
46
+
47
+ By default, these controllers are empty. Administrate's code is implemented
48
+ at `Administrate::ApplicationController`, from which all inherit. This is
49
+ where search is implemented. You can read the code yourself at:
50
+ https://github.com/thoughtbot/administrate/blob/main/app/controllers/administrate/application_controller.rb.
51
+
52
+ It is in the linked code that you can see what Administrate actually does.
53
+ For example, this is the `index` action at the time of writing these lines:
54
+
55
+ ```ruby
56
+ def index
57
+ authorize_resource(resource_class)
58
+ search_term = params[:search].to_s.strip
59
+ resources = filter_resources(scoped_resource, search_term: search_term)
60
+ resources = apply_collection_includes(resources)
61
+ resources = order.apply(resources)
62
+ resources = resources.page(params[:_page]).per(records_per_page)
63
+ page = Administrate::Page::Collection.new(dashboard, order: order)
64
+
65
+ render locals: {
66
+ resources: resources,
67
+ search_term: search_term,
68
+ page: page,
69
+ show_search_bar: show_search_bar?,
70
+ }
71
+ end
72
+ ```
73
+
74
+ What the above does is applying a few transforms
75
+ to the variable `resources`, filtering it, applying includes for associations,
76
+ ordering the results, paginating them, and finally handing them over to the
77
+ template in order to be rendered. All this is pretty standard Rails, although
78
+ split into individual steps that can be overriden by developers in order
79
+ to add customizations, and ultimately wrapped in an instance of
80
+ `Administrate::Page::Collection` which will read your dashboard definitions
81
+ and figure out what fields you want displayed.
82
+
83
+ It is the filtering part where the search is implemented. You will notice the
84
+ `filter_resources` method, which takes a parameter `search_term`. This is what
85
+ this method looks like at the moment:
86
+
87
+ ```ruby
88
+ def filter_resources(resources, search_term:)
89
+ Administrate::Search.new(
90
+ resources,
91
+ dashboard,
92
+ search_term,
93
+ ).run
94
+ end
95
+ ```
96
+
97
+ The class `Administrate::Search` implements the default search facilities
98
+ within Administrate... but you do not have to worry about it! You can ignore
99
+ it and implement your own search in `filter_resources`. For example, you
100
+ could write your own version in your controller, to override Administrate's
101
+ own. Something like this:
102
+
103
+ ```ruby
104
+ def filter_resources(resources, search_term:)
105
+ resources.where(first_name: search_term)
106
+ .or(People.where(last_name: search_term))
107
+ end
108
+ ```
109
+
110
+ It can be as complex (or simple) as you want, as long as the return value
111
+ of the method is an ActiveRecord relation.
112
+
113
+ What if you do not want to search in the DB? For example, say that your records
114
+ are indexed by Elasticsearch or something like that. You can still search
115
+ in your external index and convert the results to an ActiveRecord relation.
116
+ Here's an example:
117
+
118
+ ```ruby
119
+ def filter_resources(resources, search_term:)
120
+ # Run the search term through your search facility
121
+ results = MySuperDuperSearchSystem.search_people(search_term)
122
+
123
+ # Collect the ids of the results. This assumes that they will
124
+ # be the same ones as in the DB.
125
+ record_ids = results.entries.map(&:id)
126
+
127
+ # Use the ids to create an ActiveRecord relation and return it
128
+ People.where(id: record_ids)
129
+ end
130
+ ```
131
+
132
+ Note though: the records must still exist in the DB. Administrate does
133
+ require ActiveRecord in order to show tables, and to display, create and edit
134
+ records.
135
+
136
+ ## A working example
137
+
138
+ The [Administrate demo app](https://administrate-demo.herokuapp.com/admin)
139
+ includes an example of custom search in the "Log Entries" dashboard.
140
+ In this app, each `LogEntry` instance has a polymorphic `belongs_to`
141
+ association to a `:logeable`. Logeables are other models for which logs can be
142
+ created. At the moment these are `Order` and `Customer`.
143
+
144
+ Administrate's default search is not able to search across polymorphic
145
+ associations, and therefore it is not possible to search logs by the contents
146
+ of their logeables. Fortunately this can be fixed with a custom search. This is
147
+ done by implementing `Admin::LogEntriesController#filter_resources` to override
148
+ the default search. You can see the code at
149
+ https://github.com/thoughtbot/administrate/blob/main/spec/example_app/app/controllers/admin/log_entries_controller.rb
@@ -2,7 +2,8 @@
2
2
  title: Hiding Dashboards from the Sidebar
3
3
  ---
4
4
 
5
- Resources can be removed form the sidebar by removing their index action from the routes. For example:
5
+ Resources can be removed from the sidebar by removing their `index` action
6
+ from the routes. For example:
6
7
 
7
8
  ```ruby
8
9
  # config/routes.rb
@@ -16,4 +17,5 @@ Rails.application.routes.draw do
16
17
  end
17
18
  ```
18
19
 
19
- In this case, only Orders and Products will appear in the sidebar, while Line Items can still appear as an association.
20
+ In this case, only Orders and Products will appear in the sidebar, while
21
+ Line Items can still appear as an association.
@@ -0,0 +1,27 @@
1
+ ---
2
+ title: Scoping HasMany Relations
3
+ ---
4
+
5
+ To show a subset of a has_many relationship, create a new [has_many](https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many) relationship in your model (using the `scope` argument) and add it to the model's dashboard.
6
+
7
+ ## Creating a scoped has_many relationship
8
+
9
+ Models can define subsets of a `has_many` relationship by passing a callable (i.e. proc or lambda) as its second argument.
10
+
11
+ ```ruby
12
+ class Customer < ApplicationRecord
13
+ has_many :orders
14
+ has_many :processed_orders, ->{ where(processed: true) }, class_name: "Order"
15
+ ```
16
+
17
+ Since ActiveRecord infers the class name from the first argument, the new `has_many` relation needs to specify the model using the `class_name` option.
18
+
19
+ ## Add new relationship to dashboard
20
+
21
+ Your new scoped relation can be used in the dashboard just like the original `HasMany`. Notice the new field needs to specifiy the class name as an option like you did in the model.
22
+
23
+ ```ruby
24
+ ATTRIBUTE_TYPES = {
25
+ orders: Field::HasMany,
26
+ processed_orders: Field::HasMany.with_options(class_name: 'Order')
27
+ ```
data/docs/guides.md CHANGED
@@ -2,4 +2,6 @@
2
2
  title: Guides
3
3
  ---
4
4
 
5
- * [Hiding Dashboards from the Sidebar](./guides/hiding_dashboards_from_sidebar)
5
+ - [Hiding Dashboards from the Sidebar](./guides/hiding_dashboards_from_sidebar)
6
+ - [Customising the search](./guides/customising_search)
7
+ - [Scoping HasMany Relations](./guides/scoping_has_many_relations.md)
@@ -51,6 +51,12 @@ module Administrate
51
51
  end
52
52
 
53
53
  def form_attributes(action = nil)
54
+ action =
55
+ case action
56
+ when "update" then "edit"
57
+ when "create" then "new"
58
+ else action
59
+ end
54
60
  specific_form_attributes_for(action) || self.class::FORM_ATTRIBUTES
55
61
  end
56
62
 
@@ -62,11 +68,18 @@ module Administrate
62
68
  self.class.const_get(cname) if self.class.const_defined?(cname)
63
69
  end
64
70
 
65
- def permitted_attributes
66
- form_attributes.map do |attr|
71
+ def permitted_attributes(action = nil)
72
+ attributes = form_attributes action
73
+
74
+ if attributes.is_a? Hash
75
+ attributes = attributes.values.flatten
76
+ end
77
+
78
+ attributes.map do |attr|
67
79
  attribute_types[attr].permitted_attribute(
68
80
  attr,
69
81
  resource_class: self.class.model,
82
+ action: action,
70
83
  )
71
84
  end.uniq
72
85
  end
@@ -76,7 +89,11 @@ module Administrate
76
89
  end
77
90
 
78
91
  def collection_attributes
79
- self.class::COLLECTION_ATTRIBUTES
92
+ if self.class::COLLECTION_ATTRIBUTES.is_a?(Hash)
93
+ self.class::COLLECTION_ATTRIBUTES.values.flatten
94
+ else
95
+ self.class::COLLECTION_ATTRIBUTES
96
+ end
80
97
  end
81
98
 
82
99
  def search_attributes
@@ -94,9 +111,20 @@ module Administrate
94
111
  end
95
112
 
96
113
  def item_includes
114
+ # Deprecated, internal usage has moved to #item_associations
115
+ Administrate.warn_of_deprecated_method(self.class, :item_includes)
97
116
  attribute_includes(show_page_attributes)
98
117
  end
99
118
 
119
+ def item_associations
120
+ attributes = if show_page_attributes.is_a?(Hash)
121
+ show_page_attributes.values.flatten
122
+ else
123
+ show_page_attributes
124
+ end
125
+ attribute_associated attributes
126
+ end
127
+
100
128
  private
101
129
 
102
130
  def attribute_not_found_message(attr)
@@ -104,6 +132,14 @@ module Administrate
104
132
  end
105
133
 
106
134
  def attribute_includes(attributes)
135
+ attributes.map do |key|
136
+ field = attribute_type_for(key)
137
+
138
+ key if field.eager_load?
139
+ end.compact
140
+ end
141
+
142
+ def attribute_associated(attributes)
107
143
  attributes.map do |key|
108
144
  field = attribute_type_for(key)
109
145
 
@@ -1,11 +1,11 @@
1
- require "datetime_picker_rails"
2
1
  require "jquery-rails"
3
2
  require "kaminari"
4
- require "momentjs-rails"
5
3
  require "sassc-rails"
6
4
  require "selectize-rails"
7
5
  require "sprockets/railtie"
8
6
 
7
+ require "administrate/namespace/resource"
8
+ require "administrate/not_authorized_error"
9
9
  require "administrate/page/form"
10
10
  require "administrate/page/show"
11
11
  require "administrate/page/collection"
@@ -7,12 +7,16 @@ module Administrate
7
7
  reflection(resource_class, attr).foreign_key
8
8
  end
9
9
 
10
+ def self.association_primary_key_for(resource_class, attr)
11
+ reflection(resource_class, attr).association_primary_key
12
+ end
13
+
10
14
  def self.associated_class(resource_class, attr)
11
15
  reflection(resource_class, attr).klass
12
16
  end
13
17
 
14
18
  def self.associated_class_name(resource_class, attr)
15
- reflection(resource_class, attr).class_name
19
+ associated_class(resource_class, attr).name
16
20
  end
17
21
 
18
22
  def self.reflection(resource_class, attr)
@@ -31,12 +35,6 @@ module Administrate
31
35
  end
32
36
  end
33
37
 
34
- private
35
-
36
- def associated_dashboard
37
- "#{associated_class_name}Dashboard".constantize.new
38
- end
39
-
40
38
  def associated_class_name
41
39
  if option_given?(:class_name)
42
40
  deprecated_option(:class_name)
@@ -48,11 +46,23 @@ module Administrate
48
46
  end
49
47
  end
50
48
 
49
+ private
50
+
51
+ def associated_dashboard
52
+ "#{associated_class_name}Dashboard".constantize.new
53
+ end
54
+
51
55
  def primary_key
56
+ # Deprecated, renamed `association_primary_key`
57
+ Administrate.warn_of_deprecated_method(self.class, :primary_key)
58
+ association_primary_key
59
+ end
60
+
61
+ def association_primary_key
52
62
  if option_given?(:primary_key)
53
63
  deprecated_option(:primary_key)
54
64
  else
55
- :id
65
+ self.class.association_primary_key_for(resource.class, attribute)
56
66
  end
57
67
  end
58
68
 
@@ -16,6 +16,10 @@ module Administrate
16
16
  self < Associative
17
17
  end
18
18
 
19
+ def self.eager_load?
20
+ false
21
+ end
22
+
19
23
  def self.searchable?
20
24
  false
21
25
  end
@@ -13,18 +13,25 @@ module Administrate
13
13
  end
14
14
  end
15
15
 
16
+ def self.eager_load?
17
+ true
18
+ end
19
+
16
20
  def permitted_attribute
17
21
  foreign_key
18
22
  end
19
23
 
20
24
  def associated_resource_options
21
25
  candidate_resources.map do |resource|
22
- [display_candidate_resource(resource), resource.send(primary_key)]
26
+ [
27
+ display_candidate_resource(resource),
28
+ resource.send(association_primary_key),
29
+ ]
23
30
  end
24
31
  end
25
32
 
26
33
  def selected_option
27
- data && data.send(primary_key)
34
+ data&.send(association_primary_key)
28
35
  end
29
36
 
30
37
  def include_blank_option
@@ -25,12 +25,16 @@ module Administrate
25
25
  deferred_class.associative?
26
26
  end
27
27
 
28
+ def eager_load?
29
+ deferred_class.eager_load?
30
+ end
31
+
28
32
  def searchable?
29
33
  options.fetch(:searchable, deferred_class.searchable?)
30
34
  end
31
35
 
32
36
  def searchable_field
33
- ActiveSupport::Deprecation.warn(
37
+ Administrate.deprecator.warn(
34
38
  "searchable_field is deprecated, use searchable_fields instead",
35
39
  )
36
40
  options.fetch(:searchable_field)
@@ -22,7 +22,11 @@ module Administrate
22
22
  end
23
23
 
24
24
  def associated_collection(order = self.order)
25
- Administrate::Page::Collection.new(associated_dashboard, order: order)
25
+ Administrate::Page::Collection.new(
26
+ associated_dashboard,
27
+ order: order,
28
+ collection_attributes: options[:collection_attributes],
29
+ )
26
30
  end
27
31
 
28
32
  def attribute_key
@@ -30,21 +34,28 @@ module Administrate
30
34
  end
31
35
 
32
36
  def associated_resource_options
33
- candidate_resources.map do |resource|
34
- [display_candidate_resource(resource), resource.send(primary_key)]
37
+ candidate_resources.map do |associated_resource|
38
+ [
39
+ display_candidate_resource(associated_resource),
40
+ associated_resource.send(association_primary_key),
41
+ ]
35
42
  end
36
43
  end
37
44
 
38
45
  def selected_options
39
46
  return if data.empty?
40
47
 
41
- data.map { |object| object.send(primary_key) }
48
+ data.map { |object| object.send(association_primary_key) }
42
49
  end
43
50
 
44
51
  def limit
45
52
  options.fetch(:limit, DEFAULT_LIMIT)
46
53
  end
47
54
 
55
+ def paginate?
56
+ limit.respond_to?(:positive?) ? limit.positive? : limit.present?
57
+ end
58
+
48
59
  def permitted_attribute
49
60
  self.class.permitted_attribute(
50
61
  attribute,
@@ -53,12 +64,15 @@ module Administrate
53
64
  end
54
65
 
55
66
  def resources(page = 1, order = self.order)
56
- resources = order.apply(data).page(page).per(limit)
67
+ resources = order.apply(data)
68
+ if paginate?
69
+ resources = resources.page(page).per(limit)
70
+ end
57
71
  includes.any? ? resources.includes(*includes) : resources
58
72
  end
59
73
 
60
74
  def more_than_limit?
61
- data.count(:all) > limit
75
+ paginate? && data.count(:all) > limit
62
76
  end
63
77
 
64
78
  def data
@@ -26,6 +26,10 @@ module Administrate
26
26
  { "#{attr}_attributes": related_dashboard_attributes }
27
27
  end
28
28
 
29
+ def self.eager_load?
30
+ true
31
+ end
32
+
29
33
  def nested_form
30
34
  @nested_form ||= Administrate::Page::Form.new(
31
35
  resolver.dashboard_class.new,
@@ -38,14 +38,8 @@ module Administrate
38
38
  formatter = options[:format][:formatter]
39
39
  formatter_options = options[:format][:formatter_options].to_h
40
40
 
41
- case formatter
42
- when :number_to_delimited
43
- ActiveSupport::NumberHelper.number_to_delimited(
44
- result, **formatter_options
45
- )
46
- else
47
- result
48
- end
41
+ ActiveSupport::NumberHelper.
42
+ try(formatter, result, **formatter_options) || result
49
43
  end
50
44
  end
51
45
  end
@@ -30,7 +30,8 @@ module Administrate
30
30
  end
31
31
 
32
32
  def classes
33
- options.fetch(:classes, [])
33
+ klasses = options.fetch(:classes, [])
34
+ klasses.respond_to?(:call) ? klasses.call : klasses
34
35
  end
35
36
 
36
37
  private
@@ -8,22 +8,32 @@ module Administrate
8
8
  end
9
9
 
10
10
  def selectable_options
11
- collection
11
+ values =
12
+ if options.key?(:collection)
13
+ options.fetch(:collection)
14
+ elsif active_record_enum?
15
+ active_record_enum_values
16
+ else
17
+ []
18
+ end
19
+
20
+ if values.respond_to? :call
21
+ values = values.arity.positive? ? values.call(self) : values.call
22
+ end
23
+
24
+ values
12
25
  end
13
26
 
14
27
  def include_blank_option
15
28
  options.fetch(:include_blank, false)
16
29
  end
17
30
 
18
- private
19
-
20
- def collection
21
- values = options.fetch(:collection, [])
22
- if values.respond_to? :call
23
- return values.arity.positive? ? values.call(self) : values.call
24
- end
31
+ def active_record_enum?
32
+ resource.class.defined_enums.key?(attribute.to_s)
33
+ end
25
34
 
26
- @collection ||= values
35
+ def active_record_enum_values
36
+ resource.class.defined_enums[attribute.to_s].map(&:first)
27
37
  end
28
38
  end
29
39
  end
@@ -11,6 +11,10 @@ module Administrate
11
11
  data.to_s[0...truncation_length]
12
12
  end
13
13
 
14
+ def html_options
15
+ @options[:html_options] || {}
16
+ end
17
+
14
18
  private
15
19
 
16
20
  def truncation_length
@@ -0,0 +1,20 @@
1
+ module Administrate
2
+ class NotAuthorizedError < StandardError
3
+ def initialize(action:, resource:)
4
+ @action = action
5
+ @resource = resource
6
+
7
+ case resource
8
+ when String, Symbol
9
+ super("Not allowed to perform #{action.inspect} on #{resource.inspect}")
10
+ when Module
11
+ super("Not allowed to perform #{action.inspect} on #{resource.name}")
12
+ else
13
+ super(
14
+ "Not allowed to perform #{action.inspect} on the given " +
15
+ resource.class.name
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end