wallaby 5.0.1 → 5.1.3

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 (350) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -3
  3. data/app/assets/images/wallaby/404.png +0 -0
  4. data/app/assets/images/wallaby/422.png +0 -0
  5. data/app/assets/images/wallaby/500.png +0 -0
  6. data/app/assets/javascripts/wallaby/application.js +1 -15
  7. data/app/assets/javascripts/wallaby/auto_select.js +77 -0
  8. data/app/assets/javascripts/wallaby/base.js +17 -0
  9. data/app/assets/javascripts/wallaby/setup.js.erb +60 -0
  10. data/app/assets/stylesheets/wallaby/application.scss +1 -51
  11. data/app/assets/stylesheets/wallaby/base/_layout.scss +257 -9
  12. data/app/assets/stylesheets/wallaby/base/_layout_desktop.scss +53 -0
  13. data/app/assets/stylesheets/wallaby/base/_mixins.scss +119 -0
  14. data/app/assets/stylesheets/wallaby/base/_override.scss +10 -0
  15. data/app/assets/stylesheets/wallaby/base/_vars.scss +72 -2
  16. data/app/assets/stylesheets/wallaby/base.scss +27 -0
  17. data/app/assets/stylesheets/wallaby/components/_auto_select.scss +119 -0
  18. data/app/assets/stylesheets/wallaby/components/_auto_select_desktop.scss +7 -0
  19. data/app/assets/stylesheets/wallaby/components/_color_square.scss +8 -0
  20. data/app/assets/stylesheets/wallaby/components/_filters.scss +14 -0
  21. data/app/assets/stylesheets/wallaby/components/_links.scss +25 -0
  22. data/app/assets/stylesheets/wallaby/components/_pagination.scss +35 -91
  23. data/app/assets/stylesheets/wallaby/components/_query.scss +101 -0
  24. data/app/assets/stylesheets/wallaby/components/_text.scss +6 -6
  25. data/app/assets/stylesheets/wallaby/pages/_error.scss +108 -0
  26. data/app/assets/stylesheets/wallaby/pages/_form.scss +40 -0
  27. data/app/assets/stylesheets/wallaby/pages/_index.scss +193 -0
  28. data/app/assets/stylesheets/wallaby/pages/_index_desktop.scss +22 -0
  29. data/app/assets/stylesheets/wallaby/pages/_show.scss +21 -0
  30. data/app/controllers/wallaby/abstract_resources_controller.rb +334 -0
  31. data/app/controllers/wallaby/application_controller.rb +45 -15
  32. data/app/controllers/wallaby/base_controller.rb +30 -0
  33. data/app/controllers/wallaby/resources_controller.rb +2 -131
  34. data/app/controllers/wallaby/secure_controller.rb +38 -24
  35. data/app/routes/wallaby/resources_router.rb +40 -43
  36. data/app/security/ability.rb +4 -28
  37. data/app/views/layouts/wallaby/application.html.erb +11 -6
  38. data/app/views/layouts/wallaby/error.html.erb +26 -4
  39. data/app/views/wallaby/error.html.erb +11 -0
  40. data/app/views/wallaby/error.json.jbuilder +8 -0
  41. data/app/views/wallaby/{shared → resources}/_flash_messages.html.erb +0 -0
  42. data/app/views/wallaby/resources/_footer.html.erb +1 -0
  43. data/app/views/wallaby/resources/_form.html.erb +2 -2
  44. data/app/views/wallaby/resources/_header.html.erb +38 -0
  45. data/app/views/wallaby/resources/_imodal.html.erb +13 -0
  46. data/app/views/wallaby/resources/_index_actions.html.erb +4 -1
  47. data/app/views/wallaby/resources/_index_filters.html.erb +23 -0
  48. data/app/views/wallaby/resources/_index_pagination.html.erb +63 -0
  49. data/app/views/wallaby/resources/_index_query.html.erb +23 -0
  50. data/app/views/wallaby/resources/_logo.html.erb +1 -0
  51. data/app/views/wallaby/resources/_navs.html.erb +9 -0
  52. data/app/views/wallaby/resources/_resource_actions.html.erb +3 -3
  53. data/app/views/wallaby/resources/{shared/_resource_nav.html.erb → _resource_navs.html.erb} +6 -6
  54. data/app/views/wallaby/resources/_title.html.erb +1 -0
  55. data/app/views/wallaby/resources/_user_menu.html.erb +5 -0
  56. data/app/views/wallaby/resources/bad_request.json.jbuilder +7 -0
  57. data/app/views/wallaby/resources/edit.html.erb +16 -12
  58. data/app/views/wallaby/resources/form/_belongs_to.html.erb +63 -12
  59. data/app/views/wallaby/resources/form/_bigint.html.erb +10 -1
  60. data/app/views/wallaby/resources/form/_bigserial.html.erb +18 -0
  61. data/app/views/wallaby/resources/form/_binary.html.erb +11 -2
  62. data/app/views/wallaby/resources/form/_bit.html.erb +10 -1
  63. data/app/views/wallaby/resources/form/_bit_varying.html.erb +10 -1
  64. data/app/views/wallaby/resources/form/_blob.html.erb +21 -0
  65. data/app/views/wallaby/resources/form/_boolean.html.erb +9 -0
  66. data/app/views/wallaby/resources/form/_box.html.erb +21 -0
  67. data/app/views/wallaby/resources/form/_cidr.html.erb +13 -1
  68. data/app/views/wallaby/resources/form/_circle.html.erb +21 -0
  69. data/app/views/wallaby/resources/form/_citext.html.erb +11 -2
  70. data/app/views/wallaby/resources/form/_color.html.erb +11 -2
  71. data/app/views/wallaby/resources/form/_date.html.erb +12 -3
  72. data/app/views/wallaby/resources/form/_daterange.html.erb +14 -5
  73. data/app/views/wallaby/resources/form/_datetime.html.erb +12 -3
  74. data/app/views/wallaby/resources/form/_decimal.html.erb +10 -1
  75. data/app/views/wallaby/resources/form/_dropdown.html.erb +18 -0
  76. data/app/views/wallaby/resources/form/_email.html.erb +9 -0
  77. data/app/views/wallaby/resources/form/_file.html.erb +21 -0
  78. data/app/views/wallaby/resources/form/_float.html.erb +10 -1
  79. data/app/views/wallaby/resources/form/_has_and_belongs_to_many.html.erb +53 -7
  80. data/app/views/wallaby/resources/form/_has_many.html.erb +52 -7
  81. data/app/views/wallaby/resources/form/_has_one.html.erb +10 -2
  82. data/app/views/wallaby/resources/form/_hstore.html.erb +12 -5
  83. data/app/views/wallaby/resources/form/_inet.html.erb +13 -1
  84. data/app/views/wallaby/resources/form/_int4range.html.erb +11 -2
  85. data/app/views/wallaby/resources/form/_int8range.html.erb +11 -2
  86. data/app/views/wallaby/resources/form/_integer.html.erb +10 -1
  87. data/app/views/wallaby/resources/form/_json.html.erb +13 -6
  88. data/app/views/wallaby/resources/form/_jsonb.html.erb +13 -6
  89. data/app/views/wallaby/resources/form/_line.html.erb +21 -0
  90. data/app/views/wallaby/resources/form/_longblob.html.erb +21 -0
  91. data/app/views/wallaby/resources/form/_longtext.html.erb +30 -0
  92. data/app/views/wallaby/resources/form/_lseg.html.erb +21 -0
  93. data/app/views/wallaby/resources/form/_ltree.html.erb +17 -1
  94. data/app/views/wallaby/resources/form/_macaddr.html.erb +13 -1
  95. data/app/views/wallaby/resources/form/_markdown.html.erb +29 -0
  96. data/app/views/wallaby/resources/form/_mediumblob.html.erb +21 -0
  97. data/app/views/wallaby/resources/form/_mediumtext.html.erb +30 -0
  98. data/app/views/wallaby/resources/form/_money.html.erb +10 -1
  99. data/app/views/wallaby/resources/form/_numrange.html.erb +11 -2
  100. data/app/views/wallaby/resources/form/_password.html.erb +14 -1
  101. data/app/views/wallaby/resources/form/_path.html.erb +17 -0
  102. data/app/views/wallaby/resources/form/_point.html.erb +11 -2
  103. data/app/views/wallaby/resources/form/_polygon.html.erb +17 -0
  104. data/app/views/wallaby/resources/form/_serial.html.erb +18 -0
  105. data/app/views/wallaby/resources/form/_sti.html.erb +18 -0
  106. data/app/views/wallaby/resources/form/_string.html.erb +14 -1
  107. data/app/views/wallaby/resources/form/_text.html.erb +11 -2
  108. data/app/views/wallaby/resources/form/_time.html.erb +12 -3
  109. data/app/views/wallaby/resources/form/_tinyblob.html.erb +21 -0
  110. data/app/views/wallaby/resources/form/_tinytext.html.erb +30 -0
  111. data/app/views/wallaby/resources/form/_tsrange.html.erb +14 -5
  112. data/app/views/wallaby/resources/form/_tstzrange.html.erb +14 -5
  113. data/app/views/wallaby/resources/form/_tsvector.html.erb +9 -0
  114. data/app/views/wallaby/resources/form/_unsigned_bigint.html.erb +18 -0
  115. data/app/views/wallaby/resources/form/_unsigned_decimal.html.erb +18 -0
  116. data/app/views/wallaby/resources/form/_unsigned_float.html.erb +18 -0
  117. data/app/views/wallaby/resources/form/_unsigned_integer.html.erb +18 -0
  118. data/app/views/wallaby/resources/form/_uuid.html.erb +10 -1
  119. data/app/views/wallaby/resources/form/_xml.html.erb +11 -5
  120. data/app/views/wallaby/resources/form.json.jbuilder +5 -0
  121. data/app/views/wallaby/{core → resources}/home.html.erb +1 -1
  122. data/app/views/wallaby/resources/index/_belongs_to.html.erb +5 -1
  123. data/app/views/wallaby/resources/index/_bigint.html.erb +4 -0
  124. data/app/views/wallaby/resources/index/_bigserial.html.erb +5 -0
  125. data/app/views/wallaby/resources/index/_binary.html.erb +5 -1
  126. data/app/views/wallaby/resources/index/_bit.html.erb +4 -0
  127. data/app/views/wallaby/resources/index/_bit_varying.html.erb +4 -0
  128. data/app/views/wallaby/resources/index/_blob.html.erb +5 -0
  129. data/app/views/wallaby/resources/index/_boolean.html.erb +5 -1
  130. data/app/views/wallaby/resources/index/_box.html.erb +16 -0
  131. data/app/views/wallaby/resources/index/_cidr.html.erb +5 -1
  132. data/app/views/wallaby/resources/index/_circle.html.erb +16 -0
  133. data/app/views/wallaby/resources/index/_citext.html.erb +5 -1
  134. data/app/views/wallaby/resources/index/_color.html.erb +6 -1
  135. data/app/views/wallaby/resources/index/_date.html.erb +4 -0
  136. data/app/views/wallaby/resources/index/_daterange.html.erb +5 -1
  137. data/app/views/wallaby/resources/index/_datetime.html.erb +5 -1
  138. data/app/views/wallaby/resources/index/_decimal.html.erb +4 -0
  139. data/app/views/wallaby/resources/index/_email.html.erb +4 -0
  140. data/app/views/wallaby/resources/index/_float.html.erb +4 -0
  141. data/app/views/wallaby/resources/index/_has_and_belongs_to_many.html.erb +6 -2
  142. data/app/views/wallaby/resources/index/_has_many.html.erb +6 -2
  143. data/app/views/wallaby/resources/index/_has_one.html.erb +5 -1
  144. data/app/views/wallaby/resources/index/_hstore.html.erb +6 -2
  145. data/app/views/wallaby/resources/index/_inet.html.erb +5 -1
  146. data/app/views/wallaby/resources/index/_int4range.html.erb +4 -0
  147. data/app/views/wallaby/resources/index/_int8range.html.erb +4 -0
  148. data/app/views/wallaby/resources/index/_integer.html.erb +4 -0
  149. data/app/views/wallaby/resources/index/_json.html.erb +5 -2
  150. data/app/views/wallaby/resources/index/_jsonb.html.erb +5 -2
  151. data/app/views/wallaby/resources/index/_line.html.erb +16 -0
  152. data/app/views/wallaby/resources/index/_longblob.html.erb +5 -0
  153. data/app/views/wallaby/resources/index/_longtext.html.erb +16 -0
  154. data/app/views/wallaby/resources/index/_lseg.html.erb +16 -0
  155. data/app/views/wallaby/resources/index/_ltree.html.erb +5 -1
  156. data/app/views/wallaby/resources/index/_macaddr.html.erb +4 -0
  157. data/app/views/wallaby/resources/index/_mediumblob.html.erb +5 -0
  158. data/app/views/wallaby/resources/index/_mediumtext.html.erb +16 -0
  159. data/app/views/wallaby/resources/index/_money.html.erb +4 -0
  160. data/app/views/wallaby/resources/index/_numrange.html.erb +4 -0
  161. data/app/views/wallaby/resources/index/_password.html.erb +9 -0
  162. data/app/views/wallaby/resources/index/_path.html.erb +16 -0
  163. data/app/views/wallaby/resources/index/_point.html.erb +4 -0
  164. data/app/views/wallaby/resources/index/_polygon.html.erb +16 -0
  165. data/app/views/wallaby/resources/index/_raw.html.erb +9 -0
  166. data/app/views/wallaby/resources/index/_serial.html.erb +5 -0
  167. data/app/views/wallaby/resources/index/_sti.html.erb +16 -0
  168. data/app/views/wallaby/resources/index/_string.html.erb +5 -1
  169. data/app/views/wallaby/resources/index/_text.html.erb +5 -1
  170. data/app/views/wallaby/resources/index/_time.html.erb +4 -0
  171. data/app/views/wallaby/resources/index/_tinyblob.html.erb +5 -0
  172. data/app/views/wallaby/resources/index/_tinytext.html.erb +16 -0
  173. data/app/views/wallaby/resources/index/_tsrange.html.erb +5 -1
  174. data/app/views/wallaby/resources/index/_tstzrange.html.erb +5 -1
  175. data/app/views/wallaby/resources/index/_tsvector.html.erb +5 -1
  176. data/app/views/wallaby/resources/index/_unsigned_bigint.html.erb +5 -0
  177. data/app/views/wallaby/resources/index/_unsigned_decimal.html.erb +5 -0
  178. data/app/views/wallaby/resources/index/_unsigned_float.html.erb +5 -0
  179. data/app/views/wallaby/resources/index/_unsigned_integer.html.erb +5 -0
  180. data/app/views/wallaby/resources/index/_uuid.html.erb +5 -1
  181. data/app/views/wallaby/resources/index/_xml.html.erb +5 -1
  182. data/app/views/wallaby/resources/index.csv.erb +26 -0
  183. data/app/views/wallaby/resources/index.html.erb +47 -59
  184. data/app/views/wallaby/resources/index.json.jbuilder +5 -0
  185. data/app/views/wallaby/resources/new.html.erb +12 -11
  186. data/app/views/wallaby/resources/show/_belongs_to.html.erb +5 -1
  187. data/app/views/wallaby/resources/show/_bigint.html.erb +4 -0
  188. data/app/views/wallaby/resources/show/_bigserial.html.erb +5 -0
  189. data/app/views/wallaby/resources/show/_binary.html.erb +5 -1
  190. data/app/views/wallaby/resources/show/_bit.html.erb +4 -0
  191. data/app/views/wallaby/resources/show/_bit_varying.html.erb +4 -0
  192. data/app/views/wallaby/resources/show/_blob.html.erb +5 -0
  193. data/app/views/wallaby/resources/show/_boolean.html.erb +5 -1
  194. data/app/views/wallaby/resources/show/_box.html.erb +9 -0
  195. data/app/views/wallaby/resources/show/_cidr.html.erb +5 -1
  196. data/app/views/wallaby/resources/show/_circle.html.erb +9 -0
  197. data/app/views/wallaby/resources/show/_citext.html.erb +4 -0
  198. data/app/views/wallaby/resources/show/_color.html.erb +6 -1
  199. data/app/views/wallaby/resources/show/_date.html.erb +4 -0
  200. data/app/views/wallaby/resources/show/_daterange.html.erb +4 -0
  201. data/app/views/wallaby/resources/show/_datetime.html.erb +4 -0
  202. data/app/views/wallaby/resources/show/_decimal.html.erb +4 -0
  203. data/app/views/wallaby/resources/show/_email.html.erb +4 -0
  204. data/app/views/wallaby/resources/show/_float.html.erb +4 -0
  205. data/app/views/wallaby/resources/show/_has_and_belongs_to_many.html.erb +6 -2
  206. data/app/views/wallaby/resources/show/_has_many.html.erb +6 -2
  207. data/app/views/wallaby/resources/show/_has_one.html.erb +5 -1
  208. data/app/views/wallaby/resources/show/_hstore.html.erb +4 -0
  209. data/app/views/wallaby/resources/show/_image.html.erb +9 -0
  210. data/app/views/wallaby/resources/show/_inet.html.erb +5 -1
  211. data/app/views/wallaby/resources/show/_int4range.html.erb +4 -0
  212. data/app/views/wallaby/resources/show/_int8range.html.erb +4 -0
  213. data/app/views/wallaby/resources/show/_integer.html.erb +4 -0
  214. data/app/views/wallaby/resources/show/_json.html.erb +4 -0
  215. data/app/views/wallaby/resources/show/_jsonb.html.erb +4 -0
  216. data/app/views/wallaby/resources/show/_line.html.erb +9 -0
  217. data/app/views/wallaby/resources/show/_longblob.html.erb +5 -0
  218. data/app/views/wallaby/resources/show/_longtext.html.erb +5 -0
  219. data/app/views/wallaby/resources/show/_lseg.html.erb +9 -0
  220. data/app/views/wallaby/resources/show/_ltree.html.erb +4 -0
  221. data/app/views/wallaby/resources/show/_macaddr.html.erb +4 -0
  222. data/app/views/wallaby/resources/show/_mediumblob.html.erb +5 -0
  223. data/app/views/wallaby/resources/show/_mediumtext.html.erb +5 -0
  224. data/app/views/wallaby/resources/show/_money.html.erb +4 -0
  225. data/app/views/wallaby/resources/show/_numrange.html.erb +4 -0
  226. data/app/views/wallaby/resources/show/_password.html.erb +9 -0
  227. data/app/views/wallaby/resources/show/_path.html.erb +9 -0
  228. data/app/views/wallaby/resources/show/_point.html.erb +4 -0
  229. data/app/views/wallaby/resources/show/_polygon.html.erb +9 -0
  230. data/app/views/wallaby/resources/show/_raw.html.erb +9 -0
  231. data/app/views/wallaby/resources/show/_serial.html.erb +5 -0
  232. data/app/views/wallaby/resources/show/_sti.html.erb +5 -0
  233. data/app/views/wallaby/resources/show/_string.html.erb +4 -0
  234. data/app/views/wallaby/resources/show/_text.html.erb +4 -0
  235. data/app/views/wallaby/resources/show/_time.html.erb +4 -0
  236. data/app/views/wallaby/resources/show/_tinyblob.html.erb +5 -0
  237. data/app/views/wallaby/resources/show/_tinytext.html.erb +5 -0
  238. data/app/views/wallaby/resources/show/_tsrange.html.erb +4 -0
  239. data/app/views/wallaby/resources/show/_tstzrange.html.erb +4 -0
  240. data/app/views/wallaby/resources/show/_tsvector.html.erb +4 -0
  241. data/app/views/wallaby/resources/show/_unsigned_bigint.html.erb +5 -0
  242. data/app/views/wallaby/resources/show/_unsigned_decimal.html.erb +5 -0
  243. data/app/views/wallaby/resources/show/_unsigned_float.html.erb +5 -0
  244. data/app/views/wallaby/resources/show/_unsigned_integer.html.erb +5 -0
  245. data/app/views/wallaby/resources/show/_uuid.html.erb +4 -0
  246. data/app/views/wallaby/resources/show/_xml.html.erb +4 -0
  247. data/app/views/wallaby/resources/show.html.erb +14 -24
  248. data/app/views/wallaby/resources/show.json.jbuilder +5 -0
  249. data/config/brakeman.ignore +26 -0
  250. data/config/locales/wallaby.en.yml +91 -0
  251. data/config/massa.yml +45 -0
  252. data/config/rails_best_practices.yml +42 -0
  253. data/config/routes.rb +29 -23
  254. data/lib/adaptors/wallaby/active_record/model_decorator/fields_builder/association_builder.rb +39 -0
  255. data/lib/adaptors/wallaby/active_record/model_decorator/fields_builder/polymorphic_builder.rb +44 -0
  256. data/lib/adaptors/wallaby/active_record/model_decorator/fields_builder/sti_builder.rb +42 -0
  257. data/lib/adaptors/wallaby/active_record/model_decorator/fields_builder.rb +41 -94
  258. data/lib/adaptors/wallaby/active_record/model_decorator/title_field_finder.rb +24 -14
  259. data/lib/adaptors/wallaby/active_record/model_decorator.rb +118 -64
  260. data/lib/adaptors/wallaby/active_record/model_finder.rb +44 -15
  261. data/lib/adaptors/wallaby/active_record/model_pagination_provider.rb +30 -0
  262. data/lib/adaptors/wallaby/active_record/model_service_provider/normalizer.rb +42 -0
  263. data/lib/adaptors/wallaby/active_record/model_service_provider/permitter.rb +64 -0
  264. data/lib/adaptors/wallaby/active_record/model_service_provider/querier/transformer.rb +70 -0
  265. data/lib/adaptors/wallaby/active_record/model_service_provider/querier.rb +102 -0
  266. data/lib/adaptors/wallaby/active_record/model_service_provider/validator.rb +29 -0
  267. data/lib/adaptors/wallaby/active_record/model_service_provider.rb +137 -0
  268. data/lib/adaptors/wallaby/active_record.rb +4 -1
  269. data/lib/decorators/wallaby/abstract_resource_decorator.rb +74 -0
  270. data/lib/decorators/wallaby/resource_decorator.rb +5 -76
  271. data/lib/errors/wallaby/model_not_found.rb +5 -1
  272. data/lib/errors/wallaby/not_found.rb +4 -0
  273. data/lib/errors/wallaby/resource_not_found.rb +5 -1
  274. data/lib/forms/wallaby/form_builder.rb +42 -19
  275. data/lib/helpers/wallaby/application_helper.rb +34 -24
  276. data/lib/helpers/wallaby/base_helper.rb +52 -0
  277. data/lib/helpers/wallaby/form_helper.rb +40 -16
  278. data/lib/helpers/wallaby/index_helper.rb +59 -0
  279. data/lib/helpers/wallaby/links_helper.rb +150 -81
  280. data/lib/helpers/wallaby/resources_helper.rb +41 -46
  281. data/lib/helpers/wallaby/secure_helper.rb +36 -19
  282. data/lib/helpers/wallaby/styling_helper.rb +53 -44
  283. data/lib/interfaces/wallaby/mode.rb +19 -12
  284. data/lib/interfaces/wallaby/model_decorator.rb +176 -50
  285. data/lib/interfaces/wallaby/model_finder.rb +7 -3
  286. data/lib/interfaces/wallaby/model_pagination_provider.rb +104 -0
  287. data/lib/interfaces/wallaby/model_service_provider.rb +71 -0
  288. data/lib/paginators/wallaby/abstract_resource_paginator.rb +23 -0
  289. data/lib/paginators/wallaby/resource_paginator.rb +6 -0
  290. data/lib/parsers/wallaby/parser.rb +32 -0
  291. data/lib/responders/wallaby/abstract_responder.rb +68 -0
  292. data/lib/responders/wallaby/resources_responder.rb +6 -0
  293. data/lib/servicers/wallaby/abstract_model_servicer.rb +48 -0
  294. data/lib/servicers/wallaby/model_servicer.rb +5 -0
  295. data/lib/services/wallaby/link_options_normalizer.rb +16 -0
  296. data/lib/services/wallaby/lookup_context_wrapper.rb +23 -30
  297. data/lib/services/wallaby/map/mode_mapper.rb +19 -0
  298. data/lib/services/wallaby/map/model_class_collector.rb +41 -0
  299. data/lib/services/wallaby/map/model_class_mapper.rb +24 -0
  300. data/lib/services/wallaby/map.rb +76 -39
  301. data/lib/services/wallaby/partial_renderer.rb +60 -0
  302. data/lib/services/wallaby/prefixes_builder.rb +54 -0
  303. data/lib/services/wallaby/sorting/hash_builder.rb +16 -0
  304. data/lib/services/wallaby/sorting/link_builder.rb +45 -0
  305. data/lib/services/wallaby/sorting/next_builder.rb +54 -0
  306. data/lib/services/wallaby/url_for.rb +36 -0
  307. data/lib/tree/wallaby/node.rb +17 -0
  308. data/lib/utils/wallaby/utils.rb +67 -22
  309. data/lib/wallaby/configuration/features.rb +12 -0
  310. data/lib/wallaby/configuration/metadata.rb +12 -0
  311. data/lib/wallaby/configuration/models.rb +27 -12
  312. data/lib/wallaby/configuration/pagination.rb +12 -0
  313. data/lib/wallaby/configuration/security.rb +55 -24
  314. data/lib/wallaby/configuration.rb +38 -19
  315. data/lib/wallaby/engine.rb +84 -39
  316. data/lib/wallaby/version.rb +1 -1
  317. data/lib/wallaby.rb +22 -3
  318. metadata +200 -55
  319. data/app/assets/javascripts/wallaby/form.js +0 -8
  320. data/app/assets/stylesheets/wallaby/base/_functions.scss +0 -0
  321. data/app/assets/stylesheets/wallaby/base/_normalize.scss +0 -0
  322. data/app/assets/stylesheets/wallaby/base/_reset.scss +0 -0
  323. data/app/assets/stylesheets/wallaby/components/_forms.scss +0 -53
  324. data/app/assets/stylesheets/wallaby/components/_nav.scss +0 -19
  325. data/app/assets/stylesheets/wallaby/components/_tables.scss +0 -78
  326. data/app/assets/stylesheets/wallaby/form.scss +0 -11
  327. data/app/controllers/wallaby/core_controller.rb +0 -28
  328. data/app/views/layouts/wallaby/_footer.html.erb +0 -2
  329. data/app/views/layouts/wallaby/_header.html.erb +0 -57
  330. data/app/views/layouts/wallaby/_navs.html.erb +0 -1
  331. data/app/views/wallaby/errors/access_denied.html.erb +0 -1
  332. data/app/views/wallaby/errors/not_authenticated.html.erb +0 -1
  333. data/app/views/wallaby/errors/not_found.html.erb +0 -6
  334. data/app/views/wallaby/errors/unprocessable_entity.erb +0 -1
  335. data/config/locales/en.yml +0 -29
  336. data/lib/adaptors/wallaby/active_record/model_operator/normalizer.rb +0 -44
  337. data/lib/adaptors/wallaby/active_record/model_operator/permitter.rb +0 -50
  338. data/lib/adaptors/wallaby/active_record/model_operator/querier.rb +0 -47
  339. data/lib/adaptors/wallaby/active_record/model_operator/validator.rb +0 -16
  340. data/lib/adaptors/wallaby/active_record/model_operator.rb +0 -82
  341. data/lib/errors/wallaby/deprecated.rb +0 -4
  342. data/lib/errors/wallaby/operation_not_found.rb +0 -4
  343. data/lib/handlers/wallaby/cached_compiled_erb.rb +0 -14
  344. data/lib/helpers/wallaby/core_helper.rb +0 -50
  345. data/lib/helpers/wallaby/paginatable_helper.rb +0 -9
  346. data/lib/helpers/wallaby/sorting_helper.rb +0 -43
  347. data/lib/interfaces/wallaby/model_operator.rb +0 -31
  348. data/lib/services/wallaby/decorator_finder.rb +0 -16
  349. data/lib/services/wallaby/model_servicer.rb +0 -38
  350. data/lib/services/wallaby/servicer_finder.rb +0 -5
