administrate 0.8.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of administrate might be problematic. Click here for more details.

Files changed (100) hide show
  1. checksums.yaml +5 -5
  2. data/app/assets/javascripts/administrate/components/date_time_picker.js +5 -1
  3. data/app/assets/stylesheets/administrate/base/_typography.scss +1 -1
  4. data/app/assets/stylesheets/administrate/components/_cells.scss +2 -6
  5. data/app/assets/stylesheets/docs.scss +1 -0
  6. data/app/controllers/administrate/application_controller.rb +53 -13
  7. data/app/controllers/concerns/administrate/punditize.rb +36 -0
  8. data/app/helpers/administrate/application_helper.rb +26 -7
  9. data/app/views/administrate/application/_collection.html.erb +11 -10
  10. data/app/views/administrate/application/_flashes.html.erb +1 -1
  11. data/app/views/administrate/application/_form.html.erb +5 -2
  12. data/app/views/administrate/application/{_icons.erb → _icons.html.erb} +0 -0
  13. data/app/views/administrate/application/_navigation.html.erb +1 -1
  14. data/app/views/administrate/application/edit.html.erb +3 -3
  15. data/app/views/administrate/application/index.html.erb +7 -2
  16. data/app/views/administrate/application/new.html.erb +6 -1
  17. data/app/views/administrate/application/show.html.erb +4 -4
  18. data/app/views/fields/belongs_to/_index.html.erb +1 -1
  19. data/app/views/fields/belongs_to/_show.html.erb +1 -1
  20. data/app/views/fields/date_time/_form.html.erb +1 -1
  21. data/app/views/fields/date_time/_index.html.erb +1 -1
  22. data/app/views/fields/has_many/_form.html.erb +2 -2
  23. data/app/views/fields/has_many/_index.html.erb +1 -1
  24. data/app/views/fields/has_many/_show.html.erb +8 -5
  25. data/app/views/fields/has_one/_form.html.erb +1 -1
  26. data/app/views/fields/password/_form.html.erb +23 -0
  27. data/app/views/fields/password/_index.html.erb +18 -0
  28. data/app/views/fields/password/_show.html.erb +18 -0
  29. data/app/views/fields/polymorphic/_form.html.erb +11 -9
  30. data/app/views/fields/polymorphic/_show.html.erb +8 -4
  31. data/app/views/fields/select/_form.html.erb +1 -1
  32. data/app/views/fields/time/_form.html.erb +22 -0
  33. data/app/views/fields/time/_index.html.erb +17 -0
  34. data/app/views/fields/time/_show.html.erb +17 -0
  35. data/app/views/fields/url/_form.html.erb +23 -0
  36. data/app/views/fields/url/_index.html.erb +20 -0
  37. data/app/views/fields/url/_show.html.erb +20 -0
  38. data/app/views/layouts/administrate/application.html.erb +1 -1
  39. data/config/locales/administrate.al.yml +28 -0
  40. data/config/locales/administrate.ar.yml +6 -6
  41. data/config/locales/administrate.bs.yml +27 -0
  42. data/config/locales/administrate.ca.yml +28 -0
  43. data/config/locales/administrate.da.yml +6 -6
  44. data/config/locales/administrate.de.yml +8 -8
  45. data/config/locales/administrate.en.yml +6 -6
  46. data/config/locales/administrate.es.yml +6 -6
  47. data/config/locales/administrate.fr.yml +6 -6
  48. data/config/locales/administrate.id.yml +28 -0
  49. data/config/locales/administrate.it.yml +6 -6
  50. data/config/locales/administrate.ja.yml +6 -6
  51. data/config/locales/administrate.ko.yml +11 -11
  52. data/config/locales/administrate.nl.yml +6 -6
  53. data/config/locales/administrate.pl.yml +6 -6
  54. data/config/locales/administrate.pt-BR.yml +6 -6
  55. data/config/locales/administrate.pt.yml +6 -6
  56. data/config/locales/administrate.ru.yml +6 -6
  57. data/config/locales/administrate.sv.yml +6 -6
  58. data/config/locales/administrate.uk.yml +6 -6
  59. data/config/locales/administrate.vi.yml +6 -6
  60. data/config/locales/administrate.zh-CN.yml +6 -6
  61. data/config/locales/administrate.zh-TW.yml +8 -8
  62. data/docs/authorization.md +69 -0
  63. data/docs/customizing_attribute_partials.md +20 -1
  64. data/docs/customizing_controller_actions.md +2 -1
  65. data/docs/customizing_dashboards.md +152 -4
  66. data/docs/customizing_page_views.md +5 -1
  67. data/docs/getting_started.md +60 -10
  68. data/docs/rails_api.md +43 -0
  69. data/lib/administrate/base_dashboard.rb +27 -8
  70. data/lib/administrate/engine.rb +1 -1
  71. data/lib/administrate/field/associative.rb +8 -4
  72. data/lib/administrate/field/base.rb +1 -1
  73. data/lib/administrate/field/belongs_to.rb +6 -3
  74. data/lib/administrate/field/date_time.rb +13 -2
  75. data/lib/administrate/field/deferred.rb +10 -5
  76. data/lib/administrate/field/has_many.rb +18 -11
  77. data/lib/administrate/field/has_one.rb +16 -8
  78. data/lib/administrate/field/password.rb +25 -0
  79. data/lib/administrate/field/polymorphic.rb +41 -3
  80. data/lib/administrate/field/time.rb +8 -0
  81. data/lib/administrate/field/url.rb +21 -0
  82. data/lib/administrate/namespace.rb +1 -1
  83. data/lib/administrate/order.rb +40 -5
  84. data/lib/administrate/page/base.rb +8 -0
  85. data/lib/administrate/page/collection.rb +5 -1
  86. data/lib/administrate/resource_resolver.rb +2 -2
  87. data/lib/administrate/search.rb +121 -10
  88. data/lib/administrate/version.rb +1 -1
  89. data/lib/generators/administrate/dashboard/USAGE +1 -1
  90. data/lib/generators/administrate/dashboard/dashboard_generator.rb +11 -3
  91. data/lib/generators/administrate/dashboard/templates/controller.rb.erb +24 -11
  92. data/lib/generators/administrate/dashboard/templates/dashboard.rb.erb +18 -6
  93. data/lib/generators/administrate/install/install_generator.rb +13 -6
  94. data/lib/generators/administrate/install/templates/{application_controller.rb → application_controller.rb.erb} +1 -1
  95. data/lib/generators/administrate/routes/routes_generator.rb +11 -1
  96. data/lib/generators/administrate/routes/templates/routes.rb.erb +1 -1
  97. data/lib/generators/administrate/views/field_generator.rb +19 -5
  98. data/lib/generators/administrate/views/layout_generator.rb +1 -0
  99. metadata +41 -28
  100. data/config/secrets.yml +0 -14
