administrate 0.8.1 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +5 -5
  2. data/app/assets/javascripts/administrate/components/associative.js +5 -0
  3. data/app/assets/javascripts/administrate/components/date_time_picker.js +10 -2
  4. data/app/assets/javascripts/administrate/components/table.js +1 -1
  5. data/app/assets/stylesheets/administrate/base/_tables.scss +3 -0
  6. data/app/assets/stylesheets/administrate/base/_typography.scss +1 -1
  7. data/app/assets/stylesheets/administrate/components/_attributes.scss +4 -3
  8. data/app/assets/stylesheets/administrate/components/_buttons.scss +11 -0
  9. data/app/assets/stylesheets/administrate/components/_cells.scss +3 -5
  10. data/app/assets/stylesheets/administrate/components/_field-unit.scss +17 -4
  11. data/app/assets/stylesheets/administrate/components/_flashes.scss +0 -8
  12. data/app/assets/stylesheets/administrate/components/_main-content.scss +1 -0
  13. data/app/assets/stylesheets/administrate/components/_navigation.scss +2 -3
  14. data/app/assets/stylesheets/administrate/library/_variables.scss +10 -8
  15. data/app/assets/stylesheets/docs.scss +1 -0
  16. data/app/controllers/administrate/application_controller.rb +95 -21
  17. data/app/controllers/concerns/administrate/punditize.rb +36 -0
  18. data/app/helpers/administrate/application_helper.rb +51 -14
  19. data/app/views/administrate/application/_collection.html.erb +32 -33
  20. data/app/views/administrate/application/_collection_header_actions.html.erb +4 -0
  21. data/app/views/administrate/application/_collection_item_actions.html.erb +17 -0
  22. data/app/views/administrate/application/_flashes.html.erb +2 -1
  23. data/app/views/administrate/application/_form.html.erb +7 -4
  24. data/app/views/administrate/application/{_icons.erb → _icons.html.erb} +1 -1
  25. data/app/views/administrate/application/_index_header.html.erb +28 -0
  26. data/app/views/administrate/application/_navigation.html.erb +5 -3
  27. data/app/views/administrate/application/edit.html.erb +3 -3
  28. data/app/views/administrate/application/index.html.erb +11 -26
  29. data/app/views/administrate/application/new.html.erb +6 -1
  30. data/app/views/administrate/application/show.html.erb +5 -5
  31. data/app/views/fields/belongs_to/_form.html.erb +3 -3
  32. data/app/views/fields/belongs_to/_index.html.erb +1 -1
  33. data/app/views/fields/belongs_to/_show.html.erb +1 -1
  34. data/app/views/fields/date/_form.html.erb +24 -0
  35. data/app/views/fields/date/_index.html.erb +21 -0
  36. data/app/views/fields/date/_show.html.erb +21 -0
  37. data/app/views/fields/date_time/_form.html.erb +1 -1
  38. data/app/views/fields/date_time/_index.html.erb +1 -1
  39. data/app/views/fields/has_many/_form.html.erb +2 -2
  40. data/app/views/fields/has_many/_index.html.erb +1 -1
  41. data/app/views/fields/has_many/_show.html.erb +8 -5
  42. data/app/views/fields/has_one/_form.html.erb +1 -1
  43. data/app/views/fields/has_one/_index.html.erb +1 -1
  44. data/app/views/fields/has_one/_show.html.erb +4 -4
  45. data/app/views/fields/number/_form.html.erb +1 -1
  46. data/app/views/fields/password/_form.html.erb +23 -0
  47. data/app/views/fields/password/_index.html.erb +18 -0
  48. data/app/views/fields/password/_show.html.erb +18 -0
  49. data/app/views/fields/polymorphic/_form.html.erb +11 -9
  50. data/app/views/fields/polymorphic/_show.html.erb +8 -4
  51. data/app/views/fields/select/_form.html.erb +24 -10
  52. data/app/views/fields/string/_show.html.erb +2 -2
  53. data/app/views/fields/text/_show.html.erb +2 -3
  54. data/app/views/fields/time/_form.html.erb +23 -0
  55. data/app/views/fields/time/_index.html.erb +19 -0
  56. data/app/views/fields/time/_show.html.erb +19 -0
  57. data/app/views/fields/url/_form.html.erb +23 -0
  58. data/app/views/fields/url/_index.html.erb +20 -0
  59. data/app/views/fields/url/_show.html.erb +20 -0
  60. data/app/views/layouts/administrate/application.html.erb +2 -1
  61. data/config/locales/administrate.ar.yml +8 -6
  62. data/config/locales/administrate.bs.yml +29 -0
  63. data/config/locales/administrate.ca.yml +30 -0
  64. data/config/locales/administrate.da.yml +8 -6
  65. data/config/locales/administrate.de.yml +10 -8
  66. data/config/locales/administrate.en.yml +8 -6
  67. data/config/locales/administrate.es.yml +8 -6
  68. data/config/locales/administrate.fi.yml +30 -0
  69. data/config/locales/administrate.fr.yml +9 -7
  70. data/config/locales/administrate.id.yml +30 -0
  71. data/config/locales/administrate.it.yml +8 -6
  72. data/config/locales/administrate.ja.yml +8 -6
  73. data/config/locales/administrate.ko.yml +13 -11
  74. data/config/locales/administrate.nl.yml +12 -10
  75. data/config/locales/administrate.pl.yml +8 -6
  76. data/config/locales/administrate.pt-BR.yml +8 -6
  77. data/config/locales/administrate.pt.yml +8 -6
  78. data/config/locales/administrate.ru.yml +8 -6
  79. data/config/locales/administrate.sq.yml +30 -0
  80. data/config/locales/administrate.sv.yml +8 -6
  81. data/config/locales/administrate.tr.yml +30 -0
  82. data/config/locales/administrate.uk.yml +8 -6
  83. data/config/locales/administrate.vi.yml +8 -6
  84. data/config/locales/administrate.zh-CN.yml +8 -6
  85. data/config/locales/administrate.zh-TW.yml +10 -8
  86. data/config/unicorn.rb +8 -13
  87. data/docs/adding_controllers_without_related_model.md +52 -0
  88. data/docs/adding_custom_field_types.md +3 -1
  89. data/docs/authentication.md +3 -1
  90. data/docs/authorization.md +71 -0
  91. data/docs/customizing_attribute_partials.md +24 -2
  92. data/docs/customizing_controller_actions.md +50 -2
  93. data/docs/customizing_dashboards.md +210 -9
  94. data/docs/customizing_page_views.md +23 -5
  95. data/docs/extending_administrate.md +27 -0
  96. data/docs/getting_started.md +89 -15
  97. data/docs/guides/hiding_dashboards_from_sidebar.md +19 -0
  98. data/docs/guides.md +5 -0
  99. data/docs/rails_api.md +45 -0
  100. data/lib/administrate/base_dashboard.rb +53 -11
  101. data/lib/administrate/custom_dashboard.rb +15 -0
  102. data/lib/administrate/engine.rb +8 -1
  103. data/lib/administrate/field/associative.rb +55 -7
  104. data/lib/administrate/field/base.rb +35 -9
  105. data/lib/administrate/field/belongs_to.rb +18 -5
  106. data/lib/administrate/field/date.rb +20 -0
  107. data/lib/administrate/field/date_time.rb +13 -2
  108. data/lib/administrate/field/deferred.rb +29 -5
  109. data/lib/administrate/field/has_many.rb +32 -12
  110. data/lib/administrate/field/has_one.rb +39 -11
  111. data/lib/administrate/field/number.rb +19 -2
  112. data/lib/administrate/field/password.rb +25 -0
  113. data/lib/administrate/field/polymorphic.rb +42 -4
  114. data/lib/administrate/field/select.rb +10 -1
  115. data/lib/administrate/field/time.rb +19 -0
  116. data/lib/administrate/field/url.rb +21 -0
  117. data/lib/administrate/namespace.rb +6 -2
  118. data/lib/administrate/order.rb +52 -7
  119. data/lib/administrate/page/base.rb +9 -3
  120. data/lib/administrate/page/collection.rb +5 -1
  121. data/lib/administrate/page/form.rb +10 -3
  122. data/lib/administrate/resource_resolver.rb +4 -4
  123. data/lib/administrate/search.rb +138 -16
  124. data/lib/administrate/version.rb +1 -1
  125. data/lib/administrate/view_generator.rb +9 -3
  126. data/lib/administrate.rb +19 -0
  127. data/lib/generators/administrate/dashboard/USAGE +1 -1
  128. data/lib/generators/administrate/dashboard/dashboard_generator.rb +29 -17
  129. data/lib/generators/administrate/dashboard/templates/controller.rb.erb +35 -10
  130. data/lib/generators/administrate/dashboard/templates/dashboard.rb.erb +18 -6
  131. data/lib/generators/administrate/install/install_generator.rb +50 -7
  132. data/lib/generators/administrate/install/templates/{application_controller.rb → application_controller.rb.erb} +4 -4
  133. data/lib/generators/administrate/routes/routes_generator.rb +26 -26
  134. data/lib/generators/administrate/routes/templates/routes.rb.erb +1 -1
  135. data/lib/generators/administrate/views/field_generator.rb +19 -5
  136. data/lib/generators/administrate/views/layout_generator.rb +1 -0
  137. data/lib/generators/administrate/views/views_generator.rb +5 -4
  138. metadata +52 -53
  139. data/app/assets/javascripts/administrate/components/has_many_form.js +0 -3
  140. data/config/secrets.yml +0 -14
