tolaria 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +66 -0
  3. data/.yardopts +4 -0
  4. data/CNAME +1 -0
  5. data/CONTRIBUTING.md +32 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE.md +9 -0
  8. data/README.md +538 -0
  9. data/Rakefile +52 -0
  10. data/app/assets/fonts/admin/fontawesome.eot +0 -0
  11. data/app/assets/fonts/admin/fontawesome.svg +565 -0
  12. data/app/assets/fonts/admin/fontawesome.ttf +0 -0
  13. data/app/assets/fonts/admin/fontawesome.woff +0 -0
  14. data/app/assets/fonts/admin/fontawesome.woff2 +0 -0
  15. data/app/assets/images/admin/columbia_banner.png +0 -0
  16. data/app/assets/images/admin/favicon.ico +0 -0
  17. data/app/assets/images/admin/noise.png +0 -0
  18. data/app/assets/images/admin/select_arrows.svg +1 -0
  19. data/app/assets/javascripts/admin/admin.js +4 -0
  20. data/app/assets/javascripts/admin/base.js +12 -0
  21. data/app/assets/javascripts/admin/lib/backbone.js +1888 -0
  22. data/app/assets/javascripts/admin/lib/jquery.chosen.js +1272 -0
  23. data/app/assets/javascripts/admin/lib/jquery.js +10361 -0
  24. data/app/assets/javascripts/admin/lib/jquery.selection.js +352 -0
  25. data/app/assets/javascripts/admin/lib/moment.js +3103 -0
  26. data/app/assets/javascripts/admin/lib/no.js +6 -0
  27. data/app/assets/javascripts/admin/lib/underscore.js +1570 -0
  28. data/app/assets/javascripts/admin/models/composer_buttons.js +45 -0
  29. data/app/assets/javascripts/admin/models/rails_meta.js +4 -0
  30. data/app/assets/javascripts/admin/views/field_with_errors.js +19 -0
  31. data/app/assets/javascripts/admin/views/fields/attachment_field.js +32 -0
  32. data/app/assets/javascripts/admin/views/fields/has_many.js +64 -0
  33. data/app/assets/javascripts/admin/views/fields/image_association_select.js +31 -0
  34. data/app/assets/javascripts/admin/views/fields/markdown_composer.js +167 -0
  35. data/app/assets/javascripts/admin/views/fields/searchable_select.js +70 -0
  36. data/app/assets/javascripts/admin/views/fields/slug_field.js +38 -0
  37. data/app/assets/javascripts/admin/views/fields/swatch_field.js +55 -0
  38. data/app/assets/javascripts/admin/views/fields/timestamp_field.js +80 -0
  39. data/app/assets/javascripts/admin/views/flash_message.js +18 -0
  40. data/app/assets/javascripts/admin/views/form_orchestrator.js +41 -0
  41. data/app/assets/javascripts/admin/views/navigation.js +20 -0
  42. data/app/assets/javascripts/admin/views/resource_form.js +18 -0
  43. data/app/assets/javascripts/admin/views/search_form.js +20 -0
  44. data/app/assets/javascripts/admin/views/sessions.js +109 -0
  45. data/app/assets/javascripts/admin/views/virtual_form.js +47 -0
  46. data/app/assets/stylesheets/admin/_base.scss +5 -0
  47. data/app/assets/stylesheets/admin/_reset.scss +149 -0
  48. data/app/assets/stylesheets/admin/_root.scss +63 -0
  49. data/app/assets/stylesheets/admin/admin.scss +4 -0
  50. data/app/assets/stylesheets/admin/components/_blank_slate.scss +44 -0
  51. data/app/assets/stylesheets/admin/components/_buttons.scss +82 -0
  52. data/app/assets/stylesheets/admin/components/_flash_message.scss +64 -0
  53. data/app/assets/stylesheets/admin/components/_footer.scss +9 -0
  54. data/app/assets/stylesheets/admin/components/_header.scss +107 -0
  55. data/app/assets/stylesheets/admin/components/_index_table.scss +120 -0
  56. data/app/assets/stylesheets/admin/components/_main.scss +68 -0
  57. data/app/assets/stylesheets/admin/components/_markdown_body.scss +109 -0
  58. data/app/assets/stylesheets/admin/components/_navigation.scss +110 -0
  59. data/app/assets/stylesheets/admin/components/_pagination.scss +36 -0
  60. data/app/assets/stylesheets/admin/components/_pill.scss +11 -0
  61. data/app/assets/stylesheets/admin/components/_resource_form.scss +222 -0
  62. data/app/assets/stylesheets/admin/components/_search_form.scss +36 -0
  63. data/app/assets/stylesheets/admin/components/_sessions.scss +152 -0
  64. data/app/assets/stylesheets/admin/components/_show_table.scss +67 -0
  65. data/app/assets/stylesheets/admin/components/forms/_attachment_field.scss +59 -0
  66. data/app/assets/stylesheets/admin/components/forms/_chosen.scss +478 -0
  67. data/app/assets/stylesheets/admin/components/forms/_image_association_select.scss +20 -0
  68. data/app/assets/stylesheets/admin/components/forms/_markdown_composer.scss +149 -0
  69. data/app/assets/stylesheets/admin/components/forms/_nested_fields.scss +63 -0
  70. data/app/assets/stylesheets/admin/components/forms/_searchable_select.scss +8 -0
  71. data/app/assets/stylesheets/admin/components/forms/_slug_field.scss +20 -0
  72. data/app/assets/stylesheets/admin/components/forms/_swatch_field.scss +47 -0
  73. data/app/assets/stylesheets/admin/components/forms/_timestamp_field.scss +15 -0
  74. data/app/assets/stylesheets/admin/components/help_link.scss +6 -0
  75. data/app/assets/stylesheets/admin/mixins/_clearfix.scss +18 -0
  76. data/app/assets/stylesheets/admin/mixins/_min_max_width.scss +11 -0
  77. data/app/assets/stylesheets/admin/mixins/_rgbb.scss +7 -0
  78. data/app/assets/stylesheets/admin/mixins/_visuallyhidden.scss +33 -0
  79. data/app/assets/stylesheets/admin/settings/_animations.scss +21 -0
  80. data/app/assets/stylesheets/admin/settings/_breakpoints.scss +2 -0
  81. data/app/assets/stylesheets/admin/settings/_colors.scss +32 -0
  82. data/app/assets/stylesheets/admin/settings/_fonts.scss +31 -0
  83. data/app/assets/stylesheets/admin/settings/_icons.scss +1658 -0
  84. data/app/controllers/admin/admin_controller.rb +21 -0
  85. data/app/controllers/admin/sessions_controller.rb +112 -0
  86. data/app/controllers/tolaria/resource_controller.rb +132 -0
  87. data/app/controllers/tolaria/tolaria_controller.rb +40 -0
  88. data/app/helpers/admin/table_helper.rb +175 -0
  89. data/app/helpers/admin/view_helper.rb +76 -0
  90. data/app/mailers/passcode_mailer.rb +11 -0
  91. data/app/models/administrator.rb +146 -0
  92. data/app/views/admin/administrators/_form.html.erb +16 -0
  93. data/app/views/admin/administrators/_index.html.erb +20 -0
  94. data/app/views/admin/administrators/_search.html.erb +5 -0
  95. data/app/views/admin/administrators/_show.html.erb +14 -0
  96. data/app/views/admin/help/help_link.html.erb +16 -0
  97. data/app/views/admin/session/form.html.erb +52 -0
  98. data/app/views/admin/shared/_flash_messages.html.erb +42 -0
  99. data/app/views/admin/shared/_footer.html.erb +8 -0
  100. data/app/views/admin/shared/_head.html.erb +11 -0
  101. data/app/views/admin/shared/_header.html.erb +43 -0
  102. data/app/views/admin/shared/_navigation.html.erb +47 -0
  103. data/app/views/admin/shared/_skiplinks.html.erb +0 -0
  104. data/app/views/admin/shared/forms/_attachment_field.html.erb +17 -0
  105. data/app/views/admin/shared/forms/_has_many.html.erb +14 -0
  106. data/app/views/admin/shared/forms/_has_many_header.html.erb +19 -0
  107. data/app/views/admin/shared/forms/_image_association_select.html.erb +6 -0
  108. data/app/views/admin/shared/forms/_image_field.html.erb +19 -0
  109. data/app/views/admin/shared/forms/_markdown_composer.html.erb +29 -0
  110. data/app/views/admin/shared/forms/_searchable_select.html.erb +3 -0
  111. data/app/views/admin/shared/forms/_slug_field.html.erb +9 -0
  112. data/app/views/admin/shared/forms/_swatch_field.html.erb +4 -0
  113. data/app/views/admin/shared/forms/_timestamp_field.html.erb +19 -0
  114. data/app/views/admin/tolaria_resource/_form_buttons.html.erb +10 -0
  115. data/app/views/admin/tolaria_resource/_index_table.html.erb +73 -0
  116. data/app/views/admin/tolaria_resource/_search_form.html.erb +32 -0
  117. data/app/views/admin/tolaria_resource/_show_buttons.html.erb +13 -0
  118. data/app/views/admin/tolaria_resource/edit.html.erb +34 -0
  119. data/app/views/admin/tolaria_resource/index.html.erb +36 -0
  120. data/app/views/admin/tolaria_resource/new.html.erb +1 -0
  121. data/app/views/admin/tolaria_resource/show.html.erb +52 -0
  122. data/app/views/kaminari/admin/_first_page.html.erb +9 -0
  123. data/app/views/kaminari/admin/_last_page.html.erb +9 -0
  124. data/app/views/kaminari/admin/_next_page.html.erb +9 -0
  125. data/app/views/kaminari/admin/_page.html.erb +17 -0
  126. data/app/views/kaminari/admin/_paginator.html.erb +21 -0
  127. data/app/views/kaminari/admin/_prev_page.html.erb +9 -0
  128. data/app/views/layouts/admin/admin.html.erb +21 -0
  129. data/app/views/layouts/admin/sessions.html.erb +12 -0
  130. data/app/views/passcode_mailer/passcode.text.erb +5 -0
  131. data/lib/generators/tolaria/install/install_generator.rb +21 -0
  132. data/lib/generators/tolaria/install/templates/administrators_migration.rb +31 -0
  133. data/lib/generators/tolaria/install/templates/tolaria_initializer.rb +93 -0
  134. data/lib/tasks/admin.rake +32 -0
  135. data/lib/tolaria.rb +27 -0
  136. data/lib/tolaria/active_record.rb +55 -0
  137. data/lib/tolaria/admin.rb +4 -0
  138. data/lib/tolaria/categories.rb +21 -0
  139. data/lib/tolaria/config.rb +40 -0
  140. data/lib/tolaria/default_config.rb +74 -0
  141. data/lib/tolaria/engine.rb +23 -0
  142. data/lib/tolaria/form_buildable.rb +203 -0
  143. data/lib/tolaria/help_links.rb +78 -0
  144. data/lib/tolaria/introspection.rb +13 -0
  145. data/lib/tolaria/manage.rb +57 -0
  146. data/lib/tolaria/managed_class.rb +90 -0
  147. data/lib/tolaria/markdown.rb +28 -0
  148. data/lib/tolaria/random_tokens.rb +16 -0
  149. data/lib/tolaria/reload.rb +21 -0
  150. data/lib/tolaria/routes.rb +33 -0
  151. data/lib/tolaria/version.rb +13 -0
  152. data/test/demo/Rakefile +4 -0
  153. data/test/demo/app/assets/javascripts/application.js +1 -0
  154. data/test/demo/app/assets/stylesheets/application.scss +1 -0
  155. data/test/demo/app/controllers/application_controller.rb +5 -0
  156. data/test/demo/app/controllers/concerns/.keep +0 -0
  157. data/test/demo/app/controllers/homepage_controller.rb +4 -0
  158. data/test/demo/app/helpers/application_helper.rb +2 -0
  159. data/test/demo/app/mailers/.keep +0 -0
  160. data/test/demo/app/models/.keep +0 -0
  161. data/test/demo/app/models/blog_post.rb +43 -0
  162. data/test/demo/app/models/footnote.rb +5 -0
  163. data/test/demo/app/models/image.rb +19 -0
  164. data/test/demo/app/models/legal_page.rb +24 -0
  165. data/test/demo/app/models/miscellany.rb +12 -0
  166. data/test/demo/app/models/topic.rb +22 -0
  167. data/test/demo/app/models/video.rb +16 -0
  168. data/test/demo/app/views/admin/blog_posts/_form.html.erb +48 -0
  169. data/test/demo/app/views/admin/blog_posts/_search.html.erb +5 -0
  170. data/test/demo/app/views/admin/help/markdown-help.md +95 -0
  171. data/test/demo/app/views/admin/images/_form.html.erb +26 -0
  172. data/test/demo/app/views/admin/legal_pages/_form.html.erb +15 -0
  173. data/test/demo/app/views/admin/topics/_form.html.erb +3 -0
  174. data/test/demo/app/views/admin/videos/_form.html.erb +11 -0
  175. data/test/demo/app/views/homepage/homepage.html.erb +3 -0
  176. data/test/demo/app/views/layouts/application.html.erb +14 -0
  177. data/test/demo/bin/bundle +3 -0
  178. data/test/demo/bin/rails +4 -0
  179. data/test/demo/bin/rake +4 -0
  180. data/test/demo/bin/setup +29 -0
  181. data/test/demo/config.ru +4 -0
  182. data/test/demo/config/application.rb +26 -0
  183. data/test/demo/config/boot.rb +4 -0
  184. data/test/demo/config/database.yml +18 -0
  185. data/test/demo/config/environment.rb +3 -0
  186. data/test/demo/config/environments/development.rb +43 -0
  187. data/test/demo/config/environments/test.rb +44 -0
  188. data/test/demo/config/initializers/assets.rb +11 -0
  189. data/test/demo/config/initializers/cookies_serializer.rb +2 -0
  190. data/test/demo/config/initializers/filter_parameter_logging.rb +3 -0
  191. data/test/demo/config/initializers/inflections.rb +17 -0
  192. data/test/demo/config/initializers/markdown.rb +44 -0
  193. data/test/demo/config/initializers/secret_token.rb +2 -0
  194. data/test/demo/config/initializers/session_store.rb +2 -0
  195. data/test/demo/config/initializers/tolaria.rb +17 -0
  196. data/test/demo/config/initializers/wrap_parameters.rb +14 -0
  197. data/test/demo/config/routes.rb +4 -0
  198. data/test/demo/db/migrate/20150601202901_create_administrators.rb +31 -0
  199. data/test/demo/db/migrate/20150603204006_add_testing_models.rb +27 -0
  200. data/test/demo/db/migrate/20150609232013_create_footnotes.rb +10 -0
  201. data/test/demo/db/migrate/20150610135235_create_additional_demo_objects.rb +50 -0
  202. data/test/demo/db/schema.rb +112 -0
  203. data/test/demo/log/.keep +0 -0
  204. data/test/demo/public/404.html +67 -0
  205. data/test/demo/public/422.html +67 -0
  206. data/test/demo/public/500.html +66 -0
  207. data/test/demo/public/favicon.ico +0 -0
  208. data/test/integration/help_link_test.rb +73 -0
  209. data/test/integration/interface_test.rb +63 -0
  210. data/test/integration/router_test.rb +73 -0
  211. data/test/integration/session_test.rb +88 -0
  212. data/test/test_helper.rb +58 -0
  213. data/test/unit/configuration_test.rb +21 -0
  214. data/test/unit/managed_classes_test.rb +54 -0
  215. data/test/unit/markdown_test.rb +12 -0
  216. data/test/unit/menu_test.rb +32 -0
  217. data/test/unit/random_tokens_test.rb +13 -0
  218. data/tolaria.gemspec +35 -0
  219. metadata +499 -0
