administrate 0.18.0 → 0.20.1

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/administrate/components/select.js +3 -0
  3. data/app/assets/stylesheets/administrate/components/_field-unit.scss +7 -0
  4. data/app/controllers/administrate/application_controller.rb +7 -4
  5. data/app/controllers/concerns/administrate/punditize.rb +43 -21
  6. data/app/views/administrate/application/_collection.html.erb +2 -3
  7. data/app/views/administrate/application/_form.html.erb +19 -4
  8. data/app/views/administrate/application/_index_header.html.erb +1 -1
  9. data/app/views/administrate/application/_navigation.html.erb +1 -1
  10. data/app/views/administrate/application/_pagination.html.erb +1 -1
  11. data/app/views/administrate/application/edit.html.erb +1 -1
  12. data/app/views/administrate/application/new.html.erb +1 -1
  13. data/app/views/administrate/application/show.html.erb +19 -11
  14. data/app/views/fields/has_many/_show.html.erb +2 -1
  15. data/app/views/fields/has_one/_form.html.erb +14 -4
  16. data/app/views/fields/has_one/_show.html.erb +18 -12
  17. data/app/views/fields/select/_form.html.erb +5 -18
  18. data/app/views/layouts/administrate/application.html.erb +1 -1
  19. data/config/locales/administrate.ja.yml +5 -5
  20. data/docs/authorization.md +18 -8
  21. data/docs/customizing_controller_actions.md +14 -0
  22. data/docs/customizing_dashboards.md +77 -14
  23. data/docs/customizing_page_views.md +1 -1
  24. data/docs/getting_started.md +1 -1
  25. data/docs/guides/customising_search.md +1 -1
  26. data/lib/administrate/base_dashboard.rb +26 -4
  27. data/lib/administrate/field/associative.rb +11 -1
  28. data/lib/administrate/field/belongs_to.rb +5 -2
  29. data/lib/administrate/field/deferred.rb +1 -1
  30. data/lib/administrate/field/has_many.rb +20 -6
  31. data/lib/administrate/field/number.rb +2 -8
  32. data/lib/administrate/field/polymorphic.rb +2 -1
  33. data/lib/administrate/field/select.rb +19 -9
  34. data/lib/administrate/not_authorized_error.rb +3 -1
  35. data/lib/administrate/order.rb +49 -23
  36. data/lib/administrate/page/collection.rb +1 -0
  37. data/lib/administrate/page/form.rb +9 -8
  38. data/lib/administrate/page/show.rb +10 -2
  39. data/lib/administrate/resource_resolver.rb +2 -1
  40. data/lib/administrate/search.rb +1 -1
  41. data/lib/administrate/version.rb +1 -1
  42. data/lib/administrate/view_generator.rb +6 -1
  43. data/lib/administrate.rb +9 -4
  44. data/lib/generators/administrate/dashboard/dashboard_generator.rb +6 -1
  45. data/lib/generators/administrate/dashboard/templates/controller.rb.erb +2 -2
  46. data/lib/generators/administrate/install/install_generator.rb +6 -1
  47. data/lib/generators/administrate/routes/routes_generator.rb +11 -2
  48. data/lib/generators/administrate/test_record.rb +21 -0
  49. metadata +37 -17
@@ -3,7 +3,7 @@ title: Getting Started
3
3
  ---
4
4
 
5
5
  Administrate is released as a Ruby gem, and can be installed on Rails
6
- applications version 5.0 or greater. We support Ruby 2.7 and up.
6
+ applications version 6.0 or greater. We support Ruby 3.0 and up.
7
7
 
8
8
  First, add the following to your Gemfile:
9
9
 
@@ -135,7 +135,7 @@ records.
135
135
 
136
136
  ## A working example
137
137
 
