wallaby 5.0.1 → 5.1.3

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