administrate 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/administrate/components/{has_many_form.js → associative.js} +1 -0
  3. data/app/assets/stylesheets/administrate/base/_tables.scss +3 -0
  4. data/app/assets/stylesheets/administrate/components/_flashes.scss +0 -8
  5. data/app/assets/stylesheets/administrate/library/_variables.scss +10 -8
  6. data/app/controllers/administrate/application_controller.rb +25 -4
  7. data/app/helpers/administrate/application_helper.rb +23 -11
  8. data/app/views/administrate/application/_collection.html.erb +5 -3
  9. data/app/views/administrate/application/_form.html.erb +1 -1
  10. data/app/views/administrate/application/_navigation.html.erb +4 -4
  11. data/app/views/fields/select/_form.html.erb +21 -9
  12. data/app/views/fields/time/_form.html.erb +3 -2
  13. data/config/locales/administrate.ar.yml +2 -0
  14. data/config/locales/administrate.bs.yml +2 -0
  15. data/config/locales/administrate.ca.yml +2 -0
  16. data/config/locales/administrate.da.yml +2 -0
  17. data/config/locales/administrate.de.yml +2 -0
  18. data/config/locales/administrate.en.yml +2 -0
  19. data/config/locales/administrate.es.yml +2 -0
  20. data/config/locales/administrate.fr.yml +2 -0
  21. data/config/locales/administrate.id.yml +2 -0
  22. data/config/locales/administrate.it.yml +2 -0
  23. data/config/locales/administrate.ja.yml +2 -0
  24. data/config/locales/administrate.ko.yml +2 -0
  25. data/config/locales/administrate.nl.yml +3 -1
  26. data/config/locales/administrate.pl.yml +2 -0
  27. data/config/locales/administrate.pt-BR.yml +2 -0
  28. data/config/locales/administrate.pt.yml +2 -0
  29. data/config/locales/administrate.ru.yml +2 -0
  30. data/config/locales/{administrate.al.yml → administrate.sq.yml} +3 -1
  31. data/config/locales/administrate.sv.yml +2 -0
  32. data/config/locales/administrate.uk.yml +2 -0
  33. data/config/locales/administrate.vi.yml +2 -0
  34. data/config/locales/administrate.zh-CN.yml +2 -0
  35. data/config/locales/administrate.zh-TW.yml +2 -0
  36. data/docs/adding_controllers_without_related_model.md +36 -0
  37. data/docs/customizing_controller_actions.md +14 -0
  38. data/docs/customizing_dashboards.md +6 -5
  39. data/docs/customizing_page_views.md +15 -3
  40. data/lib/administrate/base_dashboard.rb +13 -8
  41. data/lib/administrate/custom_dashboard.rb +15 -0
  42. data/lib/administrate/field/associative.rb +1 -1
  43. data/lib/administrate/field/base.rb +4 -0
  44. data/lib/administrate/field/deferred.rb +15 -0
  45. data/lib/administrate/field/polymorphic.rb +1 -1
  46. data/lib/administrate/field/select.rb +6 -1
  47. data/lib/administrate/namespace.rb +4 -0
  48. data/lib/administrate/page/base.rb +1 -1
  49. data/lib/administrate/page/form.rb +1 -1
  50. data/lib/administrate/search.rb +22 -18
  51. data/lib/administrate/version.rb +1 -1
  52. data/lib/generators/administrate/dashboard/dashboard_generator.rb +14 -3
  53. metadata +9 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a347d47197b8258a2233e4bed126e17faf34cedf8b87ca21ad61283c7f5b8dcf
4
- data.tar.gz: 2a63e64339323c68e2daae8df8c48a50c2a85aa25cfded6a14899a275e523573
3
+ metadata.gz: e8120deb0ddc3a88302230382298ce39c25d60e4effca6336787016c46024d91
4
+ data.tar.gz: ad6262c38fca6d8be885d8687db7b02444da05c751c8dcb25a71d7a292d43f05
5
5
  SHA512:
6
- metadata.gz: e8e751c67ebafc99997f82646120be75a79f021e6a78ea207b1a393e38438abd2c9fe00596a9772514d86e9b079984b6dd110eaef6541517dbf369b27acce590
7
- data.tar.gz: ad12aac58cee58d0eb620c0b60627f07bfddaa7853e6c518b0a2cbe4d219c61d64a41b761d7a958bf8b65c4003ecca74b572245ae1db54e2616aafe4420e494c
6
+ metadata.gz: e6bfde68398c548cfe04665c21c5e44fd8f659c404ddfcb141c5b358396cb6d0ca1c087b459a322553ff9dfeb195f033d817829d7a84bc80024004f1390faf5c
7
+ data.tar.gz: 794416e6cc50fadef9674c2fcb5316be67e7523d19c31431dfe3f80165a5355f3ce12019aad78681b6f5ede71797f3b3bab9b911236e8f3231bd0fcdacd15bc2
@@ -1,3 +1,4 @@
1
1
  $(function() {
2
+ $('.field-unit--belongs-to select').selectize({});
2
3
  $(".field-unit--has-many select").selectize({});
3
4
  });