@@ -9,6 +9,9 @@ require "administrate/field/polymorphic"
9
9
  require "administrate/field/select"
10
10
  require "administrate/field/string"
11
11
  require "administrate/field/text"
12
+ require "administrate/field/time"
13
+ require "administrate/field/url"
14
+ require "administrate/field/password"
12
15
 
13
16
  module Administrate
14
17
  class BaseDashboard
@@ -30,6 +33,10 @@ module Administrate
30
33
  end
31
34
  end
32
35
 
36
+ def all_attributes
37
+ attribute_types.keys
38
+ end
39
+
33
40
  def form_attributes
34
41
  self.class::FORM_ATTRIBUTES
35
42
  end
@@ -52,15 +59,12 @@ module Administrate
52
59
  "#{resource.class} ##{resource.id}"
53
60
  end
54
61
 
55
- def association_includes
56
- association_classes = [Field::HasMany, Field::HasOne, Field::BelongsTo]
57
-
58
- collection_attributes.map do |key|
59
- field = self.class::ATTRIBUTE_TYPES[key]
62
+ def collection_includes
63
+ attribute_includes(collection_attributes)
64
+ end
60
65
 
61
- next key if association_classes.include?(field)
62
- key if association_classes.include?(field.try :deferred_class)
63
- end.compact
66
+ def item_includes
67
+ attribute_includes(show_page_attributes)
64
68
  end