@@ -1,6 +1,7 @@
1
1
  require "administrate/field/belongs_to"
2
2
  require "administrate/field/boolean"
3
3
  require "administrate/field/date_time"
4
+ require "administrate/field/date"
4
5
  require "administrate/field/email"
5
6
  require "administrate/field/has_many"
6
7
  require "administrate/field/has_one"
@@ -9,11 +10,26 @@ require "administrate/field/polymorphic"
9
10
  require "administrate/field/select"
10
11
  require "administrate/field/string"
11
12
  require "administrate/field/text"
13
+ require "administrate/field/time"
14
+ require "administrate/field/url"
15
+ require "administrate/field/password"
12
16
 
13
17
  module Administrate
14
18
  class BaseDashboard
15
19
  include Administrate
16
20
 
21
+ DASHBOARD_SUFFIX = "Dashboard".freeze
22
+
23
+ class << self
24
+ def model
25
+ to_s.chomp(DASHBOARD_SUFFIX).classify.constantize
26
+ end
27
+
28
+ def resource_name(opts)
29
+ model.model_name.human(opts)
30
+ end
31
+ end
32
+
17
33
  def attribute_types
18
34
  self.class::ATTRIBUTE_TYPES
19
35
  end
@@ -30,13 +46,28 @@ module Administrate
30
46
  end