@@ -21,6 +21,9 @@ tr {
21
21
  tbody tr {
22
22
  &:hover {
23
23
  background-color: $base-background-color;
24
+ }
25
+
26
+ [role=link] {
24
27
  cursor: pointer;
25
28
  }
26
29
 
@@ -1,11 +1,3 @@
1
- $base-spacing: 1.5em !default;
2
- $flashes: (
3
- "alert": #fff6bf,
4
- "error": #fbe3e4,
5
- "notice": #e5edf8,
6
- "success": #e6efc2,
7
- ) !default;
8
-
9
1
  @each $flash-type, $color in $flashes {
10
2
  .flash-#{$flash-type} {
11
3
  background-color: $color;
@@ -22,8 +22,10 @@ $black: #000 !default;
22
22
 
23
23
  $blue: #1976d2 !default;
24
24
  $red: #d32f2f !default;
25
- $light-yellow: #f0cd66 !default;
26
- $light-green: #4ab471 !default;
25
+ $light-yellow: #fff6bf !default;
26
+ $light-red: #fbe3e4 !default;
27
+ $light-green: #e6efc2 !default;
28
+ $light-blue: #e5edf8 !default;
27
29
 
28
30
  $grey-0: #f6f7f7 !default;
29
31
  $grey-1: #dfe0e1 !default;
@@ -47,12 +49,12 @@ $focus-outline: $focus-outline-width solid $focus-outline-color;
47
49
  $focus-outline-offset: 1px;
48
50
 
49
51
  // Flash Colors
50
- $flash-colors: (
51
- alert: $light-yellow,
52
- error: $red,
53
- notice: mix($white, $blue, 50%),
54
- success: $light-green
55
- );
52
+ $flashes: (
53
+ "alert": $light-yellow,
54
+ "error": $light-red,
55
+ "notice": $light-blue,
56
+ "success": $light-green
57
+ ) !default;
56
58
 
57
59
  // Border
58
60
  $base-border-color: $grey-1 !default;
@@ -27,7 +27,7 @@ module Administrate
27
27
  end
28
28
 
29
29
  def new
30
- resource = resource_class.new
30
+ resource = new_resource
31
31
  authorize_resource(resource)
32
32
  render locals: {
33
33
  page: Administrate::Page::Form.new(dashboard, resource),
@@ -101,12 +101,31 @@ module Administrate
101
101
  end
102
102
 
103
103
  def order
104
- @order ||= Administrate::Order.new(
105
- params.fetch(resource_name, {}).fetch(:order, nil),
106
- params.fetch(resource_name, {}).fetch(:direction, nil),
104
+ @order ||= Administrate::Order.new(sorting_attribute, sorting_direction)
105
+ end
106
+
107
+ def sorting_attribute
108
+ params.fetch(resource_name, {}).fetch(
109
+ :order,
110
+ default_sorting_attribute,
107
111
  )
108
112
  end
109
113
 
114
+ def default_sorting_attribute
115
+ nil
116
+ end
117
+
118
+ def sorting_direction
119
+ params.fetch(resource_name, {}).fetch(
120
+ :direction,
121
+ default_sorting_direction,
122
+ )
123
+ end
124
+
125
+ def default_sorting_direction
126
+ nil
127
+ end
128
+
110
129
  def dashboard
111
130
  @dashboard ||= dashboard_class.new
112
131
  end
@@ -144,6 +163,8 @@ module Administrate
144
163
  else
145
164
  raise "Unrecognised param data: #{data.inspect}"
146
165
  end
166
+ elsif data.is_a?(ActionController::Parameters)
167
+ data.transform_values { |v| read_param_value(v) }
147
168
  else
148
169
  data
149
170
  end
@@ -11,7 +11,7 @@ module Administrate
11
11
  end
12
12
 
13
13
  def render_field(field, locals = {})
14
- locals.merge!(field: field)
14
+ locals[:field] = field
15
15
  render locals: locals, partial: field.to_partial_path
16
16
  end
17
17
 
@@ -34,17 +34,20 @@ module Administrate
34
34
  resource_class.validators_on(field_name)
35
35
  end
36
36
 
37
- def class_from_resource(resource_name)
38
- resource_name.to_s.classify.constantize
37
+ def dashboard_from_resource(resource_name)
38
+ "#{resource_name.to_s.singularize}_dashboard".classify.constantize
39
+ end
40
+
41
+ def model_from_resource(resource_name)
42
+ dashboard = dashboard_from_resource(resource_name)
43
+ dashboard.try(:model) || resource_name.to_sym
39
44
  end
40
45
 
41
46
  def display_resource_name(resource_name)
42
- class_from_resource(resource_name).
43
- model_name.
44
- human(
45
- count: PLURAL_MANY_COUNT,
46
- default: resource_name.to_s.pluralize.titleize,
47
- )
47
+ dashboard_from_resource(resource_name).resource_name(
48
+ count: PLURAL_MANY_COUNT,
49
+ default: default_resource_name(resource_name),
50
+ )
48
51
  end
49
52
 
50
53
  def sort_order(order)
@@ -55,8 +58,11 @@ module Administrate
55
58
  end
56
59
  end
57
60
 
58
- def resource_index_route_key(resource_name)
59
- ActiveModel::Naming.route_key(class_from_resource(resource_name))
61
+ def resource_index_route(resource_name)
62
+ url_for(
63
+ action: "index",
64
+ controller: "/#{namespace}/#{resource_name}",
65
+ )
60
66
  end
61
67
 
62
68
  def sanitized_order_params(page, current_field_name)
@@ -72,5 +78,11 @@ module Administrate
72
78
  :per_page, resource_name => %i[order direction]
73
79
  )
74
80
  end
81
+
82
+ private
83
+
84
+ def default_resource_name(resource_name)
85
+ resource_name.to_s.pluralize.gsub("/", "_").titleize
86
+ end
75
87
  end
76
88
  end
@@ -55,19 +55,21 @@ to display a collection of resources in an HTML table.
55
55
  <tbody>
56
56
  <% resources.each do |resource| %>
57
57
  <tr class="js-table-row"
58
- tabindex="0"
59
- <% if valid_action? :show, collection_presenter.resource_name %>
60
- <%= %(role=link data-url=#{polymorphic_path([namespace, resource])}) %>
58
+ <% if show_action? :show, resource %>
59
+ <%= %(tabindex=0 role=link data-url=#{polymorphic_path([namespace, resource])}) %>
61
60
  <% end %>
62
61
  >
63
62
  <% collection_presenter.attributes_for(resource).each do |attribute| %>
64
63
  <td class="cell-data cell-data--<%= attribute.html_class %>">
65
64
  <% if show_action? :show, resource -%>
66
65
  <a href="<%= polymorphic_path([namespace, resource]) -%>"
66
+ tabindex="-1"
67
67
  class="action-show"
68
68
  >
69
69
  <%= render_field attribute %>
70
70
  </a>
71
+ <% else %>
72
+ <%= render_field attribute %>
71
73
  <% end -%>
72
74
  </td>
73
75
  <% end %>
@@ -21,7 +21,7 @@ and renders all form fields for a resource's editable attributes.
21
21
  <%= t(
22
22
  "administrate.form.errors",
23
23
  pluralized_errors: pluralize(page.resource.errors.count, t("administrate.form.error")),
24
- resource_name: display_resource_name(page.resource_name)
24
+ resource_name: display_resource_name(page.resource_name).singularize
25
25
  ) %>
26
26
  </h2>
27
27
 
@@ -8,13 +8,13 @@ as defined by the routes in the `admin/` namespace
8
8
  %>
9
9
 
10
10
  <nav class="navigation" role="navigation">
11
- <%= link_to "Back to app", root_url, class: "button button--alt" %>
11
+ <%= link_to(t("administrate.navigation.back_to_app"), root_url, class: "button button--alt") if defined?(root_url) %>
12
12
 
13
- <% Administrate::Namespace.new(namespace).resources.each do |resource| %>
13
+ <% Administrate::Namespace.new(namespace).resources_with_index_route.each do |resource| %>
14
14
  <%= link_to(
15
15
  display_resource_name(resource),
16
- [namespace, resource_index_route_key(resource)],
16
+ resource_index_route(resource),
17
17
  class: "navigation__link navigation__link--#{nav_link_state(resource)}"
18
- ) if valid_action? :index, resource %>
18
+ ) if valid_action?(:index, resource) && show_action?(:index, model_from_resource(resource)) %>
19
19
  <% end %>
20
20
  </nav>
@@ -19,13 +19,25 @@ to be displayed on a resource's edit form page.
19
19
  <%= f.label field.attribute %>
20
20
  </div>
21
21
  <div class="field-unit__field">
22
- <%= f.select(
23
- field.attribute,
24
- options_from_collection_for_select(
25
- field.selectable_options,
26
- :to_s,
27
- :to_s,
28
- field.data.presence,
29
- )
30
- ) %>
22
+ <% if field.selectable_options.first&.is_a?(Array) %>
23
+ <%= f.select(
24
+ field.attribute,
25
+ options_from_collection_for_select(
26
+ field.selectable_options,
27
+ :last,
28
+ :first,
29
+ field.data.presence,
30
+ )
31
+ ) %>
32
+ <% else %>
33
+ <%= f.select(
34
+ field.attribute,
35
+ options_from_collection_for_select(
36
+ field.selectable_options,
37
+ :to_s,
38
+ :to_s,
39
+ field.data.presence,
40
+ )
41
+ ) %>
42
+ <% end %>
31
43
  </div>
@@ -2,7 +2,7 @@
2
2
  # Time Form Partial
3
3
 
4
4
  This partial renders an input element for time attributes.
5
- By default, the input is a select field for the time attributes.
5
+ By default, the input is a text field that is augmented with [DateTimePicker].
6
6
 
7
7
  ## Local variables:
8
8
 
@@ -12,11 +12,12 @@ By default, the input is a select field for the time attributes.
12
12
  An instance of [Administrate::Field::Time][1].
13
13
  A wrapper around the tmie attributes pulled from the model.
14
14
 
15
+ [DateTimePicker]: https://github.com/Eonasdan/bootstrap-datetimepicker
15
16
  %>
16
17
 
17
18
  <div class="field-unit__label">
18
19
  <%= f.label field.attribute %>
19
20
  </div>
20
21
  <div class="field-unit__field">
21
- <%= f.text_field field.attribute, data: { type: 'time' } %>
22
+ <%= f.text_field field.attribute, data: { type: 'time' }, value: field.data&.strftime("%H:%M:%S") %>
22
23
  </div>
@@ -23,6 +23,8 @@ ar:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26
+ navigation:
27
+ back_to_app: العودة إلى التطبيق
26
28
  search:
27
29
  clear: مسح البحث
28
30
  label: بحث %{resource}
@@ -22,6 +22,8 @@ bs:
22
22
  form:
23
23
  error: error
24
24
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
25
+ navigation:
26
+ back_to_app: Natrag na aplikaciju
25
27
  search:
26
28
  clear: Izbriši pretraživanje
27
29
  label: Pretraga %{resource}
@@ -23,6 +23,8 @@ ca:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} han impedit que %{resource_name} es guardés amb èxit:"
26
+ navigation:
27
+ back_to_app: Torna a l'aplicació
26
28
  search:
27
29
  clear: Esborrar la cerca
28
30
  label: Cerca %{resource}
@@ -23,6 +23,8 @@ da:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26
+ navigation:
27
+ back_to_app: Tilbage til app
26
28
  search:
27
29
  clear: Ryd søgning
28
30
  label: Søg %{resource}
@@ -23,6 +23,8 @@ de:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} haben das Speichern dieses %{resource_name} verhindert:"
26
+ navigation:
27
+ back_to_app: Zurück zur App
26
28
  search:
27
29
  clear: Suche zurücksetzen
28
30
  label: "%{resource} durchsuchen"
@@ -23,6 +23,8 @@ en:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26
+ navigation:
27
+ back_to_app: Back to app
26
28
  search:
27
29
  clear: Clear search
28
30
  label: Search %{resource}
@@ -23,6 +23,8 @@ es:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} no permitieron guardar este %{resource_name}:"
26
+ navigation:
27
+ back_to_app: Regresar a la aplicación
26
28
  search:
27
29
  clear: Borrar búsqueda
28
30
  label: Buscar %{resource}
@@ -23,6 +23,8 @@ fr:
23
23
  form:
24
24
  error: erreur
25
25
  errors: "%{pluralized_errors} ont empêchés %{resource_name} d'être sauvergardé :"
26
+ navigation:
27
+ back_to_app: Retour à l'application
26
28
  search:
27
29
  clear: Effacer la recherche
28
30
  label: Chercher %{resource}
@@ -23,6 +23,8 @@ id:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} membuat %{resource_name} tidak bisa tersimpan:"
26
+ navigation:
27
+ back_to_app: Kembali ke aplikasi
26
28
  search:
27
29
  clear: Kosongkan pencarian
28
30
  label: Cari %{resource}
@@ -23,6 +23,8 @@ it:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26
+ navigation:
27
+ back_to_app: Torna all'app
26
28
  search:
27
29
  clear: Cancella ricerca
28
30
  label: Ricerca %{resource}
@@ -23,6 +23,8 @@ ja:
23
23
  form:
24
24
  error: エラー
25
25
  errors: "%{pluralized_errors}のため%{resource_name}を保存できません。"
26
+ navigation:
27
+ back_to_app: アプリに戻る
26
28
  search:
27
29
  clear: 検索をクリアする
28
30
  label: サーチ %{resource}
@@ -23,6 +23,8 @@ ko:
23
23
  form:
24
24
  error: 에러
25
25
  errors: "%{pluralized_errors}로 인하여 %{resource_name}을(를) 저장하는데 실패하였습니다."
26
+ navigation:
27
+ back_to_app: 앱으로 돌아 가기
26
28
  search:
27
29
  clear: 검색 초기화
28
30
  label: "%{resource} 검색"
@@ -23,6 +23,8 @@ nl:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26
+ navigation:
27
+ back_to_app: Terug naar app
26
28
  search:
27
- clear: CDuidelijke zoek
29
+ clear: Wissen
28
30
  label: Zoeken %{resource}
@@ -23,6 +23,8 @@ pl:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26
+ navigation:
27
+ back_to_app: Powrót do aplikacji
26
28
  search:
27
29
  clear: Wyczyść wyszukiwanie
28
30
  label: Szukanie %{resource}
@@ -24,6 +24,8 @@ pt-BR:
24
24
  form:
25
25
  error: error
26
26
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
27
+ navigation:
28
+ back_to_app: Voltar ao aplicativo
27
29
  search:
28
30
  clear: Limpar pesquisa
29
31
  label: Pesquisa %{resource}
@@ -24,6 +24,8 @@ pt:
24
24
  form:
25
25
  error: error
26
26
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
27
+ navigation:
28
+ back_to_app: Voltar ao aplicativo
27
29
  search:
28
30
  clear: Limpar pesquisa
29
31
  label: Pesquisa %{resource}
@@ -23,6 +23,8 @@ ru:
23
23
  form:
24
24
  error: Ошибка
25
25
  errors: "При сохранении %{resource_name} произошли ошибки:"
26
+ navigation:
27
+ back_to_app: Вернуться в приложение
26
28
  search:
27
29
  clear: Очистить поиск
28
30
  label: Поиск %{resource}
@@ -1,5 +1,5 @@
1
1
  ---
2
- al:
2
+ sq:
3
3
  administrate:
4
4
  actions:
5
5
  confirm: A jeni te sigurtë?
@@ -23,6 +23,8 @@ al:
23
23
  form:
24
24
  error: gabim
25
25
  errors: "%{pluralized_errors} nuk e lejoj %{resource_name} të ruhet:"
26
+ navigation:
27
+ back_to_app: Kthehu tek aplikacioni
26
28
  search:
27
29
  clear: Pastro kërkimin
28
30
  label: Kërko %{resource}
@@ -23,6 +23,8 @@ sv:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26
+ navigation:
27
+ back_to_app: Tillbaka till appen
26
28
  search:
27
29
  clear: Rensa sökningen
28
30
  label: Sök %{resource}
@@ -23,6 +23,8 @@ uk:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26
+ navigation:
27
+ back_to_app: Повернутися до програми
26
28
  search:
27
29
  clear: Очистити пошук
28
30
  label: пошук %{resource}
@@ -23,6 +23,8 @@ vi:
23
23
  form:
24
24
  error: error
25
25
  errors: "%{pluralized_errors} prohibited this %{resource_name} from being saved:"
26
+ navigation:
27
+ back_to_app: Quay lại ứng dụng
26
28
  search:
27
29
  clear: Tìm kiếm rõ ràng
28
30
  label: Tìm kiếm %{resource}
@@ -23,6 +23,8 @@ zh-CN:
23
23
  form:
24
24
  error: 错误
25
25
  errors: "%{resource_name} 保存前出现了 %{pluralized_errors} 个错误:"
26
+ navigation:
27
+ back_to_app: 返回应用
26
28
  search:
27
29
  clear: 清除搜索
28
30
  label: 搜索 %{resource}
@@ -23,6 +23,8 @@ zh-TW:
23
23
  form:
24
24
  error: 錯誤
25
25
  errors: "%{pluralized_errors} 導致此 %{resource_name} 不能被儲存:"
26
+ navigation:
27
+ back_to_app: 返回应用
26
28
  search:
27
29
  clear: 清除搜尋
28
30
  label: 搜尋 %{resource}
@@ -0,0 +1,36 @@
1
+ # Adding Controllers without a related Model
2
+
3
+ Sometimes you may want to add a custom controller that has no resource
4
+ related to it (for example for a statistics page).
5
+
6
+ To do that, you must define an `index` route, as only controllers with index
7
+ routes are displayed in the sidebar and then add a custom dashboard:
8
+
9
+ ```erb
10
+ # app/views/admin/stats/index.html.erb
11
+
12
+ <div style="padding: 20px">
13
+ <h1>Stats</h1>
14
+ <br>
15
+ <p><b>Total Customers:</b> <%= @stats[:customer_count] %></h1>
16
+ <br>
17
+ <p><b>Total Orders:</b> <%= @stats[:order_count] %></h1>
18
+ </div>
19
+ ```
20
+
21
+ ```ruby
22
+ # app/dashboards/stat_dashboard.rb
23
+ require "administrate/custom_dashboard"
24
+
25
+ class StatDashboard < Administrate::CustomDashboard
26
+ resource "Stats" # used by administrate in the views
27
+ end
28
+ ```
29
+
30
+ ```ruby
31
+ # config/routes.rb
32
+ namespace :admin do
33
+ # ...
34
+ resources :stats, only: [:index]
35
+ end
36
+ ```
@@ -56,3 +56,17 @@ end
56
56
  ```
57
57
 
58
58
  Action is one of `new`, `edit`, `show`, `destroy`.
59
+
60
+ ## Customizing Default Sorting
61
+
62
+ To set the default sorting on the index action you could override `default_sorting_attribute` or `default_sorting_direction` in your dashboard controller like this:
63
+
64
+ ```ruby
65
+ def default_sorting_attribute
66
+ :age
67
+ end
68
+
69
+ def default_sorting_direction
70
+ :desc
71
+ end
72
+ ```
@@ -93,7 +93,7 @@ Defaults to `:#{attribute}.to_s.singularize.camelcase`.
93
93
  `:searchable` - Specify if the attribute should be considered when searching.
94
94
  Default is `false`.
95
95
 
96
- `searchable_field` - Specify which column to use on the search, only applies
96
+ `searchable_fields` - Specify which columns to use on the search, only applies
97
97
  if `searchable` is `true`
98
98
 
99
99
  For example:
@@ -101,7 +101,7 @@ For example:
101
101
  ```ruby
102
102
  country: Field::BelongsTo.with_options(
103
103
  searchable: true,
104
- searchable_field: 'name',
104
+ searchable_fields: ['name'],
105
105
  )
106
106
  ```
107
107
 
@@ -132,7 +132,7 @@ Defaults to `:#{attribute}.to_s.singularize.camelcase`.
132
132
  `:searchable` - Specify if the attribute should be considered when searching.
133
133
  Default is `false`.
134
134
 
135
- `searchable_field` - Specify which column to use on the search, only applies if
135
+ `searchable_fields` - Specify which columns to use on the search, only applies if
136
136
  `searchable` is `true`
137
137
 
138
138
  For example:
@@ -140,7 +140,7 @@ For example:
140
140
  ```ruby
141
141
  cities: Field::HasMany.with_options(
142
142
  searchable: true,
143
- searchable_field: 'name',
143
+ searchable_fields: ['name'],
144
144
  )
145
145
  ```
146
146
 
@@ -200,7 +200,8 @@ objects to display as.
200
200
 
201
201
  **Field::Select**
202
202
 
203
- `:collection` - Specify the array or range to select from. Defaults to `[]`.
203
+ `:collection` - Specify the options shown on the select field. It accept either
204
+ an array or an object responding to `:call`. Defaults to `[]`.
204
205
 
205
206
  `:searchable` - Specify if the attribute should be considered when searching.
206
207
  Default is `true`.
@@ -2,12 +2,24 @@
2
2
  title: Customizing page views
3
3
  ---
4
4
 
5
- In order to change the appearance of any page,
6
- you can write custom Rails views.
5
+ You can provide replacements for any of Administrate's templates.
6
+ This way you can change the appearance of any page or element of
7
+ the interface.
8
+
9
+ In general, you can override any of the views under Administrate's
10
+ [/app/views][1].
11
+ For example, say that you want to customize the template used for flash
12
+ messages. You can provide your own as
13
+ `/app/views/administrate/application/_flashes.html.erb`, and it will replace
14
+ Administrate's own.
15
+
16
+ Figuring out which views are available and where can be repetitive. You can
17
+ spare yourself some effort by using the built-in view generators.
18
+
19
+ [1]: https://github.com/thoughtbot/administrate/tree/master/app/views
7
20
 
8
21
  ## Customizing for all resources
9
22
 
10
- The easiest way to get started is by using the built-in generators.
11
23
  In order to change the appearance of views for all resource types,
12
24
  call the generators with no arguments.
13
25
 
@@ -18,6 +18,18 @@ module Administrate
18
18
  class BaseDashboard
19
19
  include Administrate
20
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
+
21
33
  def attribute_types
22
34
  self.class::ATTRIBUTE_TYPES
23
35
  end
@@ -74,18 +86,11 @@ module Administrate
74
86
  "Attribute #{attr} could not be found in #{self.class}::ATTRIBUTE_TYPES"
75
87
  end
76
88
 
77
- def association_classes
78
- @association_classes ||=
79
- ObjectSpace.each_object(Class).
80
- select { |klass| klass < Administrate::Field::Associative }
81
- end
82
-
83
89
  def attribute_includes(attributes)
84
90
  attributes.map do |key|
85
91
  field = self.class::ATTRIBUTE_TYPES[key]
86
92
 
87
- next key if association_classes.include?(field)
88
- key if association_classes.include?(field.try(:deferred_class))
93
+ key if field.associative?
89
94
  end.compact
90
95
  end
91
96
  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
@@ -11,7 +11,7 @@ module Administrate
11
11
  associated_class_name.constantize
12
12
  end
13
13
 
14
- protected
14
+ private
15
15
 
16
16
  def associated_dashboard
17
17
  "#{associated_class_name}Dashboard".constantize.new
@@ -12,6 +12,10 @@ 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
@@ -21,14 +21,29 @@ 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
32
  def searchable_field
33
+ ActiveSupport::Deprecation.warn(
34
+ "searchable_field is deprecated, use searchable_fields instead",
35
+ )
29
36
  options.fetch(:searchable_field)
30
37
  end
31
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
+
32
47
  def permitted_attribute(attr, _options = nil)
33
48
  options.fetch(:foreign_key,
34
49
  deferred_class.permitted_attribute(attr, options))
@@ -23,7 +23,7 @@ module Administrate
23
23
  data ? data.to_global_id : nil
24
24
  end
25
25
 
26
- protected
26
+ private
27
27
 
28
28
  def associated_dashboard(klass = data.class)
29
29
  "#{klass.name}Dashboard".constantize.new
@@ -14,7 +14,12 @@ module Administrate
14
14
  private
15
15
 
16
16
  def collection
17
- @collection ||= options.fetch(:collection, [])
17
+ values = options.fetch(:collection, [])
18
+ if values.respond_to? :call
19
+ return values.arity.positive? ? values.call(self) : values.call
20
+ end
21
+
22
+ @collection ||= values
18
23
  end
19
24
  end
20
25
  end
@@ -18,6 +18,10 @@ module Administrate
18
18
  end
19
19
  end
20
20
 
21
+ def resources_with_index_route
22
+ routes.select { |_resource, route| route == "index" }.map(&:first).uniq
23
+ end
24
+
21
25
  private
22
26
 
23
27
  attr_reader :namespace
@@ -23,7 +23,7 @@ module Administrate
23
23
  dashboard.try(:item_includes) || []
24
24
  end
25
25
 
26
- protected
26
+ private
27
27
 
28
28
  def attribute_field(dashboard, resource, attribute_name, page)
29
29
  value = get_attribute_value(resource, attribute_name)
@@ -20,7 +20,7 @@ module Administrate
20
20
  dashboard.display_resource(resource)
21
21
  end
22
22
 
23
- protected
23
+ private
24
24
 
25
25
  attr_reader :dashboard
26
26
  end
@@ -81,14 +81,24 @@ module Administrate
81
81
  def query_template
82
82
  search_attributes.map do |attr|
83
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 ?"
84
+ searchable_fields(attr).map do |field|
85
+ attr_name = column_to_query(field)
86
+ "LOWER(CAST(#{table_name}.#{attr_name} AS CHAR(256))) LIKE ?"
87
+ end.join(" OR ")
87
88
  end.join(" OR ")
88
89
  end
89
90
 
91
+ def searchable_fields(attr)
92
+ return [attr] unless association_search?(attr)
93
+
94
+ attribute_types[attr].searchable_fields
95
+ end
96
+
90
97
  def query_values
91
- ["%#{term.mb_chars.downcase}%"] * search_attributes.count
98
+ fields_count = search_attributes.sum do |attr|
99
+ searchable_fields(attr).count
100
+ end
101
+ ["%#{term.mb_chars.downcase}%"] * fields_count
92
102
  end
93
103
 
94
104
  def search_attributes
@@ -117,7 +127,12 @@ module Administrate
117
127
 
118
128
  def query_table_name(attr)
119
129
  if association_search?(attr)
120
- ActiveRecord::Base.connection.quote_table_name(attr.to_s.pluralize)
130
+ provided_class_name = attribute_types[attr].options[:class_name]
131
+ if provided_class_name
132
+ provided_class_name.constantize.table_name
133
+ else
134
+ ActiveRecord::Base.connection.quote_table_name(attr.to_s.pluralize)
135
+ end
121
136
  else
122
137
  ActiveRecord::Base.connection.
123
138
  quote_table_name(@scoped_resource.table_name)
@@ -125,12 +140,7 @@ module Administrate
125
140
  end
126
141
 
127
142
  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
143
+ ActiveRecord::Base.connection.quote_column_name(attr)
134
144
  end
135
145
 
136
146
  def tables_to_join
@@ -140,13 +150,7 @@ module Administrate
140
150
  end
141
151
 
142
152
  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)