65
69
 
66
70
  private
@@ -68,5 +72,20 @@ module Administrate
68
72
  def attribute_not_found_message(attr)
69
73
  "Attribute #{attr} could not be found in #{self.class}::ATTRIBUTE_TYPES"
70
74
  end
75
+
76
+ def association_classes
77
+ @association_classes ||=
78
+ ObjectSpace.each_object(Class).
79
+ select { |klass| klass < Administrate::Field::Associative }
80
+ end
81
+
82
+ def attribute_includes(attributes)
83
+ attributes.map do |key|
84
+ field = self.class::ATTRIBUTE_TYPES[key]
85
+
86
+ next key if association_classes.include?(field)
87
+ key if association_classes.include?(field.try(:deferred_class))
88
+ end.compact
89
+ end
71
90
  end
72
91
  end
@@ -2,7 +2,7 @@ require "datetime_picker_rails"
2
2
  require "jquery-rails"
3
3
  require "kaminari"
4
4
  require "momentjs-rails"
5
- require "sass-rails"
5
+ require "sassc-rails"
6
6
  require "selectize-rails"
7
7
  require "sprockets/railtie"
8
8
 
@@ -7,16 +7,16 @@ module Administrate
7
7
  associated_dashboard.display_resource(data)
8
8
  end
9
9
 
10
+ def associated_class
11
+ associated_class_name.constantize
12
+ end
13
+
10
14
  protected
11
15
 
12
16
  def associated_dashboard
13
17
  "#{associated_class_name}Dashboard".constantize.new
14
18
  end
15
19
 
16
- def associated_class
17
- associated_class_name.constantize
18
- end
19
-
20
20
  def associated_class_name
21
21
  options.fetch(:class_name, attribute.to_s.singularize.camelcase)
22
22
  end
@@ -24,6 +24,10 @@ module Administrate
24
24
  def primary_key
25
25
  options.fetch(:primary_key, :id)
26
26
  end
27
+
28
+ def foreign_key
29
+ options.fetch(:foreign_key, :"#{attribute}_id")
30
+ end
27
31
  end
28
32
  end
29
33
  end
@@ -24,7 +24,7 @@ module Administrate
24
24
  @options = options
25
25
  end
26
26
 
27
- def self.permitted_attribute(attr)
27
+ def self.permitted_attribute(attr, _options = nil)
28
28
  attr
29
29
  end
30
30
 
@@ -3,12 +3,12 @@ require_relative "associative"
3
3
  module Administrate
4
4
  module Field
5
5
  class BelongsTo < Associative
6
- def self.permitted_attribute(attr)
6
+ def self.permitted_attribute(attr, _options = nil)
7
7
  :"#{attr}_id"
8
8
  end
9
9
 
10
10
  def permitted_attribute
11
- self.class.permitted_attribute(attribute)
11
+ foreign_key
12
12
  end
13
13
 
14
14
  def associated_resource_options
@@ -24,7 +24,10 @@ module Administrate
24
24
  private
25
25
 
26
26
  def candidate_resources
27
- associated_class.all
27
+ scope = options[:scope] ? options[:scope].call : associated_class.all
28
+
29
+ order = options.delete(:order)
30
+ order ? scope.reorder(order) : scope
28
31
  end
29
32
 
30
33
  def display_candidate_resource(resource)
@@ -4,11 +4,18 @@ module Administrate
4
4
  module Field
5
5
  class DateTime < Base
6
6
  def date
7
- I18n.localize(data.to_date, format: format)
7
+ I18n.localize(
8
+ data.in_time_zone(timezone).to_date,
9
+ format: format,
10
+ )
8
11
  end
9
12
 
10
13
  def datetime
11
- I18n.localize(data, format: format, default: data)
14
+ I18n.localize(
15
+ data.in_time_zone(timezone),
16
+ format: format,
17
+ default: data,
18
+ )
12
19
  end