31
47
  end
32
48
 
33
- def form_attributes
34
- self.class::FORM_ATTRIBUTES
49
+ def all_attributes
50
+ attribute_types.keys
51
+ end
52
+
53
+ def form_attributes(action = nil)
54
+ specific_form_attributes_for(action) || self.class::FORM_ATTRIBUTES
55
+ end
56
+
57
+ def specific_form_attributes_for(action)
58
+ return unless action
59
+
60
+ cname = "FORM_ATTRIBUTES_#{action.upcase}"
61
+
62
+ self.class.const_get(cname) if self.class.const_defined?(cname)
35
63
  end
36
64
 
37
65
  def permitted_attributes
38
66
  form_attributes.map do |attr|
39
- attribute_types[attr].permitted_attribute(attr)
67
+ attribute_types[attr].permitted_attribute(
68
+ attr,
69
+ resource_class: self.class.model,
70
+ )
40
71
  end.uniq
41
72
  end
42
73
 
@@ -48,19 +79,22 @@ module Administrate
48
79
  self.class::COLLECTION_ATTRIBUTES
49
80
  end
50
81
 
82
+ def search_attributes
83
+ attribute_types.keys.select do |attribute|
84
+ attribute_types[attribute].searchable?
85
+ end
86
+ end
87
+
51
88
  def display_resource(resource)