138
- The [Administrate demo app](/admin)
138
+ The [Administrate demo app](https://administrate-demo.herokuapp.com/admin)
139
139
  includes an example of custom search in the "Log Entries" dashboard.
140
140
  In this app, each `LogEntry` instance has a polymorphic `belongs_to`
141
141
  association to a `:logeable`. Logeables are other models for which logs can be
@@ -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
@@ -100,7 +117,12 @@ module Administrate
100
117
  end
101
118
 
102
119
  def item_associations
103
- attribute_associated(show_page_attributes)
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
104
126
  end
105
127
 
106
128
  private
@@ -7,6 +7,10 @@ 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
@@ -49,10 +53,16 @@ module Administrate
49
53
  end
50
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
 
@@ -23,12 +23,15 @@ module Administrate
23
23
 
24
24
  def associated_resource_options
25
25
  candidate_resources.map do |resource|
26
- [display_candidate_resource(resource), resource.send(primary_key)]
26
+ [
27
+ display_candidate_resource(resource),
28
+ resource.send(association_primary_key),
29
+ ]
27
30
  end
28
31
  end
29
32
 
30
33
  def selected_option
31
- data && data.send(primary_key)
34
+ data&.send(association_primary_key)
32
35
  end
33
36
 
34
37
  def include_blank_option
@@ -34,7 +34,7 @@ module Administrate
34
34
  end
35
35
 
36
36
  def searchable_field
37
- ActiveSupport::Deprecation.warn(
37
+ Administrate.deprecator.warn(
38
38
  "searchable_field is deprecated, use searchable_fields instead",
39
39
  )
40
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
@@ -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
@@ -5,8 +5,10 @@ module Administrate
5
5
  @resource = resource
6
6
 
7
7
  case resource
8
- when Module, String, Symbol
8
+ when String, Symbol
9
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}")
10
12
  else
11
13
  super(
12
14
  "Not allowed to perform #{action.inspect} on the given " +
@@ -10,9 +10,9 @@ module Administrate
10
10
  return order_by_association(relation) unless
11
11
  reflect_association(relation).nil?
12
12
 
13
- order = "#{relation.table_name}.#{attribute} #{direction}"
13
+ order = relation.arel_table[attribute].public_send(direction)
14
14
 
15
- return relation.reorder(Arel.sql(order)) if
15
+ return relation.reorder(order) if
16
16
  column_exist?(relation, attribute)
17
17
 
18
18
  relation
@@ -52,36 +52,57 @@ module Administrate
52
52
  end
53
53
 
54
54
  def order_by_association(relation)
55
- return order_by_count(relation) if has_many_attribute?(relation)
56
-
57
- return order_by_attribute(relation) if belongs_to_attribute?(relation)
58
-
59
- relation
55
+ case relation_type(relation)
56
+ when :has_many
57
+ order_by_count(relation)
58
+ when :belongs_to
59
+ order_by_belongs_to(relation)
60
+ when :has_one
61
+ order_by_has_one(relation)
62
+ else
63
+ relation
64
+ end
60
65
  end
61
66
 
62
67
  def order_by_count(relation)
63
68
  klass = reflect_association(relation).klass
64
- query = "COUNT(#{klass.table_name}.#{klass.primary_key}) #{direction}"
69
+ query = klass.arel_table[klass.primary_key].count.public_send(direction)
65
70
  relation.
66
71
  left_joins(attribute.to_sym).
67
72
  group(:id).
68
- reorder(Arel.sql(query))
73
+ reorder(query)
69
74
  end
70
75
 
71
- def order_by_id(relation)
72
- relation.reorder(Arel.sql(order_by_id_query(relation)))
76
+ def order_by_belongs_to(relation)
77
+ if ordering_by_association_column?(relation)
78
+ order_by_attribute(relation)
79
+ else
80
+ order_by_id(relation)
81
+ end
73
82
  end
74
83
 
75
- def order_by_attribute(relation)
84
+ def order_by_has_one(relation)
76
85
  if ordering_by_association_column?(relation)
77
- relation.joins(
78
- attribute.to_sym,
79
- ).reorder(Arel.sql(order_by_attribute_query))
86
+ order_by_attribute(relation)
80
87
  else
81
- order_by_id(relation)
88
+ order_by_association_id(relation)
82
89
  end
83
90
  end
84
91
 
92
+ def order_by_attribute(relation)
93
+ relation.joins(
94
+ attribute.to_sym,
95
+ ).reorder(order_by_attribute_query)
96
+ end
97
+
98
+ def order_by_id(relation)
99
+ relation.reorder(order_by_id_query(relation))
100
+ end
101
+
102
+ def order_by_association_id(relation)
103
+ relation.reorder(order_by_association_id_query)
104
+ end
105
+
85
106
  def ordering_by_association_column?(relation)
86
107
  association_attribute &&
87
108
  column_exist?(
@@ -94,19 +115,20 @@ module Administrate
94
115
  end
95
116
 
96
117
  def order_by_id_query(relation)
97
- "#{relation.table_name}.#{foreign_key(relation)} #{direction}"
118
+ relation.arel_table[foreign_key(relation)].public_send(direction)
98
119
  end
99
120
 
100
- def order_by_attribute_query
101
- "#{attribute.tableize}.#{association_attribute} #{direction}"
121
+ def order_by_association_id_query
122
+ Arel::Table.new(association_table_name)[:id].public_send(direction)
102
123
  end
103
124
 
104
- def has_many_attribute?(relation)
105
- reflect_association(relation).macro == :has_many
125
+ def order_by_attribute_query
126
+ table = Arel::Table.new(association_table_name)
127
+ table[association_attribute].public_send(direction)
106
128
  end
107
129
 
108
- def belongs_to_attribute?(relation)
109
- reflect_association(relation).macro == :belongs_to
130
+ def relation_type(relation)
131
+ reflect_association(relation).macro
110
132
  end
111
133
 
112
134
  def reflect_association(relation)
@@ -116,5 +138,9 @@ module Administrate
116
138
  def foreign_key(relation)
117
139
  reflect_association(relation).foreign_key
118
140
  end
141
+
142
+ def association_table_name
143
+ attribute.tableize
144
+ end
119
145
  end
120
146
  end
@@ -4,6 +4,7 @@ module Administrate
4
4
  module Page
5
5
  class Collection < Page::Base
6
6
  def attribute_names
7
+ options.fetch(:collection_attributes, nil) ||
7
8
  dashboard.collection_attributes
8
9
  end
9
10
 
@@ -11,15 +11,16 @@ module Administrate
11
11
  attr_reader :resource
12
12
 
13
13
  def attributes(action = nil)
14
- action =
15
- case action
16
- when "update" then "edit"
17
- when "create" then "new"
18
- else action
19
- end
14
+ attributes = dashboard.form_attributes(action)
15
+
16
+ if attributes.is_a? Array
17
+ attributes = { "" => attributes }
18
+ end
20
19
 
21
- dashboard.form_attributes(action).map do |attribute|
22
- attribute_field(dashboard, resource, attribute, :form)
20
+ attributes.transform_values do |attrs|
21
+ attrs.map do |attribute|
22
+ attribute_field(dashboard, resource, attribute, :form)
23
+ end
23
24
  end
24
25
  end
25
26
 
@@ -15,8 +15,16 @@ module Administrate
15
15
  end
16
16
 
17
17
  def attributes
18
- dashboard.show_page_attributes.map do |attr_name|
19
- attribute_field(dashboard, resource, attr_name, :show)
18
+ attributes = dashboard.show_page_attributes
19
+
20
+ if attributes.is_a? Array
21
+ attributes = { "" => attributes }
22
+ end
23
+
24
+ attributes.transform_values do |attrs|
25
+ attrs.map do |attr_name|
26
+ attribute_field(dashboard, resource, attr_name, :show)
27
+ end
20
28
  end
21
29
  end
22
30
  end
@@ -35,7 +35,8 @@ module Administrate
35
35
  end
36
36
 
37
37
  def controller_path_parts
38
- controller_path.split("/")[1..-1].map(&:singularize)
38
+ path_parts = controller_path.split("/")[1..-1]
39
+ path_parts << path_parts.pop.singularize
39
40
  end
40
41
 
41
42
  attr_reader :controller_path
@@ -31,7 +31,7 @@ module Administrate
31
31
  private
32
32
 
33
33
  def filter?(word)
34
- valid_filters&.any? { |filter| word.match?(/^#{filter}:\w*$/) }
34
+ valid_filters&.any? { |filter| word.match?(/^#{filter}:/) }
35
35
  end
36
36
 
37
37
  def parse_query(query)
@@ -1,3 +1,3 @@
1
1
  module Administrate
2
- VERSION = "0.18.0".freeze
2
+ VERSION = "0.20.1".freeze
3
3
  end
@@ -5,7 +5,12 @@ require "administrate/namespace"
5
5
  module Administrate
6
6
  class ViewGenerator < Rails::Generators::Base
7
7
  include Administrate::GeneratorHelpers
8
- class_option :namespace, type: :string, default: :admin
8
+ class_option(
9
+ :namespace,
10
+ type: :string,
11
+ desc: "Namespace where the admin dashboards live",
12
+ default: "admin",
13
+ )
9
14
 
10
15
  def self.template_source_path
11
16
  File.expand_path(
data/lib/administrate.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  require "administrate/engine"
2
+ require "administrate/version"
2
3
 
3
4
  module Administrate
4
5
  def self.warn_of_missing_resource_class
5
- ActiveSupport::Deprecation.warn(
6
+ deprecator.warn(
6
7
  "Calling Field::Base.permitted_attribute without the option " +
7
8
  ":resource_class is deprecated. If you are seeing this " +
8
9
  "message, you are probably using a custom field type that" +
@@ -12,7 +13,7 @@ module Administrate
12
13
  end
13
14
 
14
15
  def self.warn_of_deprecated_option(name)
15
- ActiveSupport::Deprecation.warn(
16
+ deprecator.warn(
16
17
  "The option :#{name} is deprecated. " +
17
18
  "Administrate should detect it automatically. " +
18
19
  "Please file an issue at " +
@@ -22,7 +23,7 @@ module Administrate
22
23
  end
23
24
 
24
25
  def self.warn_of_deprecated_method(klass, method)
25
- ActiveSupport::Deprecation.warn(
26
+ deprecator.warn(
26
27
  "The method #{klass}##{method} is deprecated. " +
27
28
  "If you are seeing this message you are probably " +
28
29
  "using a dashboard that depends explicitly on it. " +
@@ -32,10 +33,14 @@ module Administrate
32
33
  end
33
34
 
34
35
  def self.warn_of_deprecated_authorization_method(method)
35
- ActiveSupport::Deprecation.warn(
36
+ deprecator.warn(
36
37
  "The method `#{method}` is deprecated. " +
37
38
  "Please use `accessible_action?` instead, " +
38
39
  "or see the documentation for other options.",
39
40
  )
40
41
  end
42
+
43
+ def self.deprecator
44
+ @deprecator ||= ActiveSupport::Deprecation.new(VERSION, "Administrate")
45
+ end
41
46
  end
@@ -27,7 +27,12 @@ module Administrate
27
27
  COLLECTION_ATTRIBUTE_LIMIT = 4
28
28
  READ_ONLY_ATTRIBUTES = %w[id created_at updated_at]
29
29
 
30
- class_option :namespace, type: :string, default: :admin
30
+ class_option(
31
+ :namespace,
32
+ type: :string,
33
+ desc: "Namespace where the admin dashboards live",
34
+ default: "admin",
35
+ )
31
36
 
32
37
  source_root File.expand_path("../templates", __FILE__)
33
38
 
@@ -36,11 +36,11 @@ module <%= namespace.to_s.camelize %>
36
36
  #
37
37
  # def resource_params
38
38
  # params.require(resource_class.model_name.param_key).
39
- # permit(dashboard.permitted_attributes).
39
+ # permit(dashboard.permitted_attributes(action_name)).
40
40
  # transform_values { |value| value == "" ? nil : value }
41
41
  # end
42
42
 
43
- # See https://administrate-prototype.herokuapp.com/customizing_controller_actions
43
+ # See https://administrate-demo.herokuapp.com/customizing_controller_actions
44
44
  # for more information
45
45
  end
46
46
  end
@@ -14,7 +14,12 @@ module Administrate
14
14
  include Administrate::GeneratorHelpers
15
15
  source_root File.expand_path("../templates", __FILE__)
16
16
 
17
- class_option :namespace, type: :string, default: "admin"
17
+ class_option(
18
+ :namespace,
19
+ type: :string,
20
+ desc: "Namespace where the admin dashboards will live",
21
+ default: "admin",
22
+ )
18
23
 
19
24
  def run_routes_generator
20
25
  if dashboard_resources.none?
@@ -7,13 +7,19 @@ end
7
7
  require "rails/generators/base"
8
8
  require "administrate/generator_helpers"
9
9
  require "administrate/namespace"
10
+ require "generators/administrate/test_record"
10
11
 
11
12
  module Administrate
12
13
  module Generators
13
14
  class RoutesGenerator < Rails::Generators::Base
14
15
  include Administrate::GeneratorHelpers
15
16
  source_root File.expand_path("../templates", __FILE__)
16
- class_option :namespace, type: :string, default: "admin"
17
+ class_option(
18
+ :namespace,
19
+ type: :string,
20
+ desc: "Namespace where the admin dashboards live",
21
+ default: "admin",
22
+ )
17
23
 
18
24
  def insert_dashboard_routes
19
25
  if valid_dashboard_models.any?
@@ -55,7 +61,10 @@ module Administrate
55
61
  end
56
62
 
57
63
  def database_models
58
- ActiveRecord::Base.descendants.reject(&:abstract_class?)
64
+ ActiveRecord::Base.descendants.
65
+ reject(&:abstract_class?).
66
+ reject { |k| k < Administrate::Generators::TestRecord }.
67
+ sort_by(&:to_s)
59
68
  end
60
69
 
61
70
  def invalid_dashboard_models
@@ -0,0 +1,21 @@
1
+ module Administrate
2
+ module Generators
3
+ # This class serves only to work around a strange behaviour in Rails 7
4
+ # with Ruby 3.
5
+ #
6
+ # After running the spec for DashboardGenerator, the fake models that
7
+ # it generates (eg: Foo, Shipment) linger around despite being removed
8
+ # explicitly. This causes RouteGenerator to take them into
9
+ # account and generate routes for them, which its spec doesn't expect,
10
+ # causing a spec failure.
11
+ #
12
+ # To avoid this, the spec for DashboardGenerator defines its fake models
13
+ # as children of TestRecord. Then RoutesGenerator explicitly filters
14
+ # child classes of TestRecord when figuring out what models exist.
15
+ #
16
+ # Discussion at https://github.com/thoughtbot/administrate/pull/2324
17
+ class TestRecord < ApplicationRecord
18
+ self.abstract_class = true
19
+ end
20
+ end
21
+ end