@@ -1,18 +1,28 @@
1
- class Wallaby::ActiveRecord::ModelDecorator::TitleFieldFinder
2
- def initialize(model_class, fields)
3
- @model_class = model_class
4
- @fields = fields
5
- end
1
+ module Wallaby
2
+ class ActiveRecord
3
+ class ModelDecorator
4
+ # Try to find the field that can be used as title
5
+ class TitleFieldFinder
6
+ TITLE_FIELD_TYPES = %w(string).freeze
7
+ TITLE_NAMES = %w(name title string text).freeze
6
8
 
7
- def find
8
- possible_title_fields = @fields.select do |field_name, metadata|
9
- %w( string ).include? metadata[:type]
10
- end
11
- target_field = possible_title_fields.values.find do |metadata|
12
- %w( name title string text ).any?{ |v| metadata[:name].index v }
9
+ def initialize(model_class, fields)
10
+ @model_class = model_class
11
+ @fields = fields
12
+ end
13
+
14
+ def find
15
+ possible_title_fields = @fields.select do |_field_name, metadata|
16
+ TITLE_FIELD_TYPES.include? metadata[:type]
17
+ end
18
+ target_field = possible_title_fields.values.find do |metadata|
19
+ TITLE_NAMES.any? { |v| metadata[:name].index v }
20
+ end
21
+ target_field ||= possible_title_fields.values.first
22
+ target_field ||= { name: @model_class.primary_key }
23
+ target_field[:name]
24
+ end
25
+ end
13
26
  end
