releaf-core 0.2.1

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 (288) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +24 -0
  3. data/app/assets/fonts/releaf/RobotoRegular_normal_400.ttf +0 -0
  4. data/app/assets/fonts/releaf/RobotoRegular_normal_400.woff +0 -0
  5. data/app/assets/fonts/releaf/RobotoRegular_normal_400.woff2 +0 -0
  6. data/app/assets/images/releaf/icons/apple-touch-icon-114x114-precomposed.png +0 -0
  7. data/app/assets/images/releaf/icons/apple-touch-icon-152x152-precomposed.png +0 -0
  8. data/app/assets/images/releaf/icons/apple-touch-icon-72x72-precomposed.png +0 -0
  9. data/app/assets/images/releaf/icons/favicon.ico +0 -0
  10. data/app/assets/images/releaf/icons/favicon.png +0 -0
  11. data/app/assets/images/releaf/icons/msapplication-tile-144x144.png +0 -0
  12. data/app/assets/images/releaf/logo-login.png +0 -0
  13. data/app/assets/images/releaf/logo.png +0 -0
  14. data/app/assets/javascripts/ckeditor/plugins/mediaembed/icons/hidpi/mediaembed.png +0 -0
  15. data/app/assets/javascripts/ckeditor/plugins/mediaembed/icons/mediaembed.png +0 -0
  16. data/app/assets/javascripts/ckeditor/plugins/mediaembed/lang/en.js +12 -0
  17. data/app/assets/javascripts/ckeditor/plugins/mediaembed/lang/es.js +12 -0
  18. data/app/assets/javascripts/ckeditor/plugins/mediaembed/plugin.js +63 -0
  19. data/app/assets/javascripts/releaf/3rd_party/jquery-ui-timepicker-addon.js +1882 -0
  20. data/app/assets/javascripts/releaf/3rd_party/jquery.magnific-popup.min.js +4 -0
  21. data/app/assets/javascripts/releaf/application.js +13 -0
  22. data/app/assets/javascripts/releaf/include/ajax.js +66 -0
  23. data/app/assets/javascripts/releaf/include/ajaxbox.js +179 -0
  24. data/app/assets/javascripts/releaf/include/dialogs.js +13 -0
  25. data/app/assets/javascripts/releaf/include/field.type_associated_set.js +32 -0
  26. data/app/assets/javascripts/releaf/include/field.type_date_or_datetime_or_time.js +73 -0
  27. data/app/assets/javascripts/releaf/include/field.type_richtext.js +188 -0
  28. data/app/assets/javascripts/releaf/include/loader.js +45 -0
  29. data/app/assets/javascripts/releaf/include/localization.js +213 -0
  30. data/app/assets/javascripts/releaf/include/nested_fields.js +295 -0
  31. data/app/assets/javascripts/releaf/include/notifications.js +267 -0
  32. data/app/assets/javascripts/releaf/include/pagination.js +19 -0
  33. data/app/assets/javascripts/releaf/include/profile_settings.js +28 -0
  34. data/app/assets/javascripts/releaf/include/remote_validator.js +437 -0
  35. data/app/assets/javascripts/releaf/include/search.js +140 -0
  36. data/app/assets/javascripts/releaf/include/sidebar.js +149 -0
  37. data/app/assets/javascripts/releaf/include/sortable.js +69 -0
  38. data/app/assets/javascripts/releaf/include/toolbox.js +221 -0
  39. data/app/assets/javascripts/releaf/include/url_builder.js +193 -0
  40. data/app/assets/stylesheets/releaf/application.scss +9 -0
  41. data/app/assets/stylesheets/releaf/environment/extras.scss +11 -0
  42. data/app/assets/stylesheets/releaf/environment/functions/units.scss +12 -0
  43. data/app/assets/stylesheets/releaf/environment/functions.scss +1 -0
  44. data/app/assets/stylesheets/releaf/environment/mixins/blocks.scss +133 -0
  45. data/app/assets/stylesheets/releaf/environment/mixins/elements.scss +64 -0
  46. data/app/assets/stylesheets/releaf/environment/mixins/text.scss +61 -0
  47. data/app/assets/stylesheets/releaf/environment/mixins.scss +3 -0
  48. data/app/assets/stylesheets/releaf/environment/variables/colors.scss +93 -0
  49. data/app/assets/stylesheets/releaf/environment/variables/dimensions.scss +0 -0
  50. data/app/assets/stylesheets/releaf/environment/variables/magnific-popup.scss +6 -0
  51. data/app/assets/stylesheets/releaf/environment/variables.scss +5 -0
  52. data/app/assets/stylesheets/releaf/environment.scss +4 -0
  53. data/app/assets/stylesheets/releaf/layout/base.scss +32 -0
  54. data/app/assets/stylesheets/releaf/layout/breadcrumbs.scss +47 -0
  55. data/app/assets/stylesheets/releaf/layout/buttons.scss +107 -0
  56. data/app/assets/stylesheets/releaf/layout/deprecated.scss +29 -0
  57. data/app/assets/stylesheets/releaf/layout/dialogs.scss +138 -0
  58. data/app/assets/stylesheets/releaf/layout/errors.scss +28 -0
  59. data/app/assets/stylesheets/releaf/layout/extras.scss +10 -0
  60. data/app/assets/stylesheets/releaf/layout/fields.scss +305 -0
  61. data/app/assets/stylesheets/releaf/layout/fonts.scss +16 -0
  62. data/app/assets/stylesheets/releaf/layout/footer.scss +80 -0
  63. data/app/assets/stylesheets/releaf/layout/forms.scss +23 -0
  64. data/app/assets/stylesheets/releaf/layout/header.scss +55 -0
  65. data/app/assets/stylesheets/releaf/layout/icons.scss +4 -0
  66. data/app/assets/stylesheets/releaf/layout/images.scss +4 -0
  67. data/app/assets/stylesheets/releaf/layout/links.scss +7 -0
  68. data/app/assets/stylesheets/releaf/layout/localization.scss +79 -0
  69. data/app/assets/stylesheets/releaf/layout/main.scss +43 -0
  70. data/app/assets/stylesheets/releaf/layout/notifications.scss +67 -0
  71. data/app/assets/stylesheets/releaf/layout/pagination.scss +18 -0
  72. data/app/assets/stylesheets/releaf/layout/search.scss +70 -0
  73. data/app/assets/stylesheets/releaf/layout/sections.scss +138 -0
  74. data/app/assets/stylesheets/releaf/layout/sidebar.scss +306 -0
  75. data/app/assets/stylesheets/releaf/layout/tables.scss +99 -0
  76. data/app/assets/stylesheets/releaf/layout/text.scss +31 -0
  77. data/app/assets/stylesheets/releaf/layout/toolboxes.scss +69 -0
  78. data/app/assets/stylesheets/releaf/layout.scss +35 -0
  79. data/app/assets/stylesheets/releaf/vendor/magnific-popup/main.scss +596 -0
  80. data/app/assets/stylesheets/releaf/vendor/magnific-popup/settings.scss +45 -0
  81. data/app/builders/releaf/builders/association_reflector.rb +58 -0
  82. data/app/builders/releaf/builders/base.rb +80 -0
  83. data/app/builders/releaf/builders/collection.rb +8 -0
  84. data/app/builders/releaf/builders/confirm_destroy_dialog_builder.rb +25 -0
  85. data/app/builders/releaf/builders/confirm_dialog_builder.rb +54 -0
  86. data/app/builders/releaf/builders/edit_builder.rb +66 -0
  87. data/app/builders/releaf/builders/form_builder.rb +594 -0
  88. data/app/builders/releaf/builders/index_builder.rb +118 -0
  89. data/app/builders/releaf/builders/orderer.rb +5 -0
  90. data/app/builders/releaf/builders/page/header_builder.rb +70 -0
  91. data/app/builders/releaf/builders/page/layout_builder.rb +155 -0
  92. data/app/builders/releaf/builders/page/menu_builder.rb +140 -0
  93. data/app/builders/releaf/builders/pagination_builder.rb +107 -0
  94. data/app/builders/releaf/builders/refused_destroy_dialog_builder.rb +68 -0
  95. data/app/builders/releaf/builders/resource.rb +9 -0
  96. data/app/builders/releaf/builders/resource_dialog.rb +8 -0
  97. data/app/builders/releaf/builders/resource_view.rb +54 -0
  98. data/app/builders/releaf/builders/show_builder.rb +3 -0
  99. data/app/builders/releaf/builders/table_builder.rb +280 -0
  100. data/app/builders/releaf/builders/tags/releaf_associated_set_field.rb +40 -0
  101. data/app/builders/releaf/builders/template.rb +8 -0
  102. data/app/builders/releaf/builders/toolbox.rb +23 -0
  103. data/app/builders/releaf/builders/toolbox_builder.rb +27 -0
  104. data/app/builders/releaf/builders/view.rb +132 -0
  105. data/app/builders/releaf/builders.rb +38 -0
  106. data/app/builders/releaf/core/settings/form_builder.rb +21 -0
  107. data/app/builders/releaf/core/settings/table_builder.rb +11 -0
  108. data/app/controllers/concerns/releaf/breadcrumbs.rb +42 -0
  109. data/app/controllers/concerns/releaf/richtext_attachments.rb +20 -0
  110. data/app/controllers/releaf/base_controller.rb +458 -0
  111. data/app/controllers/releaf/core/errors_controller.rb +5 -0
  112. data/app/controllers/releaf/core/settings_controller.rb +50 -0
  113. data/app/helpers/releaf/application_helper.rb +53 -0
  114. data/app/helpers/releaf/button_helper.rb +50 -0
  115. data/app/helpers/releaf/javascript_helper.rb +75 -0
  116. data/app/lib/releaf/core/assets_resolver.rb +58 -0
  117. data/app/lib/releaf/core/default_searchable_fields.rb +43 -0
  118. data/app/lib/releaf/core/error_formatter.rb +103 -0
  119. data/app/lib/releaf/core/item_orderer.rb +102 -0
  120. data/app/lib/releaf/core/resource_base.rb +66 -0
  121. data/app/lib/releaf/core/resource_fields.rb +6 -0
  122. data/app/lib/releaf/core/resource_params.rb +47 -0
  123. data/app/lib/releaf/core/resource_table_fields.rb +10 -0
  124. data/app/lib/releaf/core/resource_utilities.rb +36 -0
  125. data/app/lib/releaf/core/responders/access_denied_responder.rb +9 -0
  126. data/app/lib/releaf/core/responders/after_save_responder.rb +28 -0
  127. data/app/lib/releaf/core/responders/confirm_destroy_responder.rb +13 -0
  128. data/app/lib/releaf/core/responders/destroy_responder.rb +10 -0
  129. data/app/lib/releaf/core/responders/error_responder.rb +9 -0
  130. data/app/lib/releaf/core/responders/feature_disabled_responder.rb +9 -0
  131. data/app/lib/releaf/core/responders/page_not_found_responder.rb +9 -0
  132. data/app/lib/releaf/core/responders.rb +31 -0
  133. data/app/lib/releaf/core/search.rb +147 -0
  134. data/app/lib/releaf/core/template_field_type_mapper.rb +127 -0
  135. data/app/models/releaf/richtext_attachment.rb +6 -0
  136. data/app/models/releaf/settings.rb +27 -0
  137. data/app/views/layouts/releaf/admin.html.erb +1 -0
  138. data/app/views/releaf/base/confirm_destroy.ruby +1 -0
  139. data/app/views/releaf/base/create_releaf_richtext_attachment.haml +1 -0
  140. data/app/views/releaf/base/edit.ruby +1 -0
  141. data/app/views/releaf/base/index.ruby +1 -0
  142. data/app/views/releaf/base/new.ruby +1 -0
  143. data/app/views/releaf/base/refused_destroy.ruby +1 -0
  144. data/app/views/releaf/base/show.ruby +1 -0
  145. data/app/views/releaf/base/toolbox.ruby +1 -0
  146. data/app/views/releaf/error_pages/_error.html.haml +10 -0
  147. data/app/views/releaf/error_pages/access_denied.html.haml +1 -0
  148. data/app/views/releaf/error_pages/feature_disabled.html.haml +1 -0
  149. data/app/views/releaf/error_pages/page_not_found.html.haml +2 -0
  150. data/lib/generators/dummy/install_generator.rb +18 -0
  151. data/lib/generators/dummy/templates/builders/admin/books/form_builder.rb +13 -0
  152. data/lib/generators/dummy/templates/builders/admin/books/index_builder.rb +30 -0
  153. data/lib/generators/dummy/templates/builders/admin/books/table_builder.rb +7 -0
  154. data/lib/generators/dummy/templates/builders/admin/nodes/form_builder.rb +7 -0
  155. data/lib/generators/dummy/templates/config/routes.rb +19 -0
  156. data/lib/generators/dummy/templates/controllers/admin/authors_controller.rb +12 -0
  157. data/lib/generators/dummy/templates/controllers/admin/books_controller.rb +17 -0
  158. data/lib/generators/dummy/templates/controllers/admin/chapters_controller.rb +3 -0
  159. data/lib/generators/dummy/templates/controllers/admin/publishers_controller.rb +3 -0
  160. data/lib/generators/dummy/templates/controllers/application_controller.rb +39 -0
  161. data/lib/generators/dummy/templates/controllers/concerns/.keep +0 -0
  162. data/lib/generators/dummy/templates/controllers/concerns/node_controller.rb +37 -0
  163. data/lib/generators/dummy/templates/controllers/contacts_controller.rb +3 -0
  164. data/lib/generators/dummy/templates/controllers/home_pages_controller.rb +3 -0
  165. data/lib/generators/dummy/templates/controllers/text_pages_controller.rb +3 -0
  166. data/lib/generators/dummy/templates/initializers/releaf.rb +35 -0
  167. data/lib/generators/dummy/templates/migrations/create_authors.rb +14 -0
  168. data/lib/generators/dummy/templates/migrations/create_banners.rb +11 -0
  169. data/lib/generators/dummy/templates/migrations/create_book_sequels.rb +14 -0
  170. data/lib/generators/dummy/templates/migrations/create_books.rb +27 -0
  171. data/lib/generators/dummy/templates/migrations/create_bundles.rb +7 -0
  172. data/lib/generators/dummy/templates/migrations/create_chapters.rb +13 -0
  173. data/lib/generators/dummy/templates/migrations/create_home_pages.rb +9 -0
  174. data/lib/generators/dummy/templates/migrations/create_node_extra_fields.rb +5 -0
  175. data/lib/generators/dummy/templates/migrations/create_publishers.rb +8 -0
  176. data/lib/generators/dummy/templates/migrations/create_text_pages.rb +9 -0
  177. data/lib/generators/dummy/templates/models/author.rb +10 -0
  178. data/lib/generators/dummy/templates/models/banner.rb +5 -0
  179. data/lib/generators/dummy/templates/models/book.rb +19 -0
  180. data/lib/generators/dummy/templates/models/book_sequel.rb +4 -0
  181. data/lib/generators/dummy/templates/models/bundle.rb +17 -0
  182. data/lib/generators/dummy/templates/models/chapter.rb +7 -0
  183. data/lib/generators/dummy/templates/models/home_page.rb +5 -0
  184. data/lib/generators/dummy/templates/models/node.rb +10 -0
  185. data/lib/generators/dummy/templates/models/publisher.rb +9 -0
  186. data/lib/generators/dummy/templates/models/text_page.rb +5 -0
  187. data/lib/generators/dummy/templates/views/contacts/show.html.haml +1 -0
  188. data/lib/generators/dummy/templates/views/home_pages/show.haml +1 -0
  189. data/lib/generators/dummy/templates/views/layouts/application.html.haml +22 -0
  190. data/lib/generators/dummy/templates/views/text_pages/show.haml +1 -0
  191. data/lib/generators/releaf/install_generator.rb +93 -0
  192. data/lib/generators/releaf/templates/initializers/haml.rb +1 -0
  193. data/lib/generators/releaf/templates/initializers/releaf.rb +30 -0
  194. data/lib/generators/releaf/templates/migrations/create_releaf_nodes.rb +28 -0
  195. data/lib/generators/releaf/templates/migrations/create_releaf_permissions.rb +12 -0
  196. data/lib/generators/releaf/templates/migrations/create_releaf_richtext_attachments.rb +12 -0
  197. data/lib/generators/releaf/templates/migrations/create_releaf_roles.rb +9 -0
  198. data/lib/generators/releaf/templates/migrations/create_releaf_settings.rb +17 -0
  199. data/lib/generators/releaf/templates/migrations/create_releaf_translations.rb +21 -0
  200. data/lib/generators/releaf/templates/migrations/create_releaf_users.rb +52 -0
  201. data/lib/generators/releaf/templates/models/node.rb +3 -0
  202. data/lib/generators/releaf/templates/seeds/seeds.rb +54 -0
  203. data/lib/releaf/core/application.rb +17 -0
  204. data/lib/releaf/core/builders_autoload.rb +27 -0
  205. data/lib/releaf/core/component.rb +9 -0
  206. data/lib/releaf/core/configuration.rb +101 -0
  207. data/lib/releaf/core/engine.rb +35 -0
  208. data/lib/releaf/core/exceptions.rb +38 -0
  209. data/lib/releaf/core/route_mapper.rb +59 -0
  210. data/lib/releaf/core/settings_ui_component.rb +7 -0
  211. data/lib/releaf/core/validation_error_codes.rb +36 -0
  212. data/lib/releaf/version.rb +3 -0
  213. data/lib/releaf-core.rb +14 -0
  214. data/lib/tasks/releaf_tasks.rake +4 -0
  215. data/releaf-core.gemspec +35 -0
  216. data/spec/builders/builders/association_reflector_spec.rb +138 -0
  217. data/spec/builders/builders/base_spec.rb +276 -0
  218. data/spec/builders/builders/collection_spec.rb +18 -0
  219. data/spec/builders/builders/confirm_destroy_dialog_builder_spec.rb +71 -0
  220. data/spec/builders/builders/confirm_dialog_builder_spec.rb +105 -0
  221. data/spec/builders/builders/edit_builder_spec.rb +215 -0
  222. data/spec/builders/builders/form_builder_spec.rb +562 -0
  223. data/spec/builders/builders/index_builder_spec.rb +345 -0
  224. data/spec/builders/builders/orderer_spec.rb +22 -0
  225. data/spec/builders/builders/page/header_builder_spec.rb +143 -0
  226. data/spec/builders/builders/page/layout_builder_spec.rb +73 -0
  227. data/spec/builders/builders/page/menu_builder_spec.rb +160 -0
  228. data/spec/builders/builders/pagination_builder_spec.rb +330 -0
  229. data/spec/builders/builders/resource_dialog_spec.rb +21 -0
  230. data/spec/builders/builders/resource_view_spec.rb +158 -0
  231. data/spec/builders/builders/show_builder_spec.rb +7 -0
  232. data/spec/builders/builders/table_builder_spec.rb +638 -0
  233. data/spec/builders/builders/template_spec.rb +12 -0
  234. data/spec/builders/builders/toolbox_builder_spec.rb +67 -0
  235. data/spec/builders/builders/toolbox_spec.rb +48 -0
  236. data/spec/builders/builders/view_spec.rb +281 -0
  237. data/spec/builders/builders_spec.rb +134 -0
  238. data/spec/builders/core/settings/form_builder_spec.rb +69 -0
  239. data/spec/builders/core/settings/table_builder_spec.rb +21 -0
  240. data/spec/controllers/concerns/releaf/richtext_attachments_spec.rb +51 -0
  241. data/spec/controllers/releaf/base_controller_spec.rb +447 -0
  242. data/spec/controllers/releaf/core/settings_controller_spec.rb +31 -0
  243. data/spec/features/ajaxbox_spec.rb +111 -0
  244. data/spec/features/authorization_spec.rb +50 -0
  245. data/spec/features/dragonfly_integration_spec.rb +24 -0
  246. data/spec/features/edit_actions_spec.rb +142 -0
  247. data/spec/features/errors_spec.rb +29 -0
  248. data/spec/features/index_actions_spec.rb +85 -0
  249. data/spec/features/index_table_spec.rb +32 -0
  250. data/spec/features/menu_spec.rb +71 -0
  251. data/spec/features/richtext_attachments_spec.rb +64 -0
  252. data/spec/features/richtext_embed_spec.rb +29 -0
  253. data/spec/features/richtext_spec.rb +19 -0
  254. data/spec/features/search_spec.rb +825 -0
  255. data/spec/features/settings_spec.rb +38 -0
  256. data/spec/features/title_spec.rb +13 -0
  257. data/spec/fixtures/common_fields.yml +17 -0
  258. data/spec/fixtures/cs.png +0 -0
  259. data/spec/fixtures/time.formats.xlsx +0 -0
  260. data/spec/fixtures/unicorn.jpg +0 -0
  261. data/spec/helpers/application_helper_spec.rb +75 -0
  262. data/spec/helpers/button_helper_spec.rb +146 -0
  263. data/spec/lib/releaf/core/application_spec.rb +42 -0
  264. data/spec/lib/releaf/core/assets_resolver_spec.rb +113 -0
  265. data/spec/lib/releaf/core/configuration_spec.rb +230 -0
  266. data/spec/lib/releaf/core/default_searchable_fields_spec.rb +161 -0
  267. data/spec/lib/releaf/core/error_formatter_spec.rb +242 -0
  268. data/spec/lib/releaf/core/item_orderer_spec.rb +142 -0
  269. data/spec/lib/releaf/core/resource_base_spec.rb +174 -0
  270. data/spec/lib/releaf/core/resource_fields_spec.rb +12 -0
  271. data/spec/lib/releaf/core/resource_params_spec.rb +117 -0
  272. data/spec/lib/releaf/core/resource_table_fields_spec.rb +18 -0
  273. data/spec/lib/releaf/core/resource_utilities_spec.rb +87 -0
  274. data/spec/lib/releaf/core/responders/access_denied_responder_spec.rb +12 -0
  275. data/spec/lib/releaf/core/responders/after_save_responder_spec.rb +102 -0
  276. data/spec/lib/releaf/core/responders/confirm_destroy_responder_spec.rb +26 -0
  277. data/spec/lib/releaf/core/responders/destroy_responder_spec.rb +30 -0
  278. data/spec/lib/releaf/core/responders/error_responder_spec.rb +26 -0
  279. data/spec/lib/releaf/core/responders/feature_disabled_responder_spec.rb +12 -0
  280. data/spec/lib/releaf/core/responders/page_not_found_responder_spec.rb +12 -0
  281. data/spec/lib/releaf/core/responders_spec.rb +60 -0
  282. data/spec/lib/releaf/core/template_field_type_mapper_spec.rb +311 -0
  283. data/spec/lib/validation_error_codes_spec.rb +56 -0
  284. data/spec/misc/factories_spec.rb +43 -0
  285. data/spec/models/settings_spec.rb +58 -0
  286. data/spec/routing/route_mapper_spec.rb +185 -0
  287. data/spec/rspec_helpers/test_helpers_spec.rb +20 -0
  288. metadata +657 -0