@@ -0,0 +1,74 @@
1
+ Tolaria.configure do |config|
2
+
3
+ # The name of the company or group using this admin interface.
4
+ # Used in system navigation and HTML page titles.
5
+ config.company_name = "Company Name"
6
+
7
+ # Redirect the users to this route by default when logging in.
8
+ config.default_redirect = :admin_administrators
9
+
10
+ # Tolaria sends authentication emails. Set the value of the From header here.
11
+ config.from_address = "Rails <tolaria@example.org>"
12
+
13
+ # You can assign models to a category group them on the nav menu.
14
+ # Add/modify categories by changing the array below.
15
+ # Categories first in the array will be first on the menu.
16
+ config.menu_categories = ["Settings"]
17
+
18
+ # The cost factor for bcrypt.
19
+ # It is VERY DANGEROUS to set this below 10 for production.
20
+ # Use 1 in test mode to speed up your test suite.
21
+ config.bcrypt_cost = Rails.env.test?? 1 : 13
22
+
23
+ # The length of time that emailed passcodes should be valid.
24
+ # It is STRONGLY RECOMMENDED that you keep this under 30 minutes.
25
+ config.passcode_lifespan = 10.minutes
26
+
27
+ # The number of times an administrator can flunk their passcode
28
+ # challenge or request a token before Tolaria disables their account.
29
+ config.lockout_threshold = 10
30
+
31
+ # The length of time that an administrator’s account is disabled
32
+ # after they trip the lockout threshold.
33
+ config.lockout_duration = 1.hour
34
+
35
+ # An array of hashes used to construct HelpLinks
36
+ config.help_links = []
37
+
38
+ # Tolaria does not come bundled with a Markdown processing strategy.
39
+ # You must provide a string that names a Ruby constant that can process Markdown.
40
+ # The constant must respond to render(document), returning a string of HTML.
41
+ # For example: if you provide "MyMarkDownRenderer", Tolaria will
42
+ # call MyMarkdownRenderer.render(document).
43
+ config.markdown_renderer = nil
44
+
45
+ # Tolaria attempts to convert models to a pretty “display”
46
+ # string for presenting in forms and listings.
47
+ # The methods below are tried in order on models until one responds.
48
+ # Must be an array of symbols.
49
+ config.display_name_methods = %i[
50
+ admin_name
51
+ display_name
52
+ title
53
+ name
54
+ label
55
+ username
56
+ login
57
+ email
58
+ to_s
59
+ id
60
+ ]
61
+
62
+ # The number of items on each page when paginating models.
63
+ config.page_size = 15
64
+
65
+ # Default permitted_params for all forms.
66
+ # End-developers probably shouldn’t change this.
67
+ config.permitted_params = %i[
68
+ _method
69
+ authenticity_token
70
+ id
71
+ utf8
72
+ ]
73
+
74
+ end
@@ -0,0 +1,23 @@
1
+ module Tolaria
2
+ class Engine < ::Rails::Engine
3
+
4
+ # Add Tolaria’s assets to config.assets.precompile
5
+ initializer "tolaria.assets.precompile", group: :all do |app|
6
+ app.config.assets.precompile += %w[
7
+ admin/admin.css
8
+ admin/admin.js
9
+ admin/lib/no.js
10
+ ]
11
+ end
12
+
13
+ # Reference ApplicationController and set Tolaria.safe_management
14
+ # to indicate that the application has finished booting
15
+ initializer "tolaria.safe_management", group: :all do |app|
16
+ app.config.after_initialize do
17
+ "ApplicationController".safe_constantize
18
+ Tolaria.safe_management = true
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,203 @@
1
+ module Tolaria
2
+ module FormBuildable
3
+
4
+ # Returns a `p.hint` used to explain a nearby form field containing
5
+ # the given +hint_text+.
6
+ def hint(hint_text)
7
+ content_tag(:p, content_tag(:span, hint_text.chomp), class:"hint")
8
+ end
9
+
10
+ # Creates a `<select>` list that can be filtered by typing word fragments.
11
+ # Uses the jQuery Chosen plugin internally to power the user interface.
12
+ # Parameters are the same as Rails’s `collection_select`.
13
+ #
14
+ # #### Special Options
15
+ #
16
+ # - `:multiple` - if set to `true`, the select allows more than one choice.
17
+ # The default is `false`.
18
+ def searchable_select(method, collection, value_method, text_method, options = {})
19
+ render(partial:"admin/shared/forms/searchable_select", locals: {
20
+ f: self,
21
+ method: method,
22
+ collection: collection,
23
+ value_method: value_method,
24
+ text_method: text_method,
25
+ options: options,
26
+ html_options: options,
27
+ })
28
+ end
29
+
30
+ # Creates a `searchable_select` that also shows a dynamic image preview of the selected record.
31
+ # Useful for previewing images or avatars chosen by name.
32
+ # +preview_url_method+ should be a method name to call on the associated model instance
33
+ # that returns a fully-qualified URL to the image preview.
34
+ def image_association_select(method, collection, value_method, text_method, preview_url_method, options = {})
35
+ render(partial:"admin/shared/forms/image_association_select", locals: {
36
+ f: self,
37
+ method: method,
38
+ collection: collection,
39
+ value_method: value_method,
40
+ text_method: text_method,
41
+ preview_url_method: preview_url_method,
42
+ options: options,
43
+ html_options: options,
44
+ })
45
+ end
46
+
47
+ # Renders a Markdown composer element for editing +method+,
48
+ # with fullscreen previewing and some text assistance tools.
49
+ # Requires that you set `Tolaria.config.markdown_renderer`.
50
+ # Options are forwarded to `text_area`.
51
+ def markdown_composer(method, options = {})
52
+ render(partial:"admin/shared/forms/markdown_composer", locals: {
53
+ f: self,
54
+ method: method,
55
+ options: options,
56
+ })
57
+ end
58
+
59
+ # Returns a file upload field with a more pleasant interface than browser
60
+ # file inputs. Changes messaging if the +method+ already exists.
61
+ # Options are forwarded to the hidden `file_field`.
62
+ def attachment_field(method, options = {})
63
+ render(partial:"admin/shared/forms/attachment_field", locals: {
64
+ f: self,
65
+ method: method,
66
+ options: options,
67
+ })
68
+ end
69
+
70
+ # Returns an image upload field with a more pleasant interface than browser
71
+ # file inputs. Changes messaging if the +method+ already exists.
72
+ #
73
+ # #### Special Options
74
+ #
75
+ # - `:preview_url` If the file already exists, provide a URL to a 42×42px
76
+ # version of the image, and it will be displayed to the user in a preview
77
+ # box to better communicate which file they are replacing.
78
+ #
79
+ # Other options are forwarded to the hidden `file_field`.
80
+ def image_field(method, options = {})
81
+ render(partial:"admin/shared/forms/image_field", locals: {
82
+ f: self,
83
+ method: method,
84
+ options: options,
85
+ preview_url: options[:preview_url]
86
+ })
87
+ end
88
+
89
+ # Returns a text field that allows the user to input a date and time.
90
+ # Automatically validates itself and recovers to a template if blanked out.
91
+ # This field uses moment.js to parse the date and set the values on a
92
+ # set of hidden Rails `datetime_select` fields.
93
+ # Options are forwarded to the hidden `datetime_select` group.
94
+ def timestamp_field(method, options = {})
95
+ render(partial:"admin/shared/forms/timestamp_field", locals: {
96
+ f: self,
97
+ method: method,
98
+ options: options,
99
+ })
100
+ end
101
+
102
+ # Returns a text field that parameterizes its input as users type
103
+ # and renders it into the given preview template. Useful for
104
+ # demonstrating the value of a URL or other sluggified text.
105
+ #
106
+ # #### Special Options
107
+ #
108
+ # - `:pattern` - Should be a string that includes an asterisk (`*`)
109
+ # character. As the user types, the asterisk will be replaced with
110
+ # a parameterized version of the text in the text box and shown
111
+ # in a preview area below the field.
112
+ # The default is `"/blog-example/*"`.
113
+ #
114
+ # Other options are forwarded to `text_field`.
115
+ def slug_field(method, options = {})
116
+ pattern = options.delete(:pattern)
117
+ preview_value = self.object.send(method).try(:parameterize).presence || "*"
118
+ render(partial:"admin/shared/forms/slug_field", locals: {
119
+ f: self,
120
+ method: method,
121
+ options: options,
122
+ preview_value: preview_value,
123
+ pattern: (pattern || "/blog-example/*")
124
+ })
125
+ end
126
+
127
+ # Returns a text field that expects to be given a 3 or 6-digit
128
+ # hexadecimal color value. A preview block near the field
129
+ # demonstrates the provided color to the user.
130
+ # Options are forwarded to `text_field`.
131
+ def swatch_field(method, options = {})
132
+ render(partial:"admin/shared/forms/swatch_field", locals: {
133
+ f: self,
134
+ method: method,
135
+ options: options,
136
+ })
137
+ end
138
+
139
+ # Opens an ERB block for UI to manage a `has_many` + `accepts_nested_attributes_for`
140
+ # association in the current form. The block will look similar to this:
141
+ #
142
+ # <%= f.has_many :footnotes do |ff| %>
143
+ # <%= ff.has_many_header allow_destroy:true %>
144
+ # <% # Your nested model fields for footnote here %>
145
+ # <% end %>
146
+ #
147
+ # You must use `f.has_many_header` inside the block to create headers.
148
+ #
149
+ # If you need to pass an ordered collection or otherwise not allow automatically
150
+ # determining the association objects from the +association+ symbol you can
151
+ # pass the collection manually as +association_objects+.
152
+ #
153
+ # If +allow_create+ is `false` then the button to append a new model
154
+ # instance will be disabled. The default is `true`.
155
+ def has_many(association, association_objects = nil, allow_create:true, &block)
156
+
157
+ new_object = self.object.send(association).klass.new
158
+
159
+ view_template = self.fields_for(association, new_object, child_index:new_object.object_id) do |builder|
160
+ yield(builder)
161
+ end
162
+
163
+ existing_fields = self.fields_for(association, association_objects) do |builder|
164
+ yield(builder)
165
+ end
166
+
167
+ render(partial:"admin/shared/forms/has_many", locals: {
168
+ association: association,
169
+ button_label: association.to_s.humanize.singularize.titleize,
170
+ new_object: new_object,
171
+ existing_fields: existing_fields,
172
+ allow_create: allow_create,
173
+ f: self,
174
+ data: {
175
+ template: view_template.tr("\n"," "),
176
+ id: new_object.object_id,
177
+ }
178
+ })
179
+
180
+ end
181
+
182
+ # Creates a header suitable for use inside `has_many` for separating
183
+ # form elements. If +allow_destroy+ is `true`, controls will be exposed that allow
184
+ # removing nested instances of the model. The default is `false` to match Rails’.
185
+ def has_many_header(allow_destroy:false)
186
+ render(partial:"admin/shared/forms/has_many_header", locals: {
187
+ allow_destroy: allow_destroy,
188
+ f: self,
189
+ })
190
+ end
191
+
192
+ end
193
+ end
194
+
195
+ class Admin::FormBuilder < ActionView::Helpers::FormBuilder
196
+ include Tolaria::FormBuildable
197
+ delegate :content_tag, :tag, :render, to: :@template
198
+ end
199
+
200
+ class Ransack::Helpers::FormBuilder
201
+ include Tolaria::FormBuildable
202
+ delegate :content_tag, :tag, :render, to: :@template
203
+ end
@@ -0,0 +1,78 @@
1
+ module Tolaria
2
+
3
+ @help_links = []
4
+
5
+ def self.initialize_help_links!
6
+ @help_links = []
7
+ self.config.help_links.each do |hashy|
8
+ @help_links << Tolaria::HelpLink.new(**hashy)
9
+ end
10
+ end
11
+
12
+ def self.help_links
13
+ @help_links ||= []
14
+ end
15
+
16
+ class HelpLink
17
+
18
+ # The title of the link
19
+ attr_reader :title
20
+ # Part part of the link at `/admin/help/:slug` when rendering a Markdown file
21
+ attr_reader :slug
22
+ # The file path to the Markdown file
23
+ attr_reader :markdown_file
24
+ # The path to link to when not rendering a Markdown file
25
+ attr_reader :link_to
26
+
27
+ # Create a new HelpLink with the passed settings.
28
+ # You must provide +title+, the title of the link.
29
+ # To configure automatic rendering of a Markdown file, provide
30
+ # a string +slug+ and the path to a +markdown_file+.
31
+ # A route to view the file will be constructed for you at `/admin/help/:slug`.
32
+ # To link to an arbirary path or URI, provide it as +link_to+.
33
+ def initialize(title:, slug:nil, markdown_file:nil, link_to:nil)
34
+ @title = title.to_s.freeze
35
+ @slug = slug.to_s.freeze
36
+ @markdown_file = markdown_file.to_s.freeze
37
+ @link_to = link_to.to_s.freeze
38
+ validate!
39
+ end
40
+
41
+ # True if this HelpLink is a link to an arbirary path
42
+ def link_type?
43
+ link_to.present?
44
+ end
45
+
46
+ # True if this HelpLink is a Markdown file
47
+ def markdown_type?
48
+ markdown_file.present?
49
+ end
50
+
51
+ # Quack like an ActiveRecord::Base model
52
+ def to_param
53
+ slug
54
+ end
55
+
56
+ # Raises RuntimeError if this HelpLink is incorrectly configured
57
+ def validate!
58
+
59
+ if title.blank?
60
+ raise RuntimeError, "HelpLinks must provide a string title"
61
+ end
62
+
63
+ file_configured = (slug.present? && markdown_file.present?)
64
+ link_configured = link_to.present?
65
+
66
+ unless file_configured || link_configured
67
+ raise RuntimeError, "Incomplete HelpLink config. You must provide link_to, or both slug and markdown_file."
68
+ end
69
+
70
+ if file_configured && link_configured
71
+ raise RuntimeError, "Ambiguous HelpLink config. You must provide link_to, or both slug and markdown_file, but not all three."
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,13 @@
1
+ module Tolaria
2
+
3
+ # Using this method, you can attempt to get a pretty “display”
4
+ # string for presenting the passed +resource+ as a label.
5
+ def self.display_name(resource)
6
+ Tolaria.config.display_name_methods.each do |method_name|
7
+ if resource.respond_to?(method_name)
8
+ return resource.send(method_name).to_s.truncate(40, omission:"…")
9
+ end
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,57 @@
1
+ module Tolaria
2
+
3
+ @safe_mangagment = false
4
+ @managed_classes = []
5
+
6
+ # True if the application has actually booted
7
+ # and Tolaria is safe to start referencing models
8
+ def self.safe_management
9
+ @safe_mangagment
10
+ end
11
+
12
+ # Set the value of Tolaria.safe_management. +bool+ should be truthy.
13
+ # Don't call this method directly.
14
+ def self.safe_management=(bool)
15
+ @safe_mangagment = !!bool
16
+ end
17
+
18
+ # Tolaria keeps a list of all managed classes and the controllers
19
+ # for those classes internally so that other parts of the system
20
+ # can iterate over them. Return the list.
21
+ def self.managed_classes
22
+ @managed_classes
23
+ end
24
+
25
+ # Internal factory for adding managed classes. Developers should
26
+ # use ActiveRecord::Base#manage_with_tolaria.
27
+ def self.manage(klass, options = {})
28
+
29
+ # If we already have a class of this name, discard it
30
+ discard_managed_class(klass)
31
+
32
+ # Wrap the Rails model inside a Tolaria::ManagedClass
33
+ managed_klass = Tolaria::ManagedClass.create(klass, options)
34
+
35
+ # Add class to the internal tracker
36
+ @managed_classes.push(managed_klass)
37
+
38
+ # Check if there is already a correctly named controller because
39
+ # this means the end-developer made one and we don't want to unseat it.
40
+ # Otherwise create a controller for the model to use in the admin namespace.
41
+ unless "Admin::#{managed_klass.controller_name}".safe_constantize
42
+ managed_controller = Class.new(Tolaria::ResourceController)
43
+ ::Admin.const_set(managed_klass.controller_name, managed_controller)
44
+ end
45
+
46
+ return managed_klass
47
+
48
+ end
49
+
50
+ # Discard a managed class instance for the given ActiveRecord::Base
51
+ def self.discard_managed_class(klass)
52
+ @managed_classes.delete_if do |managed_class|
53
+ klass.to_s == managed_class.klass.to_s
54
+ end
55
+ end
56
+
57
+ end