52
89
  "#{resource.class} ##{resource.id}"
53
90
  end
54
91
 
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]
92
+ def collection_includes
93
+ attribute_includes(collection_attributes)
94
+ end
60
95
 
61
- next key if association_classes.include?(field)
62
- key if association_classes.include?(field.try :deferred_class)
63
- end.compact
96
+ def item_includes
97
+ attribute_includes(show_page_attributes)
64
98
  end
65
99
 
66
100
  private
@@ -68,5 +102,13 @@ module Administrate
68
102
  def attribute_not_found_message(attr)
69
103
  "Attribute #{attr} could not be found in #{self.class}::ATTRIBUTE_TYPES"
70
104
  end
105
+
106
+ def attribute_includes(attributes)
107
+ attributes.map do |key|
108
+ field = attribute_type_for(key)
109
+
110
+ key if field.associative?
111
+ end.compact
112
+ end
71
113
  end
72
114
  end
@@ -0,0 +1,15 @@
1
+ module Administrate
2
+ class CustomDashboard
3
+ include Administrate
4
+
5
+ class << self
6
+ def resource_name(_opts)
7
+ named_resource.pluralize.titleize
8
+ end
9
+
10
+ def resource(resource_name)
11
+ define_singleton_method(:named_resource) { resource_name }
12
+ end
13
+ end
14
+ end
15
+ 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
 
@@ -22,6 +22,13 @@ module Administrate
22
22
  @@javascripts = []
23
23
  @@stylesheets = []
24
24
 
25
+ initializer "administrate.assets.precompile" do |app|
26
+ app.config.assets.precompile += [
27
+ "administrate/application.js",
28
+ "administrate/application.css",
29
+ ]
30
+ end
31
+
25
32
  def self.add_javascript(script)
26
33
  @@javascripts << script
27
34
  end
@@ -3,26 +3,74 @@ require_relative "base"
3
3
  module Administrate
4
4
  module Field
5
5
  class Associative < Base
6
+ def self.foreign_key_for(resource_class, attr)
7
+ reflection(resource_class, attr).foreign_key
8
+ end
9
+
10
+ def self.associated_class(resource_class, attr)
11
+ reflection(resource_class, attr).klass
12
+ end
13
+
14
+ def self.associated_class_name(resource_class, attr)
15
+ reflection(resource_class, attr).class_name
16
+ end
17
+
18
+ def self.reflection(resource_class, attr)
19
+ resource_class.reflect_on_association(attr)
20
+ end
21
+
6
22
  def display_associated_resource
7
23
  associated_dashboard.display_resource(data)
8
24
  end
9
25
 
10
- protected
26
+ def associated_class
27
+ if option_given?(:class_name)
28
+ associated_class_name.constantize
29
+ else
30
+ self.class.associated_class(resource.class, attribute)
31
+ end
32
+ end
33
+
34
+ private
11
35
 
12
36
  def associated_dashboard
13
37
  "#{associated_class_name}Dashboard".constantize.new
14
38
  end
15
39
 
16
- def associated_class
17
- associated_class_name.constantize
18
- end
19
-
20
40
  def associated_class_name
21
- options.fetch(:class_name, attribute.to_s.singularize.camelcase)
41
+ if option_given?(:class_name)
42
+ deprecated_option(:class_name)
43
+ else
44
+ self.class.associated_class_name(
45
+ resource.class,
46
+ attribute,
47
+ )
48
+ end
22
49
  end
23
50
 
24
51
  def primary_key
