tolaria 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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