13
20
 
14
21
  private
@@ -16,6 +23,10 @@ module Administrate
16
23
  def format
17
24
  options.fetch(:format, :default)
18
25
  end
26
+
27
+ def timezone
28
+ options.fetch(:timezone, ::Time.zone.name || "UTC")
29
+ end
19
30
  end
20
31
  end
21
32
  end
@@ -25,11 +25,16 @@ module Administrate
25
25
  options.fetch(:searchable, deferred_class.searchable?)
26
26
  end
27
27
 
28
- delegate(
29
- :html_class,
30
- :permitted_attribute,
31
- to: :deferred_class,
32
- )
28
+ def searchable_field
29
+ options.fetch(:searchable_field)
30
+ end
31
+
32
+ def permitted_attribute(attr, _options = nil)
33
+ options.fetch(:foreign_key,
34
+ deferred_class.permitted_attribute(attr, options))
35
+ end
36
+
37
+ delegate :html_class, to: :deferred_class
33
38
  end
34
39
  end
35
40
  end
@@ -7,12 +7,12 @@ module Administrate
7
7
  class HasMany < Associative
8
8
  DEFAULT_LIMIT = 5
9
9
 
10
- def self.permitted_attribute(attribute)
11
- { "#{attribute.to_s.singularize}_ids".to_sym => [] }
10
+ def self.permitted_attribute(attr, _options = nil)
11
+ { "#{attr.to_s.singularize}_ids".to_sym => [] }
12
12
  end
13
13
 
14
- def associated_collection
15
- Administrate::Page::Collection.new(associated_dashboard)
14
+ def associated_collection(order = self.order)
15
+ Administrate::Page::Collection.new(associated_dashboard, order: order)
16
16
  end
17
17
 
18
18
  def attribute_key
@@ -39,7 +39,7 @@ module Administrate
39
39
  self.class.permitted_attribute(attribute)
40
40
  end
41
41
 
42
- def resources(page = 1)
42
+ def resources(page = 1, order = self.order)
43
43
  resources = order.apply(data).page(page).per(limit)
44
44
  includes.any? ? resources.includes(*includes) : resources
45
45
  end
@@ -49,13 +49,24 @@ module Administrate
49
49
  end
50
50
 
51
51
  def data
52
- @data ||= associated_class.none
52
+ @data ||= associated_class.none
53
+ end
54
+
55
+ def order_from_params(params)
56
+ Administrate::Order.new(
57
+ params.fetch(:order, sort_by),
58
+ params.fetch(:direction, direction),
59
+ )
60
+ end
61
+
62
+ def order
63
+ @order ||= Administrate::Order.new(sort_by, direction)
53
64
  end
54
65
 
55
66
  private
56
67
 
57
68
  def includes
58
- associated_dashboard.association_includes
69
+ associated_dashboard.collection_includes
59
70
  end
60
71
 
61
72
  def candidate_resources
@@ -71,10 +82,6 @@ module Administrate
71
82
  associated_dashboard.display_resource(resource)
72
83
  end
73
84
 
74
- def order
75
- @_order ||= Administrate::Order.new(sort_by, direction)
76
- end
77
-
78
85
  def sort_by
79
86
  options[:sort_by]
80
87
  end
@@ -3,25 +3,33 @@ require_relative "associative"
3
3
  module Administrate
4
4
  module Field
5
5
  class HasOne < Associative