25
- options.fetch(:primary_key, :id)
52
+ if option_given?(:primary_key)
53
+ deprecated_option(:primary_key)
54
+ else
55
+ :id
56
+ end
57
+ end
58
+
59
+ def foreign_key
60
+ if option_given?(:foreign_key)
61
+ deprecated_option(:foreign_key)
62
+ else
63
+ self.class.foreign_key_for(resource.class, attribute)
64
+ end
65
+ end
66
+
67
+ def option_given?(name)
68
+ options.key?(name)
69
+ end
70
+
71
+ def deprecated_option(name)
72
+ Administrate.warn_of_deprecated_option(name)
73
+ options.fetch(name)
26
74
  end
27
75
  end
28
76
  end
@@ -12,10 +12,22 @@ module Administrate
12
12
  field_type.dasherize
13
13
  end
14
14
 
15
+ def self.associative?
16
+ self < Associative
17
+ end
18
+
15
19
  def self.searchable?
16
20
  false
17
21
  end
18
22
 
23
+ def self.field_type
24
+ to_s.split("::").last.underscore
25
+ end
26
+
27
+ def self.permitted_attribute(attr, _options = nil)
28
+ attr
29
+ end
30
+
19
31
  def initialize(attribute, data, page, options = {})
20
32
  @attribute = attribute
21
33
  @data = data
@@ -24,10 +36,6 @@ module Administrate
24
36
  @options = options
25
37
  end
26
38
 
27
- def self.permitted_attribute(attr)
28
- attr
29
- end
30
-
31
39
  def html_class
32
40
  self.class.html_class
33
41
  end
@@ -40,15 +48,33 @@ module Administrate
40
48
  "/fields/#{self.class.field_type}/#{page}"
41
49
  end
42
50
 
43
- attr_reader :attribute, :data, :page, :resource
51
+ def required?
52
+ return false unless resource.class.respond_to?(:validators_on)
44
53
 
45
- protected
54
+ resource.class.validators_on(attribute).any? do |v|
55
+ next false unless v.class == ActiveRecord::Validations::PresenceValidator
46
56
 
47
- attr_reader :options
57
+ options = v.options
58
+ next false if options.include?(:if)
59
+ next false if options.include?(:unless)
48
60
 
49
- def self.field_type
50
- to_s.split("::").last.underscore
61
+ if on_option = options[:on]
62
+ if on_option == :create && !resource.persisted?
63
+ next true
64
+ end
65
+
66
+ if on_option == :update && resource.persisted?
67
+ next true
68
+ end
69
+
70
+ next false
71
+ end
72
+
73
+ true
74
+ end
51
75
  end
76
+
77
+ attr_reader :attribute, :data, :options, :page, :resource
52
78
  end
53
79
  end
54
80
  end
@@ -3,16 +3,22 @@ require_relative "associative"
3
3
  module Administrate
4
4
  module Field
5
5
  class BelongsTo < Associative
6
- def self.permitted_attribute(attr)
7
- :"#{attr}_id"
6
+ def self.permitted_attribute(attr, options = {})
7
+ resource_class = options[:resource_class]
8
+ if resource_class
9
+ foreign_key_for(resource_class, attr)
10
+ else
11
+ Administrate.warn_of_missing_resource_class
12
+ :"#{attr}_id"
13
+ end
8
14
  end
9
15
 
10
16
  def permitted_attribute
11
- self.class.permitted_attribute(attribute)
17
+ foreign_key
12
18
  end
13
19
 
14
20
  def associated_resource_options
15
- [nil] + candidate_resources.map do |resource|
21
+ candidate_resources.map do |resource|
16
22
  [display_candidate_resource(resource), resource.send(primary_key)]
17
23
  end
18
24
  end
@@ -21,10 +27,17 @@ module Administrate
21
27
  data && data.send(primary_key)
22
28
  end
23
29
 
30
+ def include_blank_option
31
+ options.fetch(:include_blank, true)
32
+ end
33
+
24
34
  private