153
+ attribute_types[attribute].associative?
150
154
  end
151
155
 
152
156
  def term
@@ -1,3 +1,3 @@
1
1
  module Administrate
2
- VERSION = "0.13.0".freeze
2
+ VERSION = "0.14.0".freeze
3
3
  end
@@ -7,7 +7,7 @@ module Administrate
7
7
  boolean: "Field::Boolean",
8
8
  date: "Field::Date",
9
9
  datetime: "Field::DateTime",
10
- enum: "Field::String",
10
+ enum: "Field::Select",
11
11
  float: "Field::Number",
12
12
  integer: "Field::Number",
13
13
  time: "Field::Time",
@@ -16,7 +16,9 @@ module Administrate
16
16
  }
17
17
 
18
18
  ATTRIBUTE_OPTIONS_MAPPING = {
19
- enum: { searchable: false },
19
+ # procs must be defined in one line!
20
+ enum: { searchable: false,
21
+ collection: ->(field) { field.resource.class.send(field.attribute.to_s.pluralize).keys } },
20
22
  float: { decimals: 2 },
21
23
  }
22
24
 
@@ -136,7 +138,16 @@ module Administrate
136
138
  end
137
139
 
138
140
  def inspect_hash_as_ruby(hash)
139
- hash.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
141
+ hash.map do |key, value|
142
+ v_str = value.respond_to?(:call) ? proc_string(value) : value.inspect
143
+ "#{key}: #{v_str}"
144
+ end.join(", ")
145
+ end
146
+
147
+ def proc_string(value)
148
+ source = value.source_location
149
+ proc_string = IO.readlines(source.first)[source.second - 1]
150
+ proc_string[/->[^}]*} | (lambda|proc).*end/x]
140
151
  end