6
- def initialize(attribute, data, page, options = {})
7
- resolver = Administrate::ResourceResolver.new("admin/#{attribute}")
8
- @nested_form = Administrate::Page::Form.new(
6
+ def nested_form
7
+ @nested_form ||= Administrate::Page::Form.new(
9
8
  resolver.dashboard_class.new,
10
9
  data || resolver.resource_class.new,
11
10
  )
12
-
13
- super
14
11
  end
15
12
 
16
- def self.permitted_attribute(attr)
13
+ def self.permitted_attribute(attr, options = nil)
14
+ associated_class_name =
15
+ if options
16
+ options.fetch(:class_name, attr.to_s.singularize.camelcase)
17
+ else
18
+ attr
19
+ end
17
20
  related_dashboard_attributes =
18
- Administrate::ResourceResolver.new("admin/#{attr}").
21
+ Administrate::ResourceResolver.new("admin/#{associated_class_name}").
19
22
  dashboard_class.new.permitted_attributes + [:id]
20
23
 
21
24
  { "#{attr}_attributes": related_dashboard_attributes }
22
25
  end
23
26
 
24
- attr_reader :nested_form
27
+ private
28
+
29
+ def resolver
30
+ @resolver ||=
31
+ Administrate::ResourceResolver.new("admin/#{associated_class_name}")
32
+ end
25
33
  end
26
34
  end
27
35
  end
@@ -0,0 +1,25 @@
1
+ require_relative "base"
2
+
3
+ module Administrate
4
+ module Field
5
+ class Password < Field::Base
6
+ def self.searchable?
7
+ false
8
+ end
9
+
10
+ def truncate
11
+ data.to_s.gsub(/./, character)[0...truncation_length]
12
+ end
13
+
14
+ private
15
+
16
+ def truncation_length
17
+ options.fetch(:truncate, 50)
18
+ end
19
+
20
+ def character
21
+ options.fetch(:character, "•")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,11 +2,49 @@ require_relative "associative"
2
2
 
3
3
  module Administrate
4
4
  module Field
5
- class Polymorphic < Associative
5
+ class Polymorphic < BelongsTo
6
+ def associated_resource_grouped_options
7
+ classes.map do |klass|
8
+ [klass.to_s, candidate_resources_for(klass).map do |resource|
9
+ [display_candidate_resource(resource), resource.to_global_id]
10
+ end]
11
+ end
12
+ end
13
+
14
+ def self.permitted_attribute(attr, _options = nil)
15
+ { attr => %i{type value} }
16
+ end
17
+
18
+ def permitted_attribute
19
+ { attribute => %i{type value} }
20
+ end
21
+
22
+ def selected_global_id
23
+ data ? data.to_global_id : nil
24
+ end
25
+
6
26
  protected
7
27
 
8
- def associated_dashboard
9
- "#{data.class.name}Dashboard".constantize.new
28
+ def associated_dashboard(klass = data.class)
29
+ "#{klass.name}Dashboard".constantize.new
30
+ end
31
+
32
+ def classes
33
+ options.fetch(:classes, [])
34
+ end
35
+
36
+ private
37
+
38
+ def order
39
+ @_order ||= options.delete(:order)
40
+ end
41
+
42
+ def candidate_resources_for(klass)
43
+ order ? klass.order(order) : klass.all
44
+ end
45
+
46
+ def display_candidate_resource(resource)
47
+ associated_dashboard(resource.class).display_resource(resource)
10
48
  end
11
49
  end
12
50
  end
@@ -0,0 +1,8 @@
1
+ require_relative "base"
2
+
3
+ module Administrate
4
+ module Field
5
+ class Time < Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "base"
2
+
3
+ module Administrate
4
+ module Field
5
+ class Url < Field::Base
6
+ def self.searchable?
7
+ true
8
+ end
9
+
10
+ def truncate
11
+ data.to_s[0...truncation_length]
12
+ end
13
+
14
+ private
15
+
16
+ def truncation_length
17
+ options.fetch(:truncate, 50)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -12,7 +12,7 @@ module Administrate
12
12
 
13
13
  def routes
14
14
  @routes ||= all_routes.select do |controller, _action|
15
- controller.starts_with?(namespace.to_s)
15
+ controller.starts_with?("#{namespace}/")
16
16
  end.map do |controller, action|
17
17
  [controller.gsub(/^#{namespace}\//, ""), action]
18
18
  end
@@ -6,11 +6,15 @@ module Administrate
6
6
  end
7
7
 
8
8
  def apply(relation)
9
- if relation.columns_hash.keys.include?(attribute.to_s)
10
- relation.order("#{attribute} #{direction}")
11
- else
12
- relation
13
- end
9
+ return order_by_association(relation) unless
10
+ reflect_association(relation).nil?
11
+
12
+ order = "#{relation.table_name}.#{attribute} #{direction}"
13
+
14
+ return relation.reorder(order) if
15
+ relation.columns_hash.keys.include?(attribute.to_s)
16
+
17
+ relation
14
18
  end
15
19
 
16
20
  def ordered_by?(attr)
@@ -41,5 +45,36 @@ module Administrate
41
45
  def opposite_direction
42
46
  direction.to_sym == :asc ? :desc : :asc
43
47
  end
48
+
49
+ def order_by_association(relation)
50
+ return order_by_count(relation) if has_many_attribute?(relation)
51
+
52
+ return order_by_id(relation) if belongs_to_attribute?(relation)
53
+
54
+ relation
55
+ end
56
+
57
+ def order_by_count(relation)
58
+ relation.
59
+ left_joins(attribute.to_sym).
60
+ group(:id).
61
+ reorder("COUNT(#{attribute}.id) #{direction}")
62
+ end
63
+
64
+ def order_by_id(relation)
65
+ relation.reorder("#{attribute}_id #{direction}")
66
+ end
67
+
68
+ def has_many_attribute?(relation)
69
+ reflect_association(relation).macro == :has_many
70
+ end
71
+
72
+ def belongs_to_attribute?(relation)
73
+ reflect_association(relation).macro == :belongs_to
74
+ end
75
+
76
+ def reflect_association(relation)
77
+ relation.klass.reflect_on_association(attribute.to_s)
78
+ end
44
79
  end
45
80
  end
@@ -15,6 +15,14 @@ module Administrate
15
15
  @resource_path ||= resource_name.gsub("/", "_")
16
16
  end
17
17
 
18
+ def collection_includes
19
+ dashboard.try(:collection_includes) || []
20
+ end
21
+
22
+ def item_includes
23
+ dashboard.try(:item_includes) || []
24
+ end
25
+
18
26
  protected
19
27
 
20
28
  def attribute_field(dashboard, resource, attribute_name, page)
@@ -21,7 +21,11 @@ module Administrate
21
21
  ordered_by?(attr) && order.direction
22
22
  end
23
23
 
24
- delegate :ordered_by?, :order_params_for, to: :order
24
+ delegate :ordered_by?, to: :order
25
+
26
+ def order_params_for(attr, key: resource_name)
27
+ { key => order.order_params_for(attr) }
28
+ end
25
29
 
26
30
  private
27
31
 
@@ -5,7 +5,7 @@ module Administrate
5
5
  end
6
6
 
7
7
  def dashboard_class
8
- Object.const_get(resource_class_name + "Dashboard")
8
+ ActiveSupport::Inflector.constantize("#{resource_class_name}Dashboard")
9
9
  end
10
10
 
11
11
  def namespace
@@ -13,7 +13,7 @@ module Administrate
13
13
  end
14
14
 
15
15
  def resource_class
16
- Object.const_get(resource_class_name)
16
+ ActiveSupport::Inflector.constantize(resource_class_name)
17
17
  end
18
18
 
19
19
  def resource_name
@@ -3,32 +3,91 @@ require "active_support/core_ext/object/blank"
3
3
 
4
4
  module Administrate
5
5
  class Search
6
+ class Query
7
+ attr_reader :filters
8
+
9
+ def blank?
10
+ terms.blank? && filters.empty?
11
+ end
12
+
13
+ def initialize(original_query)
14
+ @original_query = original_query
15
+ @filters, @terms = parse_query(original_query)
16
+ end
17
+
18
+ def original
19
+ @original_query
20
+ end
21
+
22
+ def terms
23
+ @terms.join(" ")
24
+ end
25
+
26
+ def to_s
27
+ original
28
+ end
29
+
30
+ private
31
+
32
+ def filter?(word)
33
+ word.match?(/^\w+:$/)
34
+ end
35
+
36
+ def parse_query(query)
37
+ filters = []
38
+ terms = []
39
+ query.to_s.split.each do |word|
40
+ if filter?(word)
41
+ filters << word.split(":").first
42
+ else
43
+ terms << word
44
+ end
45
+ end
46
+ [filters, terms]
47
+ end
48
+ end
49
+
6
50
  def initialize(scoped_resource, dashboard_class, term)
7
51
  @dashboard_class = dashboard_class
8
52
  @scoped_resource = scoped_resource
9
- @term = term
53
+ @query = Query.new(term)
10
54
  end
11
55
 
12
56
  def run
13
- if @term.blank?
57
+ if query.blank?
14
58
  @scoped_resource.all
15
59
  else
16
- @scoped_resource.where(query, *search_terms)
60
+ results = search_results(@scoped_resource)
61
+ results = filter_results(results)
62
+ results
17
63
  end
18
64
  end
19
65
 
20
66
  private
21
67
 
22
- def query
68
+ def apply_filter(filter, resources)
69
+ return resources unless filter
70
+ filter.call(resources)
71
+ end
72
+
73
+ def filter_results(resources)
74
+ query.filters.each do |filter_name|
75
+ filter = valid_filters[filter_name]
76
+ resources = apply_filter(filter, resources)
77
+ end
78
+ resources
79
+ end
80
+
81
+ def query_template
23
82
  search_attributes.map do |attr|
24
- table_name = ActiveRecord::Base.connection.
25
- quote_table_name(@scoped_resource.table_name)
26
- attr_name = ActiveRecord::Base.connection.quote_column_name(attr)
27
- "lower(#{table_name}.#{attr_name}) LIKE ?"
83
+ table_name = query_table_name(attr)
84
+ attr_name = column_to_query(attr)
85
+
86
+ "LOWER(CAST(#{table_name}.#{attr_name} AS CHAR(256))) LIKE ?"
28
87
  end.join(" OR ")
29
88
  end
30
89
 
31
- def search_terms
90
+ def query_values
32
91
  ["%#{term.mb_chars.downcase}%"] * search_attributes.count
33
92
  end
34
93
 
@@ -38,10 +97,62 @@ module Administrate
38
97
  end
39
98
  end
40
99
 
100
+ def search_results(resources)
101
+ resources.
102
+ joins(tables_to_join).
103
+ where(query_template, *query_values)
104
+ end
105
+
106
+ def valid_filters
107
+ if @dashboard_class.const_defined?(:COLLECTION_FILTERS)
108
+ @dashboard_class.const_get(:COLLECTION_FILTERS).stringify_keys
109
+ else
110
+ {}
111
+ end
112
+ end
113
+
41
114
  def attribute_types
42
115
  @dashboard_class::ATTRIBUTE_TYPES
43
116
  end
44
117
 
45
- attr_reader :resolver, :term
118
+ def query_table_name(attr)
119
+ if association_search?(attr)
120
+ ActiveRecord::Base.connection.quote_table_name(attr.to_s.pluralize)
121
+ else
122
+ ActiveRecord::Base.connection.
123
+ quote_table_name(@scoped_resource.table_name)
124
+ end
125
+ end
126
+
127
+ def column_to_query(attr)
128
+ if association_search?(attr)
129
+ ActiveRecord::Base.connection.
130
+ quote_column_name(attribute_types[attr].searchable_field)
131
+ else
132
+ ActiveRecord::Base.connection.quote_column_name(attr)
133
+ end
134
+ end
135
+
136
+ def tables_to_join
137
+ attribute_types.keys.select do |attribute|
138
+ attribute_types[attribute].searchable? && association_search?(attribute)
139
+ end
140
+ end
141
+
142
+ def association_search?(attribute)
143
+ return unless attribute_types[attribute].respond_to?(:deferred_class)
144
+
145
+ [
146
+ Administrate::Field::BelongsTo,
147
+ Administrate::Field::HasMany,
148
+ Administrate::Field::HasOne,
149
+ ].include?(attribute_types[attribute].deferred_class)
150
+ end
151
+
152
+ def term
153
+ query.terms
154
+ end
155
+
156
+ attr_reader :resolver, :query
46
157
  end
47
158
  end