@@ -0,0 +1,102 @@
1
+ module Releaf::Core
2
+ # Utility class for array items reordering.
3
+ #
4
+ # There are 4 possible reordering options: <code>:first, :last, :before and :after</code>.
5
+ #
6
+ # All reorder methods are chainable, so:
7
+ # Releaf::Core::ItemOrderer.new(:a, :b, :c, :d, :e, :f).reorder(:c, :last).reorder([:a, :b], after: :d).result
8
+ # will return
9
+ # [:d, :a, :b, :e, :f, :c]
10
+ #
11
+ # For reordering with <code>`first`</code> and <code>`last`</code> options you must give this option as symbols.
12
+ # Example:
13
+ # Releaf::Core::ItemOrderer.new(:a, :b, :c).reorder(:c, :first)
14
+ #
15
+ # For reorderind with <code>`before`</code> and <code>`after`</code> options you must give option together with target value as hash.
16
+ # Example:
17
+ # Releaf::Core::ItemOrderer.new(:a, :b, :c).reorder(:a, after: :b)
18
+ class ItemOrderer
19
+
20
+ attr_accessor :list
21
+
22
+ def initialize(*args)
23
+ self.list = args
24
+ end
25
+
26
+ def result
27
+ list
28
+ end
29
+
30
+ def to_a
31
+ result
32
+ end
33
+
34
+ def reorder(values, options)
35
+ values = [values] unless values.is_a? Array
36
+ deleted_values = delete(values)
37
+ index = reorder_index(options)
38
+
39
+ values.reverse.each do|value|
40
+ list.insert(index, deleted_values[value])
41
+ end
42
+
43
+ self
44
+ end
45
+
46
+ def reorder_index(options)
47
+ if options == :first
48
+ index = 0
49
+ elsif options == :last
50
+ index = list.length
51
+ elsif options[:after]
52
+ index = index(options[:after]) + 1
53
+ elsif options[:before]
54
+ index = index(options[:before])
55
+ else
56
+ raise ArgumentError, "unknown reorder option"
57
+ end
58
+ end
59
+
60
+ def delete(values)
61
+ deleted = {}
62
+
63
+ values.each do|value|
64
+ index = index(value)
65
+ deleted[value] = list[index]
66
+ list.delete_at(index)
67
+ end
68
+
69
+ deleted
70
+ end
71
+
72
+ def index(value)
73
+ value = value.to_s
74
+ list.index do |existing_value|
75
+ if existing_value.is_a? Hash
76
+ existing_value.keys.first.to_s == value
77
+ else
78
+ existing_value.to_s == value
79
+ end
80
+ end
81
+ end
82
+
83
+ # Shortcut for creating new ItemOrderer class, ordering and result retrieving.
84
+ #
85
+ # Releaf::Core::ItemOrderer.reorder([:a, :b, :c, :d, :e, :f], c: :last, [:a, :b] => {after: :d})
86
+ # is same as
87
+ # Releaf::Core::ItemOrderer.new(:a, :b, :c, :d, :e, :f).reorder(:c, :last).reorder([:a, :b], after: :d).result
88
+ #
89
+ # @param list [Array] array to reorder
90
+ # @param options_list [Hash] hash with reorder options
91
+ # @return [Array] reordered array
92
+ def self.reorder(list, options_list)
93
+ orderer = new(*list)
94
+
95
+ options_list.each_pair do|values, options|
96
+ orderer.reorder(values, options)
97
+ end
98
+
99
+ orderer.result
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,66 @@
1
+ class Releaf::Core::ResourceBase
2
+ attr_accessor :resource_class
3
+
4
+ def initialize(resource_class)
5
+ self.resource_class = resource_class
6
+ end
7
+
8
+ def excluded_attributes
9
+ %w{id created_at updated_at}
10
+ end
11
+
12
+ def localized_attributes?
13
+ resource_class.translates?
14
+ end
15
+
16
+ def localized_attributes
17
+ resource_class.translated_attribute_names.collect { |a| a.to_s }
18
+ end
19
+
20
+ def base_attributes
21
+ resource_class.column_names - excluded_attributes
22
+ end
23
+
24
+ def values(include_associations: true)
25
+ list = base_attributes
26
+ list += localized_attributes if localized_attributes?
27
+ list += associations_attributes if include_associations
28
+ list
29
+ end
30
+
31
+
32
+ def associations_attributes
33
+ associations.collect do |association|
34
+ {association.name => association_attributes(association)}
35
+ end
36
+ end
37
+
38
+ def association_attributes(association)
39
+ self.class.new(association.klass).values - association_excluded_attributes(association)
40
+ end
41
+
42
+ def association_excluded_attributes(association)
43
+ [association.foreign_key, association.type].compact.map(&:to_s)
44
+ end
45
+
46
+ def associations
47
+ resource_class.reflect_on_all_associations.collect do |association|
48
+ association if includable_association?(association)
49
+ end.compact
50
+ end
51
+
52
+ def includable_association?(association)
53
+ includable_association_types.include?(association.macro) &&
54
+ excluded_associations.exclude?(association.name) &&
55
+ association.class != ActiveRecord::Reflection::ThroughReflection &&
56
+ resource_class.nested_attributes_options.has_key?(association.name)
57
+ end
58
+
59
+ def includable_association_types
60
+ [:has_many, :has_one]
61
+ end
62
+
63
+ def excluded_associations
64
+ [:translations]
65
+ end
66
+ end
@@ -0,0 +1,6 @@
1
+ class Releaf::Core::ResourceFields < Releaf::Core::ResourceBase
2
+
3
+ def excluded_attributes
4
+ super + %w(password password_confirmation encrypted_password)
5
+ end
6
+ end
@@ -0,0 +1,47 @@
1
+ class Releaf::Core::ResourceParams < Releaf::Core::ResourceBase
2
+
3
+ def file_attributes
4
+ resource_class.dragonfly_attachment_classes.collect{|c| "#{c.attribute}_uid" }
5
+ end
6
+
7
+ def file_attribute?(column)
8
+ file_attributes.include? column
9
+ end
10
+
11
+ def file_attribute_params(column)
12
+ file_field = column.sub(/_uid$/, "")
13
+ [file_field, "retained_#{file_field}", "remove_#{file_field}"]
14
+ end
15
+
16
+ def base_attributes
17
+ super.inject([]) do|list, attribute|
18
+ if file_attribute?(attribute)
19
+ list += file_attribute_params(attribute)
20
+ else
21
+ list << attribute
22
+ end
23
+ end
24
+ end
25
+
26
+ def localized_attributes
27
+ super.inject([]) do |list, column|
28
+ list += localized_attribute_params(column)
29
+ end
30
+ end
31
+
32
+ def localized_attribute_params(column)
33
+ resource_class.globalize_locales.collect do|locale|
34
+ "#{column}_#{locale}"
35
+ end
36
+ end
37
+
38
+ def associations_attributes
39
+ associations.collect do |association|
40
+ {"#{association.name}_attributes" => association_attributes(association)}
41
+ end
42
+ end
43
+
44
+ def association_attributes(association)
45
+ super + ["id", "_destroy"]
46
+ end
47
+ end
@@ -0,0 +1,10 @@
1
+ class Releaf::Core::ResourceTableFields < Releaf::Core::ResourceFields
2
+
3
+ def excluded_attributes
4
+ super + table_excluded_attributes
5
+ end
6
+
7
+ def table_excluded_attributes
8
+ resource_class.column_names.select{|c| c.match(/.*_html$/) }
9
+ end
10
+ end
@@ -0,0 +1,36 @@
1
+ class Releaf::Core::ResourceUtilities
2
+ # Lists relations for @resource with dependent: :restrict_with_exception
3
+ #
4
+ # @return hash of all related objects, who have dependancy :restrict_with_exception
5
+ def self.restricted_relations(resource)
6
+ restricted_associations(resource).inject({}) do|relations, association|
7
+ relations[association.name.to_sym] = {
8
+ objects: resource.send(association.name),
9
+ controller: association_controller(association)
10
+ }
11
+ relations
12
+ end
13
+ end
14
+
15
+ def self.restricted_associations(resource)
16
+ resource.class.reflect_on_all_associations.select do |association|
17
+ restricted_association?(resource, association)
18
+ end
19
+ end
20
+
21
+ def self.restricted_association?(resource, association)
22
+ association.options[:dependent] == :restrict_with_exception && resource.send(association.name).exists?
23
+ end
24
+
25
+ # Attempts to guess associated controllers name
26
+ #
27
+ # @return controller name
28
+ def self.association_controller(association)
29
+ guessed_name = association.name.to_s.pluralize
30
+ guessed_name if Releaf.application.config.controllers.values.map { |v| v[:controller] }.grep(/(\/#{guessed_name}$|^#{guessed_name}$)/).present?
31
+ end
32
+
33
+ def self.destroyable?(resource)
34
+ restricted_associations(resource).empty?
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ module Releaf::Core::Responders
2
+ class AccessDeniedResponder < ActionController::Responder
3
+ include Releaf::Core::Responders::ErrorResponder
4
+
5
+ def status_code
6
+ 403
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ module Releaf::Core::Responders
2
+ class AfterSaveResponder < ActionController::Responder
3
+ delegate :render_notification, to: :controller
4
+
5
+ def json_resource_errors
6
+ {errors: Releaf::Core::ErrorFormatter.format_errors(resource)}
7
+ end
8
+
9
+ def to_json
10
+ if has_errors?
11
+ display_errors
12
+ elsif options[:redirect]
13
+ render json: {url: resource_location}, status: 303
14
+ else
15
+ redirect_to resource_location, status: 303
16
+ end
17
+ end
18
+
19
+ def respond
20
+ render_notification(!has_errors?) if render_notification?
21
+ super
22
+ end
23
+
24
+ def render_notification?
25
+ !(format == :json && has_errors?)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ module Releaf::Core::Responders
2
+ class ConfirmDestroyResponder < ActionController::Responder
3
+ delegate :render_notification, to: :controller
4
+
5
+ def to_html
6
+ if options[:destroyable]
7
+ super
8
+ else
9
+ render 'refused_destroy'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Releaf::Core::Responders
2
+ class DestroyResponder < ActionController::Responder
3
+ delegate :render_notification, to: :controller
4
+
5
+ def to_html
6
+ render_notification(@resource.destroyed?, failure_message_key: 'cant destroy, because relations exists')
7
+ super
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module Releaf::Core::Responders::ErrorResponder
2
+ def template
3
+ self.class.name.split("::").last.gsub(/Responder$/, "").underscore
4
+ end
5
+
6
+ def to_html
7
+ render "releaf/error_pages/#{template}", status: status_code
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Releaf::Core::Responders
2
+ class FeatureDisabledResponder < ActionController::Responder
3
+ include Releaf::Core::Responders::ErrorResponder
4
+
5
+ def status_code
6
+ 403
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Releaf::Core::Responders
2
+ class PageNotFoundResponder < ActionController::Responder
3
+ include Releaf::Core::Responders::ErrorResponder
4
+
5
+ def status_code
6
+ 404
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ module Releaf::Core::Responders
2
+
3
+ def respond_with(resource = nil, options = {}, &block)
4
+ options[:responder] = active_responder unless options.has_key? :responder
5
+ super
6
+ end
7
+
8
+ def action_responders
9
+ {
10
+ create: Releaf::Core::Responders::AfterSaveResponder,
11
+ update: Releaf::Core::Responders::AfterSaveResponder,
12
+ confirm_destroy: Releaf::Core::Responders::ConfirmDestroyResponder,
13
+ destroy: Releaf::Core::Responders::DestroyResponder,
14
+ access_denied: Releaf::Core::Responders::AccessDeniedResponder,
15
+ feature_disabled: Releaf::Core::Responders::FeatureDisabledResponder,
16
+ page_not_found: Releaf::Core::Responders::PageNotFoundResponder,
17
+ }
18
+ end
19
+
20
+ # Returns generic view name for given action
21
+ # @return String
22
+ def action_responder(_action_name)
23
+ action_responders[_action_name.to_sym]
24
+ end
25
+
26
+ # Returns generic view name for current action
27
+ # @return String
28
+ def active_responder
29
+ action_responder(action_name)
30
+ end
31
+ end
@@ -0,0 +1,147 @@
1
+ module Releaf::Core
2
+ class Search
3
+ attr_accessor :relation, :fields, :text, :join_index, :searchable_arel_fields
4
+
5
+ delegate :base_class, to: :relation
6
+
7
+ def self.prepare(relation:, fields:, text:)
8
+ searcher = new(relation: relation, fields: fields, text: text)
9
+ searcher.prepare
10
+ searcher.relation
11
+ end
12
+
13
+ def initialize(relation: , fields:, text:)
14
+ self.relation = relation.all
15
+ self.fields = fields
16
+ self.text = text
17
+ end
18
+
19
+ def prepare
20
+ self.join_index = 0
21
+ self.searchable_arel_fields = []
22
+
23
+ join_search_tables(base_class)
24
+ add_search_to_relation
25
+ end
26
+
27
+ private
28
+
29
+ def join_search_tables klass, attributes: fields, table: klass.arel_table
30
+ attributes.each do |attribute|
31
+ if attribute.is_a? Hash
32
+ attribute.each_pair do |key, values|
33
+ reflection = klass.reflect_on_association(key.to_sym)
34
+ joined_table = join_reflection(reflection, table)
35
+ join_search_tables(reflection.klass, attributes: values, table: joined_table)
36
+ end
37
+ elsif attribute.is_a?(Symbol) || attribute.is_a?(String)
38
+ self.searchable_arel_fields << table[attribute]
39
+ else
40
+ raise 'not implemented'
41
+ end
42
+ end
43
+ end
44
+
45
+ def add_search_to_relation
46
+ text.strip.split(" ").each do |word|
47
+ query = searchable_arel_fields.map do |field|
48
+ field.matches(Arel::Nodes::Quoted.new("%#{word}%"))
49
+ end.inject { |result, query_part| result.or(query_part) }
50
+
51
+ self.relation = relation.where(query)
52
+ end
53
+ end
54
+
55
+ def join_reflection(reflection, table)
56
+ if reflection.options[:through]
57
+ join_reflection_with_through(reflection, table)
58
+ else
59
+ join_reflection_without_through(reflection, table)
60
+ end
61
+ end
62
+
63
+ def join_reflection_without_through(reflection, table)
64
+ klass = reflection.active_record
65
+ other_class = reflection.klass
66
+
67
+ table1 = table || klass.arel_table
68
+ table2_alias = "#{other_class.arel_table.name}_f#{join_index}"
69
+ table2 = other_class.arel_table.alias(table2_alias)
70
+ self.join_index += 1
71
+
72
+ foreign_key = reflection.foreign_key.to_sym
73
+ primary_key = klass.primary_key.to_sym
74
+
75
+ join_condition = case reflection.macro
76
+ when :has_many
77
+ self.relation = relation.uniq
78
+ table1[primary_key].eq(table2[foreign_key])
79
+ when :has_one
80
+ table1[primary_key].eq(table2[foreign_key])
81
+ when :belongs_to
82
+ table1[foreign_key].eq(table2[primary_key])
83
+ else
84
+ raise 'not implemented'
85
+ end
86
+
87
+ if reflection.type
88
+ polymorphic_type_condition = table2[reflection.type.to_sym].eq(klass.base_class.name)
89
+ join_condition = join_condition.and(polymorphic_type_condition)
90
+ end
91
+
92
+ if reflection.scope
93
+ where_scope = extract_where_condtion_from_scope(reflection, table2_alias)
94
+ join_condition = join_condition.and(where_scope) if where_scope.present?
95
+ end
96
+
97
+ if other_class.ancestors.include?(Globalize::ActiveRecord::Translation)
98
+ # only search in current locale
99
+ join_condition = join_condition.and(table2[:locale].eq(I18n.locale.to_s))
100
+ end
101
+
102
+ self.relation = relation.joins(arel_join(table1, table2, join_condition))
103
+ table2
104
+ end
105
+
106
+ def extract_where_condtion_from_scope(reflection, table_alias)
107
+ # XXX Hack based on ActiveRecord::Relation#to_sql
108
+ tmp_relation = build_tmp_relation(reflection, table_alias)
109
+
110
+ where_scope = tmp_relation.where_values
111
+
112
+ return nil if where_scope.blank?
113
+
114
+ connection = tmp_relation.klass.connection
115
+ visitor = connection.visitor
116
+
117
+ arel = tmp_relation.arel
118
+ binds = (arel.bind_values + tmp_relation.bind_values).dup
119
+ binds.map! { |bv| connection.quote(*bv.reverse) }
120
+
121
+ wheres = tmp_relation.arel.ast.cores.first.wheres
122
+ collect = visitor.accept(wheres, Arel::Collectors::Bind.new)
123
+ sql = collect.compile(binds)
124
+ Arel::Nodes::SqlLiteral.new(sql)
125
+ end
126
+
127
+ def build_tmp_relation(reflection, table_alias)
128
+ # Need to create tmp relation since setting table alias for actual klass
129
+ # is a bad idea.
130
+ klass = Class.new(reflection.klass) do
131
+ self.arel_table.table_alias = table_alias
132
+ end
133
+
134
+ klass.instance_exec(&reflection.scope)
135
+ end
136
+
137
+ def join_reflection_with_through(reflection, table)
138
+ joined_table = join_reflection_without_through(reflection.through_reflection, table)
139
+ join_reflection_without_through(reflection.source_reflection, joined_table)
140
+ end
141
+
142
+ def arel_join(table1, table2, join_condition, join_type: Arel::Nodes::OuterJoin)
143
+ on_condition = table1.create_on(join_condition)
144
+ table1.create_join(table2, on_condition, join_type)
145
+ end
146
+ end
147
+ end