141
152
  end
142
153
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: administrate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Charlton
8
8
  - Grayson Wright
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-03-13 00:00:00.000000000 Z
12
+ date: 2020-07-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -187,8 +187,8 @@ extra_rdoc_files: []
187
187
  files:
188
188
  - Rakefile
189
189
  - app/assets/javascripts/administrate/application.js
190
+ - app/assets/javascripts/administrate/components/associative.js
190
191
  - app/assets/javascripts/administrate/components/date_time_picker.js
191
- - app/assets/javascripts/administrate/components/has_many_form.js
192
192
  - app/assets/javascripts/administrate/components/table.js
193
193
  - app/assets/stylesheets/administrate/application.scss
194
194
  - app/assets/stylesheets/administrate/base/_forms.scss
@@ -275,7 +275,6 @@ files:
275
275
  - app/views/fields/url/_show.html.erb
276
276
  - app/views/layouts/administrate/application.html.erb
277
277
  - config/i18n-tasks.yml
278
- - config/locales/administrate.al.yml
279
278
  - config/locales/administrate.ar.yml
280
279
  - config/locales/administrate.bs.yml
281
280
  - config/locales/administrate.ca.yml
@@ -293,6 +292,7 @@ files:
293
292
  - config/locales/administrate.pt-BR.yml
