administrate 0.17.0 → 0.20.0

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 (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