25
35
 
26
36
  def candidate_resources
27
- associated_class.all
37
+ scope = options[:scope] ? options[:scope].call : associated_class.all
38
+
39
+ order = options.delete(:order)
40
+ order ? scope.reorder(order) : scope
28
41
  end
29
42
 
30
43
  def display_candidate_resource(resource)
@@ -0,0 +1,20 @@
1
+ require_relative "base"
2
+
3
+ module Administrate
4
+ module Field
5
+ class Date < Base
6
+ def date
7
+ I18n.localize(
8
+ data.to_date,
9
+ format: format,
10
+ )
11
+ end
12
+
13
+ private
14
+
15
+ def format
16
+ options.fetch(:format, :default)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -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
@@ -21,15 +21,39 @@ module Administrate
21
21
  options == other.options
22
22
  end
23
23
 
24
+ def associative?
25
+ deferred_class.associative?
26
+ end
27
+
24
28
  def searchable?
25
29
  options.fetch(:searchable, deferred_class.searchable?)
26
30
  end
27
31
 
28
- delegate(
29
- :html_class,
30
- :permitted_attribute,
31
- to: :deferred_class,
32
- )
32
+ def searchable_field
33
+ ActiveSupport::Deprecation.warn(
34
+ "searchable_field is deprecated, use searchable_fields instead",
35
+ )
36
+ options.fetch(:searchable_field)
37
+ end
38
+
39
+ def searchable_fields
40
+ if options.key?(:searchable_field)
41
+ [searchable_field]
42
+ else
43
+ options.fetch(:searchable_fields)
44
+ end
45
+ end
46
+
47
+ def permitted_attribute(attr, opts = {})
48
+ if options.key?(:foreign_key)
49
+ Administrate.warn_of_deprecated_option(:foreign_key)
50
+ options.fetch(:foreign_key)
51
+ else
52
+ deferred_class.permitted_attribute(attr, options.merge(opts))
53
+ end
54
+ end
55
+
56
+ delegate :html_class, to: :deferred_class
33
57
  end
34
58
  end
35
59
  end
@@ -7,12 +7,22 @@ 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 = {})
11
+ # This may seem arbitrary, and improvable by using reflection.
12
+ # Worry not: here we do exactly what Rails does. Regardless of the name
13
+ # of the foreign key, has_many associations use the suffix `_ids`
14
+ # for this.
15
+ #
16
+ # Eg: if the associated table and primary key are `countries.code`,
17
+ # you may expect `country_codes` as attribute here, but it will
18
+ # be `country_ids` instead.
19
+ #
20
+ # See https://github.com/rails/rails/blob/b30a23f53b52e59d31358f7b80385ee5c2ba3afe/activerecord/lib/active_record/associations/builder/collection_association.rb#L48
21
+ { "#{attr.to_s.singularize}_ids".to_sym => [] }
12
22
  end
13
23
 
14
- def associated_collection
15
- Administrate::Page::Collection.new(associated_dashboard)
24
+ def associated_collection(order = self.order)
25
+ Administrate::Page::Collection.new(associated_dashboard, order: order)
16
26
  end
17
27
 
18
28
  def attribute_key
@@ -36,10 +46,13 @@ module Administrate
36
46
  end
37
47
 
38
48
  def permitted_attribute
39
- self.class.permitted_attribute(attribute)
49
+ self.class.permitted_attribute(
50
+ attribute,
51
+ resource_class: resource.class,
52
+ )
40
53
  end
41
54
 
42
- def resources(page = 1)
55
+ def resources(page = 1, order = self.order)
43
56
  resources = order.apply(data).page(page).per(limit)
44
57
  includes.any? ? resources.includes(*includes) : resources
45
58
  end
@@ -49,13 +62,24 @@ module Administrate
49
62
  end
50
63
 
51
64
  def data