294
293
  - config/locales/administrate.pt.yml
295
294
  - config/locales/administrate.ru.yml
295
+ - config/locales/administrate.sq.yml
296
296
  - config/locales/administrate.sv.yml
297
297
  - config/locales/administrate.uk.yml
298
298
  - config/locales/administrate.vi.yml
@@ -300,6 +300,7 @@ files:
300
300
  - config/locales/administrate.zh-TW.yml
301
301
  - config/routes.rb
302
302
  - config/unicorn.rb
303
+ - docs/adding_controllers_without_related_model.md
303
304
  - docs/adding_custom_field_types.md
304
305
  - docs/authentication.md
305
306
  - docs/authorization.md
@@ -312,6 +313,7 @@ files:
312
313
  - docs/rails_api.md
313
314
  - lib/administrate.rb
314
315
  - lib/administrate/base_dashboard.rb
316
+ - lib/administrate/custom_dashboard.rb
315
317
  - lib/administrate/engine.rb
316
318
  - lib/administrate/field/associative.rb
317
319
  - lib/administrate/field/base.rb
@@ -374,7 +376,7 @@ homepage: https://administrate-prototype.herokuapp.com/
374
376
  licenses:
375
377
  - MIT
376
378
  metadata: {}
377
- post_install_message:
379
+ post_install_message:
378
380
  rdoc_options: []
379
381
  require_paths:
380
382
  - lib
@@ -390,7 +392,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
390
392
  version: '0'
391
393
  requirements: []
392
394
  rubygems_version: 3.0.3
393
- signing_key:
395
+ signing_key:
394
396
  specification_version: 4
395
397
  summary: A Rails engine for creating super-flexible admin dashboards
396
398
  test_files: []