14
- target_field ||= possible_title_fields.values.first
15
- target_field ||= { name: @model_class.primary_key }
16
- target_field[:name]
17
27
  end
18
28
  end
@@ -1,81 +1,135 @@
1
- class Wallaby::ActiveRecord::ModelDecorator < Wallaby::ModelDecorator
2
- def fields
3
- @fields ||= {}.tap do |hash|
4
- if model_class.table_exists?
5
- hash.merge! general_fields
6
- hash.merge! association_fields
7
- hash.except! *foreign_keys_from_associations
8
- end
9
- end.with_indifferent_access
10
- end
1
+ module Wallaby
2
+ class ActiveRecord
3
+ # Modal decorator for Active Record
4
+ class ModelDecorator < ::Wallaby::ModelDecorator
5
+ # Data types to exclude for index page
6
+ INDEX_EXCLUSIVE_DATA_TYPES =
7
+ %w(binary citext hstore json jsonb text tsvector xml).freeze
11
8
 
12
- def index_fields
13
- @index_fields ||= fields.deep_dup
14
- end
9
+ # Data types to exclude for form page
10
+ FORM_EXCLUSIVE_DATA_TYPES = %w(created_at updated_at).freeze
15
11
 
16
- def show_fields
17
- @show_fields ||= fields.deep_dup
18
- end
12
+ # Origin metadata coming from data source.
13
+ # It needs to be frozen so that we can keep the metadata integrity
14
+ # @return [Hash]
15
+ # example:
16
+ # {
17
+ # # general field
18
+ # id: { name: 'id', type: 'integer', label: 'Id', is_origin: true },
19
+ # # association field
20
+ # category: {
21
+ # 'name' => 'category',
22
+ # 'type' => 'belongs_to',
23
+ # 'label' => 'Category',
24
+ # 'is_origin' => true,
25
+ # 'is_association' => true,
26
+ # 'is_through' => false,
27
+ # 'has_scope' => false,
28
+ # 'foreign_key' => 'category_id',
29
+ # 'class' => Category
30
+ # }
31
+ # }
32
+ def fields
33
+ @fields ||= ::ActiveSupport::HashWithIndifferentAccess.new.tap do |hash|
34
+ # NOTE: There is a chance that people create Active Record class
35
+ # before they do the migration, so initialising the fields will raise
36
+ # all kinds of error. Therefore, we need to check the table existence
37
+ if @model_class.table_exists?
38
+ hash.merge! general_fields
39
+ hash.merge! association_fields
40
+ hash.except!(*foreign_keys_from_associations)
41
+ end
42
+ end.freeze
43
+ end
19
44
 