52
- @data ||= associated_class.none
65
+ @data ||= associated_class.none
66
+ end
67
+
68
+ def order_from_params(params)
69
+ Administrate::Order.new(
70
+ params.fetch(:order, sort_by),
71
+ params.fetch(:direction, direction),
72
+ )
73
+ end
74
+
75
+ def order
76
+ @order ||= Administrate::Order.new(sort_by, direction)
53
77
  end
54
78
 
55
79
  private
56
80
 
57
81
  def includes
58
- associated_dashboard.association_includes
82
+ associated_dashboard.collection_includes
59
83
  end
60
84
 
61
85
  def candidate_resources
@@ -71,10 +95,6 @@ module Administrate
71
95
  associated_dashboard.display_resource(resource)
72
96
  end
73
97
 
74
- def order
75
- @_order ||= Administrate::Order.new(sort_by, direction)
76
- end
77
-
78
98
  def sort_by
79
99
  options[:sort_by]
80
100
  end
@@ -3,25 +3,53 @@ 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 self.permitted_attribute(attr, options = {})
7
+ resource_class = options[:resource_class]
8
+ final_associated_class_name =
9
+ if options.key?(:class_name)
10
+ Administrate.warn_of_deprecated_option(:class_name)
11
+ options.fetch(:class_name)
12
+ elsif resource_class
13
+ associated_class_name(resource_class, attr)
14
+ else
15
+ Administrate.warn_of_missing_resource_class
16
+ if options
17
+ attr.to_s.singularize.camelcase
18
+ else
19
+ attr
20
+ end
21
+ end
22
+ related_dashboard_attributes =
23
+ Administrate::ResourceResolver.
24
+ new("admin/#{final_associated_class_name}").
25
+ dashboard_class.new.permitted_attributes + [:id]
26
+ { "#{attr}_attributes": related_dashboard_attributes }
27
+ end
28
+
29
+ def nested_form
30
+ @nested_form ||= Administrate::Page::Form.new(
9
31
  resolver.dashboard_class.new,
10
32
  data || resolver.resource_class.new,
11
33
  )
12
-
13
- super
14
34
  end
15
35
 
16
- def self.permitted_attribute(attr)
17
- related_dashboard_attributes =
18
- Administrate::ResourceResolver.new("admin/#{attr}").
19
- dashboard_class.new.permitted_attributes + [:id]
36
+ def nested_show
37
+ @nested_show ||= Administrate::Page::Show.new(
38
+ resolver.dashboard_class.new,
39
+ data || resolver.resource_class.new,
40
+ )
41
+ end
20
42
 
21
- { "#{attr}_attributes": related_dashboard_attributes }
43
+ def linkable?
44
+ data.try(:persisted?)
22
45
  end
23
46
 
24
- attr_reader :nested_form
47
+ private
48
+
49
+ def resolver
50
+ @resolver ||=
51
+ Administrate::ResourceResolver.new("admin/#{associated_class.name}")
52
+ end
25
53
  end
26
54
  end
27
55
  end
@@ -1,16 +1,19 @@
1
1
  require_relative "base"
2
+ require "active_support/number_helper"
2
3
 
3
4
  module Administrate
4
5
  module Field
5
6
  class Number < Field::Base
6
7
  def to_s
7
- data.nil? ? "-" : format_string % value
8
+ result = data.nil? ? "-" : format_string % value
9
+ result = format(result) if options[:format]
10
+ prefix + result + suffix
8
11
  end
9
12
 
10
13
  private
11
14
 
12
15
  def format_string
13
- prefix + "%.#{decimals}f" + suffix
16
+ "%.#{decimals}f"
14
17
  end
15
18
 
16
19
  def prefix
@@ -30,6 +33,20 @@ module Administrate
30
33
  def value
31
34
  data * options.fetch(:multiplier, 1)
32
35
  end
36
+
37
+ def format(result)
38
+ formatter = options[:format][:formatter]
39
+ formatter_options = options[:format][:formatter_options].to_h
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
49
+ end
33
50
  end
34
51
  end
35
52
  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