20
- def form_fields
21
- @form_fields ||= fields.deep_dup
22
- end
45
+ # A copy of all the fields for index page
46
+ # @return [Hash]
47
+ def index_fields
48
+ @index_fields ||= Utils.clone fields
49
+ end
23
50
 
24
- def index_field_names
25
- @index_field_names ||= index_fields.reject do |field_name, metadata|
26
- metadata[:is_association] ||
27
- %w( binary citext hstore json jsonb text tsvector xml ).include?(metadata[:type])
28
- end.keys
29
- end
51
+ # A copy of all the fields for show page
52
+ # @return [Hash]
53
+ def show_fields
54
+ @show_fields ||= Utils.clone fields
55
+ end
30
56
 
31
- def form_field_names
32
- @form_field_names ||= form_fields.reject do |field_name, metadata|
33
- %W( #{ primary_key } updated_at created_at ).include?(field_name) ||
34
- metadata[:has_scope] ||
35
- metadata[:is_through]
36
- end.keys
37
- end
57
+ # A copy of all the fields for form page
58
+ # @return [Hash]
59
+ def form_fields
60
+ @form_fields ||= Utils.clone fields
61
+ end
38
62
 
39
- def form_active_errors(resource)
40
- resource.errors
41
- end
63
+ # Fields name for index page
64
+ # @return [Array]
65
+ def index_field_names
66
+ @index_field_names ||= begin
67
+ index_fields.reject do |_field_name, metadata|
68
+ metadata[:is_association] \
69
+ || INDEX_EXCLUSIVE_DATA_TYPES.include?(metadata[:type])
70
+ end.keys
71
+ end
72
+ end
42
73
 
43
- def primary_key
44
- @model_class.primary_key
45
- end
74
+ # Fields name for form page
75
+ # @return [Array]
76
+ def form_field_names
77
+ @form_field_names ||= begin
78
+ form_fields.reject do |field_name, metadata|
79
+ field_name == primary_key \
80
+ || FORM_EXCLUSIVE_DATA_TYPES.include?(field_name) \
81
+ || metadata[:has_scope] || metadata[:is_through]
82
+ end.keys
83
+ end
84
+ end
46
85
 
47
- def guess_title(resource)
48
- resource.public_send title_field_finder.find
49
- end
86
+ # Errors for resource
87
+ # @return [ActiveModel::Errors]
88
+ def form_active_errors(resource)
89
+ resource.errors
90
+ end
50
91
 
51
- protected
52
- def field_builder
53
- @field_builder ||= Wallaby::ActiveRecord::ModelDecorator::FieldsBuilder.new @model_class
54
- end
92
+ # Primary key for the resource
93
+ # @return [String]
94
+ def primary_key
95
+ @model_class.primary_key
96
+ end
55
97
 
56
- def title_field_finder
57
- @title_field_finder ||= Wallaby::ActiveRecord::ModelDecorator::TitleFieldFinder.new @model_class, general_fields
58
- end
98
+ # To guess the title for resource.
99
+ # It will go through the fields and try to find out the one that looks
100
+ # like a name or text to represent this resource. Otherwise, it will fall
101
+ # back to primary key.
102
+ # @param resource [Object]
103
+ # @return [String] the title of given resource
104
+ def guess_title(resource)
105
+ resource.public_send title_field_finder.find
106
+ end
59
107
 
60
- delegate :general_fields, :association_fields, to: :field_builder
108
+ protected
61
109
 
62
- def foreign_keys_from_associations(associations = association_fields)
63
- associations.inject([]) do |keys, (field_name, metadata)|
64
- keys << metadata[:foreign_key] if metadata[:foreign_key]
65
- keys << metadata[:polymorphic_type] if metadata[:polymorphic_type]
66
- keys
67
- end
68
- end
110
+ # @see Wallaby::ModelDecorator::FieldsBuilder
111
+ def field_builder
112
+ @field_builder ||= FieldsBuilder.new @model_class
113
+ end
69
114
 
70
- def many_associations(associations = association_fields)
71
- associations.select do |field_name, metadata|
72
- /many/ =~ metadata[:type] && !metadata[:is_through]
73
- end
74
- end
115
+ # @see Wallaby::ModelDecorator::TitleFieldFinder
116
+ def title_field_finder
117
+ @title_field_finder ||=
118
+ TitleFieldFinder.new @model_class, general_fields
119
+ end
120
+
121
+ delegate :general_fields, :association_fields, to: :field_builder
75
122
 
76
- def belongs_to_associations(associations = association_fields)
77
- associations.select do |field_name, metadata|
78
- 'belongs_to' == metadata[:type]
123
+ # Find out all the foreign keys for a list of fields
124
+ # @param fields [Hash] metadata of fields
125
+ # @return [Array] a list of foreign keys
126
+ def foreign_keys_from_associations(fields = association_fields)
127
+ fields.each_with_object([]) do |(_field_name, metadata), keys|
128
+ keys << metadata[:foreign_key] if metadata[:foreign_key]
129
+ keys << metadata[:polymorphic_type] if metadata[:polymorphic_type]
130
+ keys
131
+ end
132
+ end
79
133
  end
80
134
  end
81
135
  end
@@ -1,22 +1,51 @@
1
- class Wallaby::ActiveRecord::ModelFinder < Wallaby::ModelFinder
2
- def base
3
- Rails.cache.fetch 'wallaby/active_record/model_finder/base' do
4
- if defined? ::ApplicationRecord
5
- ::ApplicationRecord
6
- else
1
+ module Wallaby
2
+ class ActiveRecord
3
+ # Model finder
4
+ class ModelFinder < ::Wallaby::ModelFinder
5
+ # @return [Array] a list of ActiveRecord subclasses
6
+ def all
7
+ self.class.base.descendants.reject do |model_class|
8
+ abstract?(model_class) || anonymous?(model_class) \
9
+ || schema?(model_class) || habtm?(model_class)
10
+ end.sort_by(&:to_s)
11
+ end
12
+
13
+ # Base class should be either ApplicationRecord or ActiveRecord::Base
14
+ def self.base
15
+ return ::ApplicationRecord if defined? ::ApplicationRecord
7
16
  ::ActiveRecord::Base
8
17
  end
9
- end
10
- end
11
18
 
12
- def all
13
- Rails.cache.fetch 'wallaby/active_record/model_finder' do
14
- base.subclasses.reject do |model_class|
15
- model_class.abstract_class? ||
16
- model_class.to_s.start_with?('#<') ||
17
- model_class.name == 'ActiveRecord::SchemaMigration' ||
19
+ private
20
+
21
+ # Is model class abstract?
22
+ # @param [Class] model class
23
+ # @return [Boolean]
24
+ def abstract?(model_class)
25
+ model_class.abstract_class?
26
+ end
27
+
28
+ # Is model class anonymous?
29
+ # @param [Class] model class
30
+ # @return [Boolean]
31
+ def anonymous?(model_class)
32
+ model_class.to_s.start_with? '#<Class'
33
+ end
34
+
35
+ # Is model class the shcema migration class?
36
+ # @param [Class] model class
37
+ # @return [Boolean]
38
+ def schema?(model_class)
39
+ model_class.name == 'ActiveRecord::SchemaMigration'
40
+ end
41
+
42
+ # Check and see if given model class is intermediate class that generated
43
+ # for has and belongs to many assocation
44
+ # @param [Class] model class
45
+ # @return [Boolean]
46
+ def habtm?(model_class)
18
47
  model_class.name.index('HABTM')
19
- end.sort_by &:to_s
48
+ end
20
49
  end
21
50
  end
22
51
  end
@@ -0,0 +1,30 @@
1
+ module Wallaby
2
+ class ActiveRecord
3
+ # Model pagination provider
4
+ class ModelPaginationProvider < ::Wallaby::ModelPaginationProvider
5
+ # Check if collection has pagination feature
6
+ # @return [Boolean]
7
+ def paginatable?
8
+ @collection.respond_to? :total_count
9
+ end
10
+
11
+ # Total count for the query
12
+ # @return [Integer]
13
+ def total
14
+ @collection.total_count
15
+ end
16
+
17
+ # Page size
18
+ # @return [Integer]
19
+ def page_size
20
+ @params[:per].try(:to_i) || Wallaby.configuration.pagination.page_size
21
+ end
22
+
23
+ # Page number
24
+ # @return [Integer]
25
+ def page_number
26
+ [@params[:page].to_i, 1].max
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,42 @@
1
+ module Wallaby
2
+ class ActiveRecord
3
+ class ModelServiceProvider
4
+ # Normalizer
5
+ class Normalizer
6
+ def initialize(model_decorator)
7
+ @model_decorator = model_decorator
8
+ end
9
+
10
+ def normalize(params)
11
+ params.each do |field_name, values|
12
+ metadata = @model_decorator.fields[field_name] || EMPTY_HASH
13
+ type = (metadata[:type] || EMPTY_STRING)[/range|point|binary/]
14
+ next unless type
15
+ send "normalize_#{type}_values", params, field_name, values
16
+ end
17
+ end
18
+
19
+ def normalize_range_values(params, field_name, values)
20
+ normalized = Array(values).map(&:presence).compact
21
+ params[field_name] =
22
+ if normalized.present? && values.length == 2
23
+ values.first...values.last
24
+ end
25
+ end
26
+
27
+ def normalize_point_values(params, field_name, values)
28
+ normalized = Array(values).map(&:presence).compact
29
+ params[field_name] =
30
+ normalized.present? &&
31
+ values.map(&:to_f) || nil
32
+ end
33
+
34
+ def normalize_binary_values(params, field_name, values)
35
+ params[field_name] =
36
+ values.is_a?(::ActionDispatch::Http::UploadedFile) &&
37
+ values.read || nil
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,64 @@
1
+ module Wallaby
2
+ class ActiveRecord
3
+ class ModelServiceProvider
4
+ # Filter the params
5
+ class Permitter
6
+ def initialize(model_decorator)
7
+ @model_decorator = model_decorator
8
+ end
9
+
10
+ def simple_field_names
11
+ field_names =
12
+ non_range_fields.keys +
13
+ belongs_to_fields.map do |_, metadata|
14
+ [metadata[:foreign_key], metadata[:polymorphic_type]]
15
+ end.flatten.compact
16
+ fields = [@model_decorator.primary_key, 'created_at', 'updated_at']
17
+ field_names.reject { |field_name| fields.include? field_name }
18
+ end
19
+
20
+ def compound_hashed_fields
21
+ field_names =
22
+ range_fields.keys +
23
+ many_association_fields.map { |_, metadata| metadata[:foreign_key] }
24
+ field_names.each_with_object({}) { |name, hash| hash[name] = [] }
25
+ end
26
+
27
+ protected
28
+
29
+ def non_association_fields
30
+ @model_decorator
31
+ .fields.reject { |_, metadata| metadata[:is_association] }
32
+ end
33
+
34
+ def non_range_fields
35
+ non_association_fields
36
+ .reject { |_, metadata| /range|point/ =~ metadata[:type] }
37
+ end
38
+
39
+ def range_fields
40
+ non_association_fields.select do |_, metadata|
41
+ /range|point/ =~ metadata[:type]
42
+ end
43
+ end
44
+
45
+ def association_fields
46
+ @model_decorator.fields.select do |_, metadata|
47
+ metadata[:is_association] &&
48
+ !metadata[:has_scope] && !metadata[:is_through]
49
+ end
50
+ end
51
+
52
+ def many_association_fields
53
+ association_fields.select { |_, metadata| /many/ =~ metadata[:type] }
54
+ end
55
+
56
+ def belongs_to_fields
57
+ association_fields.select do |_field_name, metadata|
58
+ metadata[:type] == 'belongs_to'
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,70 @@
1
+ module Wallaby
2
+ class ActiveRecord
3
+ class ModelServiceProvider
4
+ class Querier
5
+ # Build up query using the results
6
+ class Transformer < Parslet::Transform
7
+ SIMPLE_OPERATORS = {
8
+ ':' => :eq,
9
+ ':=' => :eq,
10
+ ':!' => :not_eq,
11
+ ':!=' => :not_eq,
12
+ ':<>' => :not_eq,
13
+ ':~' => :matches,
14
+ ':^' => :matches,
15
+ ':$' => :matches,
16
+ ':!~' => :does_not_match,
17
+ ':!^' => :does_not_match,
18
+ ':!$' => :does_not_match,
19
+ ':>' => :gt,
20
+ ':>=' => :gteq,
21
+ ':<' => :lt,
22
+ ':<=' => :lteq
23
+ }.freeze
24
+
25
+ SEQUENCE_OPERATORS = {
26
+ ':' => :in,
27
+ ':=' => :in,
28
+ ':!' => :not_in,
29
+ ':!=' => :not_in,
30
+ ':<>' => :not_in,
31
+ ':()' => :between,
32
+ ':!()' => :not_between
33
+ }.freeze
34
+
35
+ rule keyword: simple(:value) do
36
+ value.try :to_str
37
+ end
38
+
39
+ rule keyword: sequence(:value) do
40
+ value.presence.try :map, :to_str
41
+ end
42
+
43
+ rule left: simple(:left), op: simple(:op), right: simple(:right) do
44
+ oped = op.try :to_str
45
+ operator = SIMPLE_OPERATORS[oped]
46
+ next unless operator
47
+ lefted = left.try :to_str
48
+ convert = case oped
49
+ when ':~', ':!~' then "%#{right}%"
50
+ when ':^', ':!^' then "#{right}%"
51
+ when ':$', ':!$' then "%#{right}"
52
+ end
53
+ { left: lefted, op: operator, right: convert || right }
54
+ end
55
+
56
+ rule left: simple(:left), op: simple(:op), right: sequence(:right) do
57
+ oped = op.try :to_str
58
+ operator = SEQUENCE_OPERATORS[oped]
59
+ next unless operator
60
+ lefted = left.try :to_str
61
+ convert = if %w(:() :!()).include?(oped)
62
+ Range.new right.try(:first), right.try(:last)
63
+ end
64
+ { left: lefted, op: operator, right: convert || right }
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,102 @@
1
+ module Wallaby
2
+ class ActiveRecord
3
+ class ModelServiceProvider
4
+ # Query builder
5
+ class Querier
6
+ TEXT_FIELDS = %w(string text citext).freeze
7
+
8
+ def initialize(model_decorator)
9
+ @model_decorator = model_decorator
10
+ @model_class = @model_decorator.model_class
11
+ end
12
+
13
+ def search(params)
14
+ filter_name, keywords, field_queries = extract params
15
+ scope = filtered_by filter_name
16
+ query = text_search keywords
17
+ query = field_search field_queries, query
18
+ scope.where query
19
+ end
20
+
21
+ private
22
+
23
+ def parser
24
+ @parser ||= Parser.new
25
+ end
26
+
27
+ def transformer
28
+ @transformer ||= Transformer.new
29
+ end
30
+
31
+ def table
32
+ @model_class.arel_table
33
+ end
34
+
35
+ def extract(params)
36
+ expressions = to_expressions params
37
+ keywords = expressions.select { |v| v.is_a? String }
38
+ field_queries = expressions.select { |v| v.is_a? Hash }
39
+ filter_name = params[:filter]
40
+ [filter_name, keywords, field_queries]
41
+ end
42
+
43
+ def to_expressions(params)
44
+ parsed = parser.parse(params[:q] || EMPTY_STRING)
45
+ converted = transformer.apply parsed
46
+ converted.is_a?(Array) ? converted : [converted]
47
+ end
48
+
49
+ def filtered_by(filter_name)
50
+ valid_filter_name =
51
+ Utils.find_filter_name(filter_name, @model_decorator.filters)
52
+ scope = find_scope(valid_filter_name)
53
+ if scope.blank? then unscoped
54
+ elsif scope.respond_to?(:call) then @model_class.instance_exec(&scope)
55
+ elsif @model_class.respond_to?(scope)
56
+ @model_class.public_send(scope)
57
+ else unscoped
58
+ end
59
+ end
60
+
61
+ def find_scope(filter_name)
62
+ filter = @model_decorator.filters[filter_name] || {}
63
+ filter[:scope] || filter_name
64
+ end
65
+
66
+ def unscoped
67
+ @model_class.where nil
68
+ end
69
+
70
+ def text_search(keywords, query = nil)
71
+ return query if keywords.blank?
72
+ text_fields.each do |field_name|
73
+ sub_query = nil
74
+ keywords.each do |keyword|
75
+ exp = table[field_name].matches("%#{keyword}%")
76
+ sub_query = sub_query.try(:and, exp) || exp
77
+ end
78
+ query = query.try(:or, sub_query) || sub_query
79
+ end
80
+ query
81
+ end
82
+
83
+ def field_search(field_queries, query)
84
+ field_queries.each do |exp|
85
+ next unless @model_decorator.fields[exp[:left]]
86
+ sub_query = table[exp[:left]].public_send(exp[:op], exp[:right])
87
+ query = query.try(:and, sub_query) || sub_query
88
+ end
89
+ query
90
+ end
91
+
92
+ def text_fields
93
+ index_field_names = @model_decorator.index_field_names.map(&:to_s)
94
+ @model_decorator.fields.select do |field_name, metadata|
95
+ index_field_names.include?(field_name) &&
96
+ TEXT_FIELDS.include?(metadata[:type].to_s)
97
+ end.keys
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,29 @@
1
+ module Wallaby
2
+ class ActiveRecord
3
+ class ModelServiceProvider
4
+ # Validator
5
+ class Validator
6
+ def initialize(model_decorator)
7
+ @model_decorator = model_decorator
8
+ end
9
+
10
+ def valid?(resource)
11
+ resource.attributes.each do |field_name, values|
12
+ metadata = @model_decorator.fields[field_name]
13
+ next if valid_range_type? values, metadata
14
+ resource.errors.add field_name, 'required for range data'
15
+ end
16
+ resource.errors.blank?
17
+ end
18
+
19
+ private
20
+
21
+ def valid_range_type?(values, metadata)
22
+ !metadata \
23
+ || !%w(daterange tsrange tstzrange).include?(metadata[:type]) \
24
+ || !values.try(:any?, &:blank?)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end