rules 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. data/README.md +40 -3
  2. data/db/migrate/20120831163952_create_rules.rb +1 -2
  3. data/lib/assets/javascripts/rules/active_admin.js +21 -0
  4. data/lib/rules.rb +11 -0
  5. data/lib/rules/config.rb +20 -0
  6. data/lib/rules/engine.rb +1 -0
  7. data/lib/rules/evaluators/definitions.rb +4 -2
  8. data/lib/rules/evaluators/evaluator.rb +8 -2
  9. data/lib/rules/extensions/active_admin/dsl.rb +32 -31
  10. data/lib/rules/extensions/active_model/absence_validator.rb +5 -0
  11. data/lib/rules/extensions/active_model/parameter_key_validator.rb +7 -0
  12. data/lib/rules/has_rules.rb +1 -1
  13. data/lib/rules/parameters.rb +1 -1
  14. data/lib/rules/parameters/attribute.rb +5 -8
  15. data/lib/rules/parameters/constant.rb +1 -14
  16. data/lib/rules/parameters/constant_definitions.rb +14 -15
  17. data/lib/rules/parameters/parameter.rb +39 -13
  18. data/lib/rules/rule.rb +85 -52
  19. data/lib/rules/rule_set.rb +53 -50
  20. data/lib/rules/version.rb +1 -1
  21. data/spec/dummy/app/assets/images/edit_example.png +0 -0
  22. data/spec/dummy/db/development.sqlite3 +0 -0
  23. data/spec/dummy/db/migrate/{20120831163952_create_rules.rb → 20121029183622_create_rules.rules.rb} +2 -2
  24. data/spec/dummy/db/migrate/{20120831173835_create_rule_sets.rb → 20121029183623_create_rule_sets.rules.rb} +1 -0
  25. data/spec/dummy/db/schema.rb +74 -75
  26. data/spec/dummy/db/test.sqlite3 +0 -0
  27. data/spec/dummy/log/development.log +8405 -7718
  28. data/spec/dummy/log/test.log +6158 -282
  29. data/spec/dummy/script/rails +0 -0
  30. data/spec/dummy/tmp/cache/assets/C5F/0B0/sprockets%2F7b74141c7941d947343b6d7593e3761e +0 -0
  31. data/spec/dummy/tmp/cache/assets/C7B/E40/sprockets%2Fc629141780bbee6259a21437c1428a67 +0 -0
  32. data/spec/dummy/tmp/cache/assets/{C58/950/sprockets%2F615625588c137a1529206d80ee81fe93 → C84/1D0/sprockets%2F649513e42d02d9493f733015c1c8cf58} +0 -0
  33. data/spec/dummy/tmp/cache/assets/{D0B/EB0/sprockets%2Fa18a9144dadd3f628d2f061f91190d65 → C88/A50/sprockets%2F61269f1f7806037ed104f099517e4b6d} +0 -0
  34. data/spec/dummy/tmp/cache/assets/{E03/290/sprockets%2Fd469f86939a1cbd52d9b9aaf1db22dae → C9E/670/sprockets%2Ff47927ce03e3b12202e62552e11d911d} +0 -0
  35. data/spec/dummy/tmp/cache/assets/CAB/910/sprockets%2Fd55a96c4e70110aa337804446dd3983d +0 -0
  36. data/spec/dummy/tmp/cache/assets/CAE/360/sprockets%2F6039fb610c813f60e634a4782b4aa877 +0 -0
  37. data/spec/dummy/tmp/cache/assets/CB8/5D0/sprockets%2F5c94c5d562bcb216295496c26397c1d5 +0 -0
  38. data/spec/dummy/tmp/cache/assets/{CF9/580/sprockets%2F9b6f302133d13906b7d823b4ae14f0aa → CBB/6B0/sprockets%2F81c6f6d9206a39d8743c13c59f461f24} +0 -0
  39. data/spec/dummy/tmp/cache/assets/{CAD/F70/sprockets%2F5a1855f4cda19a255ffc802226735517 → CBC/7F0/sprockets%2F9c674dfe9e0f6503950e9212602fa198} +0 -0
  40. data/spec/dummy/tmp/cache/assets/CC4/300/sprockets%2Fc455e0353702d71fea6a99e9988763d2 +0 -0
  41. data/spec/dummy/tmp/cache/assets/{CA2/8F0/sprockets%2Fa283030e028b24b4ba4084e067888c3e → CCE/540/sprockets%2F7897a2f839c15d27739c478e5098cae3} +0 -0
  42. data/spec/dummy/tmp/cache/assets/{D11/FD0/sprockets%2Ff7129bbf85d84e8bc056543e20281e3b → CCF/A70/sprockets%2Ff84117614042da23eac1c3c1c95177d5} +0 -0
  43. data/spec/dummy/tmp/cache/assets/{DCE/350/sprockets%2F812b6c1998c5ad61096ae7b16cdaeeea → CCF/B30/sprockets%2Ff8e4785e47480d99304deb197af77716} +0 -0
  44. data/spec/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  45. data/spec/dummy/tmp/cache/assets/CDB/040/sprockets%2Fc732474e67003a4eb840eb44c5ca9385 +0 -0
  46. data/spec/dummy/tmp/cache/assets/CE6/210/sprockets%2F65a1063d333cef4cc977b687c063f753 +0 -0
  47. data/spec/dummy/tmp/cache/assets/{D84/0C0/sprockets%2Fe48907efa9d3370f9a2f687a1a7b8d8d → D03/EA0/sprockets%2F58fba513bbd32d023349c0d57f396c22} +0 -0
  48. data/spec/dummy/tmp/cache/assets/{DD3/370/sprockets%2Ff23609d51b09058fdd08daeecfd7e5e3 → D1C/C00/sprockets%2Ff3e537b590d1e8c26ba67794ca9a2549} +0 -0
  49. data/spec/dummy/tmp/cache/assets/{CFE/5D0/sprockets%2F5e63c7f47b86e8916fe1b8f88f470370 → D1E/A20/sprockets%2F448a2575960df5788cb1b6347d9c4bfa} +0 -0
  50. data/spec/dummy/tmp/cache/assets/{D29/AE0/sprockets%2Fb62ea1b010afb027501b4f18b8dd3689 → D1F/7D0/sprockets%2F39f84da4e78a14f35fc2559b709218cf} +0 -0
  51. data/spec/dummy/tmp/cache/assets/D30/800/sprockets%2Fb5a31c56ce1b4444a1a1fb826c8881a6 +0 -0
  52. data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  53. data/spec/dummy/tmp/cache/assets/{CD0/AC0/sprockets%2F6f2c1b921c2b86e16aee3245772240c1 → D37/3D0/sprockets%2F3f693950778fef29f540ccf486789edb} +0 -0
  54. data/spec/dummy/tmp/cache/assets/D38/540/sprockets%2F86e1ad844bc9df4491a2a52b870421db +0 -0
  55. data/spec/dummy/tmp/cache/assets/D45/D90/sprockets%2F786ea0631bc7ab5a84183882f07cff7b +0 -0
  56. data/spec/dummy/tmp/cache/assets/D48/640/sprockets%2F9f4f18d910b678b1d0777e04cdaf6f24 +0 -0
  57. data/spec/dummy/tmp/cache/assets/{CB8/A30/sprockets%2Ff35759652c08bb7b3921c434d9ee1654 → D4D/0F0/sprockets%2Fe101ce9532cd7f637a3a889fee74291f} +0 -0
  58. data/spec/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  59. data/spec/dummy/tmp/cache/assets/{D72/9E0/sprockets%2Fdf58dccde6a5e1266ba5c386149108c6 → D52/DA0/sprockets%2F36936870fc1855efeb9d8119eec2ee41} +0 -0
  60. data/spec/dummy/tmp/cache/assets/{D9E/530/sprockets%2F8dbb0027bd9ce6b96c81b82bdec17814 → D54/0D0/sprockets%2F39d86b7fa34888e64af3993acc6e7c10} +0 -0
  61. data/spec/dummy/tmp/cache/assets/D59/D70/sprockets%2Fed9f0de1b65ae8d7ef89087c83728461 +0 -0
  62. data/spec/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  63. data/spec/dummy/tmp/cache/assets/{D41/AB0/sprockets%2Fd112ea59edbe4b7335549d814cb1d848 → D64/DB0/sprockets%2F7a3fd8bcc2642600f5ce0d5926e5be10} +0 -0
  64. data/spec/dummy/tmp/cache/assets/D66/7F0/sprockets%2F71eb5b2174c2bca8ed14955fd01c57a7 +0 -0
  65. data/spec/dummy/tmp/cache/assets/{D47/050/sprockets%2F237eeefd847c43be2e7810a77e80841b → D6F/450/sprockets%2F02efd499574bb3628cec534aee4b11e6} +0 -0
  66. data/spec/dummy/tmp/cache/assets/D75/770/sprockets%2Fa0501a374162cf29c8a9c8cecf678cf7 +0 -0
  67. data/spec/dummy/tmp/cache/assets/{C9B/B40/sprockets%2F29dc70cbda38311141464e041655de41 → D78/D80/sprockets%2F9930fb628d6fe577ab5707632a0dffbc} +0 -0
  68. data/spec/dummy/tmp/cache/assets/D7D/FA0/sprockets%2F4124a7757f7f9ca6ca072ae8ffd3269f +0 -0
  69. data/spec/dummy/tmp/cache/assets/D8D/970/sprockets%2Fccea3ce5a756eb6e3c61e246c121a730 +0 -0
  70. data/spec/dummy/tmp/cache/assets/D93/4C0/sprockets%2F857ecd2092cee11f740b3cf2e373ad2a +0 -0
  71. data/spec/dummy/tmp/cache/assets/{CE4/D80/sprockets%2Fd7f4b155b13ea1d8578693e62b0e6463 → D98/530/sprockets%2F6d51419c03c562d84be5ebfbe71b5a3e} +0 -0
  72. data/spec/dummy/tmp/cache/assets/D9A/960/sprockets%2F1b481cb0d9ea61f57a341cdc96fc823e +0 -0
  73. data/spec/dummy/tmp/cache/assets/DA1/DC0/sprockets%2F76c4315e48dbb2cb7e92dd5b8b7ab185 +0 -0
  74. data/spec/dummy/tmp/cache/assets/{D24/B00/sprockets%2F671d6f261d89a29e918b1ec7818db6a9 → DA5/DE0/sprockets%2F366c4e5e2d81e438b4e50bec5e4d88da} +0 -0
  75. data/spec/dummy/tmp/cache/assets/{CAC/4A0/sprockets%2F3d00565c9e04f1bd481d33b4f1985706 → DA9/C90/sprockets%2F5bdea8b6a1c99fff81a38fb37281f137} +0 -0
  76. data/spec/dummy/tmp/cache/assets/{DAA/990/sprockets%2F8e0ca45c660183bfe809afc1f69bf9a9 → DAB/4B0/sprockets%2F9b48d3d915c5ddf603344e595de1fbee} +0 -0
  77. data/spec/dummy/tmp/cache/assets/DB0/1D0/sprockets%2Fa89dd6f509b6ff2fc8d458d371da2c29 +0 -0
  78. data/spec/dummy/tmp/cache/assets/{CB2/140/sprockets%2F035ac84d6c64495d0067b926533f8bc3 → DC8/EB0/sprockets%2F8b8dc54ec9133ff2a2b8fc31b1c6da63} +0 -0
  79. data/spec/dummy/tmp/cache/assets/{D60/6C0/sprockets%2Fd7ef2595e5c50eb96e9d3c3d8698f481 → DD0/170/sprockets%2Fd9eb23a676ff2da1c29e2cb2f0ff4791} +0 -0
  80. data/spec/dummy/tmp/cache/assets/{D65/EE0/sprockets%2Fd9062d87143bb5c1d9d73c3ea02d7eb1 → DD5/380/sprockets%2Fb835ac5f9225cbc2e3c2c6669df46cee} +0 -0
  81. data/spec/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  82. data/spec/dummy/tmp/cache/assets/DDE/420/sprockets%2Ffb8b186d5bf0307f86f65fc787d2adeb +0 -0
  83. data/spec/dummy/tmp/cache/assets/DE6/000/sprockets%2F03f0d1a05d0ee270ce8acacef4233bf8 +0 -0
  84. data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  85. data/spec/dummy/tmp/cache/assets/{ECA/140/sprockets%2Fd8ebea8564753ddfde91fadbdacc9f5b → E21/900/sprockets%2F4013eebd9f4bf6b2870f0d7adbba4e1d} +0 -0
  86. data/spec/dummy/tmp/cache/sass/{40a0b76d82cd847e80571abbfe75b703bdb6b77f → 2086f86f19c1c7590bd3622023362d065fe06dd4}/active_admin.css.scssc +0 -0
  87. data/spec/dummy/tmp/cache/sass/{a28435efd4cb26f8ebc9e55bbaf23e3537a13190 → 35fba3f569e02e3ff55b98665f3046be151c4360}/_button.scssc +0 -0
  88. data/spec/dummy/tmp/cache/sass/{a28435efd4cb26f8ebc9e55bbaf23e3537a13190 → 35fba3f569e02e3ff55b98665f3046be151c4360}/_clearfix.scssc +0 -0
  89. data/spec/dummy/tmp/cache/sass/{a28435efd4cb26f8ebc9e55bbaf23e3537a13190 → 35fba3f569e02e3ff55b98665f3046be151c4360}/_font-face.scssc +0 -0
  90. data/spec/dummy/tmp/cache/sass/{a28435efd4cb26f8ebc9e55bbaf23e3537a13190 → 35fba3f569e02e3ff55b98665f3046be151c4360}/_font-family.scssc +0 -0
  91. data/spec/dummy/tmp/cache/sass/{a28435efd4cb26f8ebc9e55bbaf23e3537a13190 → 35fba3f569e02e3ff55b98665f3046be151c4360}/_hide-text.scssc +0 -0
  92. data/spec/dummy/tmp/cache/sass/{a28435efd4cb26f8ebc9e55bbaf23e3537a13190 → 35fba3f569e02e3ff55b98665f3046be151c4360}/_html5-input-types.scssc +0 -0
  93. data/spec/dummy/tmp/cache/sass/{a28435efd4cb26f8ebc9e55bbaf23e3537a13190 → 35fba3f569e02e3ff55b98665f3046be151c4360}/_position.scssc +0 -0
  94. data/spec/dummy/tmp/cache/sass/{a28435efd4cb26f8ebc9e55bbaf23e3537a13190 → 35fba3f569e02e3ff55b98665f3046be151c4360}/_timing-functions.scssc +0 -0
  95. data/spec/dummy/tmp/cache/sass/{ee5b745f862b39ff22bf2068a6582c3ee05d1a79 → 4498c5797b40d28956767b90fa545e9739e43edc}/_dashboard.scssc +0 -0
  96. data/spec/dummy/tmp/cache/sass/{ee5b745f862b39ff22bf2068a6582c3ee05d1a79 → 4498c5797b40d28956767b90fa545e9739e43edc}/_logged_out.scssc +0 -0
  97. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_all.css.scssc +0 -0
  98. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_buttons.css.scssc +0 -0
  99. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_gradients.css.scssc +0 -0
  100. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_icons.css.scssc +0 -0
  101. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_reset.css.scssc +0 -0
  102. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_rounded.css.scssc +0 -0
  103. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_sections.css.scssc +0 -0
  104. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_shadows.css.scssc +0 -0
  105. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_typography.scssc +0 -0
  106. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_utilities.scssc +0 -0
  107. data/spec/dummy/tmp/cache/sass/{597823364192c14f6a72ee20d1a1496a722ab1f2 → 4c643142bab3f2738bc6b6f785180a24ac1b79de}/_variables.css.scssc +0 -0
  108. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_batch_actions.css.scssc +0 -0
  109. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_blank_slates.scssc +0 -0
  110. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_breadcrumbs.scssc +0 -0
  111. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_buttons.scssc +0 -0
  112. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_columns.scssc +0 -0
  113. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_comments.css.scssc +0 -0
  114. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_date_picker.css.scssc +0 -0
  115. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_dropdown_menu.scssc +0 -0
  116. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_flash_messages.css.scssc +0 -0
  117. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_grid.scssc +0 -0
  118. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_links.scssc +0 -0
  119. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_pagination.scssc +0 -0
  120. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_panels.scssc +0 -0
  121. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_popovers.css.scssc +0 -0
  122. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_scopes.scssc +0 -0
  123. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_status_tags.scssc +0 -0
  124. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_table_tools.css.scssc +0 -0
  125. data/spec/dummy/tmp/cache/sass/{7f31076cc0ed50f35bd0098f8e2a34720ee15633 → 5d62cb9b998e0464beef8c02d6cedb219bafb98d}/_tables.css.scssc +0 -0
  126. data/spec/dummy/tmp/cache/sass/{bb2f93296c9b1e89237438ea63572f38aae290c3 → 9f77ade2a538829418bc2e2168a13e3c5e8d566e}/_deprecated-webkit-gradient.scssc +0 -0
  127. data/spec/dummy/tmp/cache/sass/{bb2f93296c9b1e89237438ea63572f38aae290c3 → 9f77ade2a538829418bc2e2168a13e3c5e8d566e}/_flex-grid.scssc +0 -0
  128. data/spec/dummy/tmp/cache/sass/{bb2f93296c9b1e89237438ea63572f38aae290c3 → 9f77ade2a538829418bc2e2168a13e3c5e8d566e}/_grid-width.scssc +0 -0
  129. data/spec/dummy/tmp/cache/sass/{bb2f93296c9b1e89237438ea63572f38aae290c3 → 9f77ade2a538829418bc2e2168a13e3c5e8d566e}/_linear-gradient.scssc +0 -0
  130. data/spec/dummy/tmp/cache/sass/{bb2f93296c9b1e89237438ea63572f38aae290c3 → 9f77ade2a538829418bc2e2168a13e3c5e8d566e}/_modular-scale.scssc +0 -0
  131. data/spec/dummy/tmp/cache/sass/{bb2f93296c9b1e89237438ea63572f38aae290c3 → 9f77ade2a538829418bc2e2168a13e3c5e8d566e}/_radial-gradient.scssc +0 -0
  132. data/spec/dummy/tmp/cache/sass/{bb2f93296c9b1e89237438ea63572f38aae290c3 → 9f77ade2a538829418bc2e2168a13e3c5e8d566e}/_render-gradients.scssc +0 -0
  133. data/spec/dummy/tmp/cache/sass/{bb2f93296c9b1e89237438ea63572f38aae290c3 → 9f77ade2a538829418bc2e2168a13e3c5e8d566e}/_tint-shade.scssc +0 -0
  134. data/spec/dummy/tmp/cache/sass/{bb2f93296c9b1e89237438ea63572f38aae290c3 → 9f77ade2a538829418bc2e2168a13e3c5e8d566e}/_transition-property-name.scssc +0 -0
  135. data/spec/dummy/tmp/cache/sass/{1a9fc2ebd62e6e7cb58441dc0293552ac07c99a9 → a500b9d7e9e6288c20d3d089871d43ac3aee2604}/_bourbon.scssc +0 -0
  136. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_animation.scssc +0 -0
  137. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_appearance.scssc +0 -0
  138. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_background-image.scssc +0 -0
  139. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_background-size.scssc +0 -0
  140. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_background.scssc +0 -0
  141. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_border-image.scssc +0 -0
  142. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_border-radius.scssc +0 -0
  143. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_box-shadow.scssc +0 -0
  144. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_box-sizing.scssc +0 -0
  145. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_columns.scssc +0 -0
  146. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_flex-box.scssc +0 -0
  147. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_inline-block.scssc +0 -0
  148. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_linear-gradient.scssc +0 -0
  149. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_prefixer.scssc +0 -0
  150. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_radial-gradient.scssc +0 -0
  151. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_transform.scssc +0 -0
  152. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_transition.scssc +0 -0
  153. data/spec/dummy/tmp/cache/sass/{da77e6f9a782ec4d4cbe7e3a439e89684c300b51 → b44994b8f6b8597cac35b9d162a52f9cd517eda4}/_user-select.scssc +0 -0
  154. data/spec/dummy/tmp/cache/sass/{6d594ee297c7ebeecae08432451ce722569d1a35 → d39759080a3787a3183222f58ca8294b28eca502}/_footer.scssc +0 -0
  155. data/spec/dummy/tmp/cache/sass/{6d594ee297c7ebeecae08432451ce722569d1a35 → d39759080a3787a3183222f58ca8294b28eca502}/_main_structure.scssc +0 -0
  156. data/spec/dummy/tmp/cache/sass/{6d594ee297c7ebeecae08432451ce722569d1a35 → d39759080a3787a3183222f58ca8294b28eca502}/_title_bar.scssc +0 -0
  157. data/spec/dummy/tmp/cache/sass/{89a136914378618e18c0f75bc8c0f78e84403b0f → ecc4c9317361a12ede9a49c29635837608d17540}/_base.css.scssc +0 -0
  158. data/spec/dummy/tmp/cache/sass/{89a136914378618e18c0f75bc8c0f78e84403b0f → ecc4c9317361a12ede9a49c29635837608d17540}/_forms.css.scssc +0 -0
  159. data/spec/dummy/tmp/cache/sass/{89a136914378618e18c0f75bc8c0f78e84403b0f → ecc4c9317361a12ede9a49c29635837608d17540}/_header.css.scssc +0 -0
  160. data/spec/dummy/tmp/cache/sass/{89a136914378618e18c0f75bc8c0f78e84403b0f → ecc4c9317361a12ede9a49c29635837608d17540}/_mixins.css.scssc +0 -0
  161. data/spec/dummy/tmp/cache/sass/{89a136914378618e18c0f75bc8c0f78e84403b0f → ecc4c9317361a12ede9a49c29635837608d17540}/_typography.css.scssc +0 -0
  162. data/spec/dummy/tmp/cache/sass/{89a136914378618e18c0f75bc8c0f78e84403b0f → ecc4c9317361a12ede9a49c29635837608d17540}/print.css.scssc +0 -0
  163. data/spec/rules/evaluators/evaluator_spec.rb +36 -0
  164. data/spec/rules/parameters/attribute_spec.rb +20 -0
  165. data/spec/rules/rule_set_spec.rb +21 -11
  166. data/spec/rules/rule_spec.rb +138 -65
  167. metadata +262 -289
  168. data/spec/dummy/db/production.sqlite3 +0 -0
  169. data/spec/dummy/log/production.log +0 -74
  170. data/spec/dummy/tmp/cache/assets/C83/FF0/sprockets%2F75b536e9b6927c15b60310fa867e3613 +0 -0
  171. data/spec/dummy/tmp/cache/assets/C88/FD0/sprockets%2F6c558405457e9b59062515dc86bfa252 +0 -0
  172. data/spec/dummy/tmp/cache/assets/C99/A50/sprockets%2Ffd135176f584f5f5658ef48f41187352 +0 -0
  173. data/spec/dummy/tmp/cache/assets/C9F/2F0/sprockets%2Faf814c1513450b5466432d2a565ca5c6 +0 -0
  174. data/spec/dummy/tmp/cache/assets/CA7/860/sprockets%2F0b1c90a9344b3c3989fc2153d0d63193 +0 -0
  175. data/spec/dummy/tmp/cache/assets/CAE/DA0/sprockets%2F9a133e6ab132f569c40b5e82188f4290 +0 -0
  176. data/spec/dummy/tmp/cache/assets/CB2/920/sprockets%2F2db5288c0689b237264d0fb22b74d981 +0 -0
  177. data/spec/dummy/tmp/cache/assets/CCA/AE0/sprockets%2Ff80b892078268a51ddf973986035bf9d +0 -0
  178. data/spec/dummy/tmp/cache/assets/CE0/300/sprockets%2F79b0286bc0a316d639ed5f83b1d33763 +0 -0
  179. data/spec/dummy/tmp/cache/assets/CFD/B50/sprockets%2Fb21fb4d7213b3bf64d6317b3f22584b1 +0 -0
  180. data/spec/dummy/tmp/cache/assets/CFE/450/sprockets%2F088e843f7fffd845821822c7668a4d7e +0 -0
  181. data/spec/dummy/tmp/cache/assets/CFE/D50/sprockets%2Fc912074248a7b0bdc219a5eac3c52348 +0 -0
  182. data/spec/dummy/tmp/cache/assets/D0B/650/sprockets%2Fb66b60c6b42330849f5b7449bbe108bd +0 -0
  183. data/spec/dummy/tmp/cache/assets/D0E/820/sprockets%2F1611a9cc354f4e02c97d26b3c6ce7466 +0 -0
  184. data/spec/dummy/tmp/cache/assets/D38/5F0/sprockets%2F0c09a45e38408ec6e0e9caf206d5028d +0 -0
  185. data/spec/dummy/tmp/cache/assets/D3A/CE0/sprockets%2F238bf80bfbe1e2a5bb9280e62162584f +0 -0
  186. data/spec/dummy/tmp/cache/assets/D3B/000/sprockets%2F961758db39c6223d18ea6b7aeac400d5 +0 -0
  187. data/spec/dummy/tmp/cache/assets/D3B/F60/sprockets%2Ffd56cbfde039018718a0933340af6ef2 +0 -0
  188. data/spec/dummy/tmp/cache/assets/D48/0D0/sprockets%2F1ef4ab19f690478ac62ad77fcb284447 +0 -0
  189. data/spec/dummy/tmp/cache/assets/D65/B10/sprockets%2Fca02dba972b587d3a69b8c32c7200fc6 +0 -0
  190. data/spec/dummy/tmp/cache/assets/D66/2A0/sprockets%2F7b7cb56109acc9205fbbb792187ad04b +0 -0
  191. data/spec/dummy/tmp/cache/assets/D94/FA0/sprockets%2F8b190b3f72af75f09bfab1f354a0c37b +0 -0
  192. data/spec/dummy/tmp/cache/assets/D98/550/sprockets%2F69a91a7b4aca241c17daf6a78ac6845a +0 -0
  193. data/spec/dummy/tmp/cache/assets/DC1/C40/sprockets%2Fa8cbeabf3ef2d34b43be61671536f2a2 +0 -0
  194. data/spec/dummy/tmp/cache/assets/DC7/8B0/sprockets%2Ffad93382facdc50f097e26ebf19010ea +0 -0
  195. data/spec/dummy/tmp/cache/assets/DD7/6A0/sprockets%2Fd5b1cca8d0835fbe9957aa4ee5f8333e +0 -0
  196. data/spec/dummy/tmp/cache/assets/DD8/440/sprockets%2F7edf42cfb3ac43c7cd4f63c3980d7c86 +0 -0
  197. data/spec/dummy/tmp/cache/assets/DF5/270/sprockets%2Fc7f64f609aceac43da16557daacc421f +0 -0
  198. data/spec/dummy/tmp/cache/assets/DF5/410/sprockets%2Fd6efccfb4bfeb13ed0125e8c53424a65 +0 -0
  199. data/spec/dummy/tmp/cache/assets/DFF/180/sprockets%2Fdc24014f1fcc77f40c5e7de188ad8ebd +0 -0
  200. data/spec/dummy/tmp/cache/assets/E07/7C0/sprockets%2Fcc5def6ed69cb6a1b7f6084992ed3ca1 +0 -0
  201. data/spec/dummy/tmp/pids/server.pid +0 -1
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
- AR Rules
1
+ Rules
2
2
  =========
3
+ [![build status](https://secure.travis-ci.org/azach/rules.png?branch=master)](https://secure.travis-ci.org/azach/rules) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/azach/rules)
3
4
 
4
5
  A Ruby gem engine that allows you to add customizable business rules to any ActiveRecord model. Rules integrates with ActiveAdmin to make it trivial to allow admin users to create rules on the fly for your models.
5
6
 
@@ -8,7 +9,7 @@ Installation
8
9
  Add it to your Gemfile:
9
10
 
10
11
  ```ruby
11
- gem "rules", :github => 'azach/rules'
12
+ gem "rules"
12
13
  ```
13
14
 
14
15
  Update your schema:
@@ -33,6 +34,7 @@ class Order < ActiveRecord::Base
33
34
  has_rule_attributes({
34
35
  customer_email: {
35
36
  name: "customer email address"
37
+ type: :string # see Rules::Parameters::Parameter::VALID_TYPES for a full list
36
38
  }
37
39
  })
38
40
  end
@@ -45,7 +47,7 @@ For example:
45
47
  ```ruby
46
48
  order = Order.new
47
49
  order.email_address = "morbo@example.com"
48
- order.evaluate(customer_email: order.email_address)
50
+ order.rules_pass?(customer_email: order.email_address)
49
51
  ```
50
52
 
51
53
  Defining Rules
@@ -70,6 +72,41 @@ This will give you something like:
70
72
 
71
73
  ![ActiveAdmin form for editing rules](https://github.com/azach/rules/raw/master/spec/dummy/app/assets/images/edit_example.png)
72
74
 
75
+ However, rules are defined using keys and values, so you can easily use your own custom solution.
76
+
77
+ ```ruby
78
+ rule = Rules::Rule.new(lhs_parameter_key: 'day_of_week', evaluator_key: 'equals', rhs_parameter_raw: 'Sunday')
79
+ rule.lhs_parameter_value
80
+ => 'Sunday' # (or the current day of week)
81
+ rule.evaluate
82
+ => true
83
+
84
+ Order.has_rule_attributes(customer_email: { name: "Customer's email address" })
85
+ order = Order.new
86
+ rule_set = Rules::RuleSet.new(source: order)
87
+
88
+ rule = Rules::Rule.new(rule_set: rule_set, lhs_parameter_key: 'customer_email', evaluator_key: 'matches', rhs_parameter_raw: 'example.com$')
89
+ rule.lhs_parameter_value
90
+ => nil # we didn't pass anything in
91
+ rule.rhs_parameter_value
92
+ => /example.com$/ # this is a Regexp class
93
+ rule.evaluate(customer_email: 'john@example.com')
94
+ => true
95
+ rule.evaluate(customer_email: 'john@example.net')
96
+ => false
97
+ ```
98
+
99
+ Configuration
100
+ ------------
101
+ You can add an initializer to configure default options.
102
+
103
+ ```ruby
104
+ Rules.configure do |config|
105
+ config.errors_are_false = true # return false if an evaluator raises an error (true by default)
106
+ config.missing_attributes_are_nil = true # return nil when a value is not passed for an attribute parameter
107
+ end
108
+ ```
109
+
73
110
  Default Constants
74
111
  ------------
75
112
  TODO
@@ -2,8 +2,7 @@ class CreateRules < ActiveRecord::Migration
2
2
  def change
3
3
  create_table :rules_rules do |t|
4
4
  t.belongs_to :rule_set
5
- t.string :evaluator
6
- t.text :parameters
5
+ t.text :expression
7
6
 
8
7
  t.timestamps
9
8
  end
@@ -0,0 +1,21 @@
1
+ $(function() {
2
+ $('.rules_rule_set select[name*="[lhs_parameter_key]"]').on('change', function(ev) {
3
+ var $rawInput;
4
+ var selectedType;
5
+
6
+ $rawInput = $(this).parent('li').siblings('[id*="rhs_parameter_raw_input"]').find('input');
7
+ selectedType = $(this).find(':selected').data('type');
8
+
9
+ $rawInput.get(0).type = selectedType;
10
+ });
11
+
12
+ $('.rules_rule_set select[name*="[evaluator_key]"]').on('change', function(ev) {
13
+ var $rhsInputs;
14
+ var requiresRHS;
15
+
16
+ $rawInputs = $(this).parent('li').siblings('[id*="rhs_parameter"]').find('input, select');
17
+ requiresRHS = $(this).find(':selected').data('requires-rhs');
18
+
19
+ $rawInputs.prop('disabled', !requiresRHS);
20
+ });
21
+ });
@@ -1,4 +1,7 @@
1
1
  require 'rules/engine'
2
+ require 'rules/extensions/active_model/absence_validator'
3
+ require 'rules/extensions/active_model/parameter_key_validator'
4
+ require 'rules/config'
2
5
  require 'rules/evaluators'
3
6
  require 'rules/evaluators/definitions'
4
7
  require 'rules/has_rules'
@@ -8,6 +11,14 @@ require 'rules/rule'
8
11
  require 'rules/rule_set'
9
12
 
10
13
  module Rules
14
+ def self.config
15
+ @config ||= Config.instance
16
+ end
17
+
18
+ def self.configure
19
+ yield config
20
+ end
21
+
11
22
  def self.evaluators
12
23
  @evaluators ||= Evaluators.list
13
24
  end
@@ -0,0 +1,20 @@
1
+ require 'singleton'
2
+
3
+ class Rules::Config
4
+ include Singleton
5
+
6
+ attr_accessor :errors_are_false, :missing_attributes_are_nil
7
+
8
+ def initialize
9
+ self.errors_are_false = true
10
+ self.missing_attributes_are_nil = true
11
+ end
12
+
13
+ def missing_attributes_are_nil?
14
+ !!missing_attributes_are_nil
15
+ end
16
+
17
+ def errors_are_false?
18
+ !!errors_are_false
19
+ end
20
+ end
@@ -5,6 +5,7 @@ module Rules
5
5
  initializer :active_admin do
6
6
  if defined? ActiveAdmin
7
7
  require 'rules/extensions/active_admin/dsl'
8
+ ActiveAdmin.application.javascripts << 'rules/active_admin.js'
8
9
  end
9
10
  end
10
11
  end
@@ -35,14 +35,16 @@ module Rules
35
35
  end
36
36
 
37
37
  define_evaluator :matches do
38
- self.evaluation_method = ->(lhs, rhs) { !!(lhs =~ Regexp.new(rhs)) }
38
+ self.evaluation_method = ->(lhs, rhs) { !!(lhs =~ rhs) }
39
39
  self.name = 'matches'
40
+ self.type_for_rhs = :regexp
40
41
  self.requires_rhs = true
41
42
  end
42
43
 
43
44
  define_evaluator :not_matches do
44
- self.evaluation_method = ->(lhs, rhs) { !(lhs =~ Regexp.new(rhs)) }
45
+ self.evaluation_method = ->(lhs, rhs) { !(lhs =~ rhs) }
45
46
  self.name = 'does not match'
47
+ self.type_for_rhs = :regexp
46
48
  self.requires_rhs = true
47
49
  end
48
50
 
@@ -1,7 +1,7 @@
1
1
  module Rules
2
2
  module Evaluators
3
3
  class Evaluator
4
- attr_accessor :evaluation_method, :requires_rhs, :name
4
+ attr_accessor :evaluation_method, :requires_rhs, :type_for_rhs, :name
5
5
 
6
6
  def initialize(key)
7
7
  @name = key.to_s
@@ -10,7 +10,13 @@ module Rules
10
10
 
11
11
  def evaluate(lhs, rhs = nil)
12
12
  raise 'Unknown evaluation method' unless evaluation_method
13
- requires_rhs? ? evaluation_method.call(lhs, rhs) : evaluation_method.call(lhs)
13
+
14
+ begin
15
+ requires_rhs? ? evaluation_method.call(lhs, rhs) : evaluation_method.call(lhs)
16
+ rescue StandardError => ex
17
+ return false if Rules.config.errors_are_false?
18
+ raise ex
19
+ end
14
20
  end
15
21
 
16
22
  def requires_rhs?
@@ -1,31 +1,32 @@
1
- ActiveAdmin::FormBuilder.class_eval do
2
- def has_rules
3
- self.inputs 'Rules' do
4
- self.semantic_fields_for :rule_set do |rules_rule_set_form|
5
- rules_rule_set_form.has_many :rules do |rules_rule_form|
6
- rules_rule_form.input :lhs_parameter, :label => 'Left hand side', collection: rules_parameter_collection(rules_rule_form.object.rule_set)
7
- rules_rule_form.input :evaluator, :as => :select, :collection => Rules.evaluators.map {|key, evaluator| [evaluator.name, key] }.sort_by {|name, key| name }
8
- rules_rule_form.input :rhs_parameter, :label => 'Right hand side'
9
- end
10
- rules_rule_set_form.input :evaluation_logic, :as => :select, :label => 'Must match', collection: [['All Rules', 'all'], ['Any Rules', 'any']]
11
- end
12
- end
13
- end
14
-
15
- def rules_parameter_collection(rule_set)
16
- @rules_parameter_collection ||= Rules.constants.merge(rule_set.try(:attributes) || {}).map {|key, const| [const.name, key] }
17
- end
18
- end
19
-
20
- ActiveAdmin::Views::Pages::Show.class_eval do
21
- def show_rules
22
- panel "Rules" do
23
- div resource.rule_set.evaluation_logic == 'any' ? 'Must match any rule' : 'Must match all rules'
24
- table_for resource.rule_set.rules do |rule|
25
- column('Left hand side') { |rule| rule.lhs_parameter_object.to_s.titleize }
26
- column('Condition') { |rule| rule.get_evaluator }
27
- column('Right hand side') { |rule| rule.rhs_parameter_value.to_s }
28
- end
29
- end
30
- end
31
- end
1
+ ActiveAdmin::FormBuilder.class_eval do
2
+ def has_rules
3
+ inputs 'Rules' do
4
+ semantic_fields_for :rule_set do |rules_rule_set_form|
5
+ rules_rule_set_form.has_many :rules do |rules_rule_form|
6
+ rules_rule_form.input :lhs_parameter_key, :label => 'Left hand side', collection: rules_parameter_collection(object.rule_set)
7
+ rules_rule_form.input :evaluator_key, :label => 'Evaluator', :as => :select, :collection => Rules.evaluators.map {|key, evaluator| [evaluator.name, key, {:'data-requires-rhs' => evaluator.requires_rhs?}] }.sort_by {|name, key| name }
8
+ rules_rule_form.input :rhs_parameter_raw, :label => 'Enter a value', :wrapper_html => { :class => "rules_rhs_parameter" }
9
+ rules_rule_form.input :rhs_parameter_key, :label => 'Or choose a value', collection: rules_parameter_collection(object.rule_set), :wrapper_html => { :class => "rules_rhs_parameter" }
10
+ end
11
+ rules_rule_set_form.input :evaluation_logic, :as => :select, :label => 'Must match', collection: [['All Rules', 'all'], ['Any Rules', 'any']]
12
+ end
13
+ end
14
+ end
15
+
16
+ def rules_parameter_collection(rule_set)
17
+ @rules_parameter_collection ||= Rules.constants.merge(rule_set.try(:attributes) || {}).map {|key, const| [const.name, key, {:'data-type' => const.try(:type) || 'string'}] }
18
+ end
19
+ end
20
+
21
+ ActiveAdmin::Views::Pages::Show.class_eval do
22
+ def show_rules
23
+ panel "Rules" do
24
+ div resource.rule_set.evaluation_logic == 'any' ? 'Must match any rule' : 'Must match all rules'
25
+ table_for resource.rule_set.rules do |rule|
26
+ column('Left hand side') { |rule| rule.lhs_parameter.to_s }
27
+ column('Condition') { |rule| rule.evaluator }
28
+ column('Right hand side') { |rule| rule.rhs_parameter.to_s }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ class AbsenceValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ record.errors.add attribute, (options[:message] || "must be blank") unless value.blank?
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class ParameterKeyValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless record.valid_parameter_keys.include?(value)
4
+ record.errors.add attribute, (options[:message] || "is not a valid parameter")
5
+ end
6
+ end
7
+ end
@@ -14,7 +14,7 @@ module Rules
14
14
  end
15
15
 
16
16
  def rule_set
17
- super || Rules::RuleSet.new
17
+ super || self.build_rule_set
18
18
  end
19
19
 
20
20
  def rules_pass?(options = {})
@@ -12,7 +12,7 @@ module Rules
12
12
 
13
13
  def self.define_constant(key, &block)
14
14
  raise "Constant #{key} already exists" if @@constants[key]
15
- constant = Constant.new(key)
15
+ constant = Constant.new(key: key)
16
16
  constant.instance_eval(&block) if block_given?
17
17
  @@constants[key] = constant
18
18
  end
@@ -2,15 +2,12 @@ require 'rules/parameters/parameter'
2
2
 
3
3
  module Rules::Parameters
4
4
  class Attribute < Parameter
5
- attr_reader :key
6
-
7
- def initialize(options = {})
8
- @name = options[:name]
9
- @key = options[:key].to_sym
10
- end
11
-
12
5
  def evaluate(attributes = {})
13
- attributes.fetch(key)
6
+ if Rules.config.missing_attributes_are_nil?
7
+ attributes[key]
8
+ else
9
+ attributes.fetch(key)
10
+ end
14
11
  end
15
12
  end
16
13
  end
@@ -2,24 +2,11 @@ require 'rules/parameters/parameter'
2
2
 
3
3
  module Rules::Parameters
4
4
  class Constant < Parameter
5
- attr_accessor :evaluation_method, :casting_method, :input_type
6
-
7
- def initialize(key)
8
- self.name = key.to_s
9
- end
5
+ attr_accessor :evaluation_method
10
6
 
11
7
  def evaluate(attributes = {})
12
8
  raise 'Unknown evaluation method' unless evaluation_method
13
9
  evaluation_method.call
14
10
  end
15
-
16
- def input_type
17
- @input_type || :string
18
- end
19
-
20
- def cast(value)
21
- return value unless casting_method
22
- casting_method.call(value)
23
- end
24
11
  end
25
12
  end
@@ -1,15 +1,14 @@
1
- module Rules
2
- module Parameters
3
- define_constant :today do
4
- self.name = 'current date'
5
- self.input_type = :date_select
6
- self.evaluation_method = -> { Time.now.utc.to_date }
7
- self.casting_method = ->(value) { value.is_a?(Date) ? value : Date.parse(value.to_s) }
8
- end
9
-
10
- define_constant :day_of_week do
11
- self.name = 'day of week'
12
- self.evaluation_method = -> { Date::DAYNAMES[Time.now.utc.to_date.wday] }
13
- end
14
- end
15
- end
1
+ module Rules
2
+ module Parameters
3
+ define_constant :today do
4
+ self.name = 'current date'
5
+ self.type = :date
6
+ self.evaluation_method = -> { Time.now.utc.to_date }
7
+ end
8
+
9
+ define_constant :day_of_week do
10
+ self.name = 'day of week'
11
+ self.evaluation_method = -> { Date::DAYNAMES[Time.now.utc.to_date.wday] }
12
+ end
13
+ end
14
+ end
@@ -1,13 +1,39 @@
1
- module Rules::Parameters
2
- class Parameter
3
- attr_accessor :name
4
-
5
- def cast(value)
6
- value
7
- end
8
-
9
- def to_s
10
- name
11
- end
12
- end
13
- end
1
+ module Rules::Parameters
2
+ class Parameter
3
+ VALID_TYPES = [:date, :integer, :float, :boolean, :string, :regexp]
4
+
5
+ attr_accessor :name, :type, :key
6
+
7
+ def self.cast(value, type)
8
+ return value unless type
9
+ case type
10
+ when :date
11
+ Date.parse(value.to_s)
12
+ when :integer
13
+ value.to_i
14
+ when :float
15
+ value.to_f
16
+ when :boolean
17
+ value.to_s == 'true' ? true : false
18
+ when :string
19
+ value.to_s
20
+ when :regexp
21
+ Regexp.new(value.to_s)
22
+ else
23
+ raise "Don't know how to cast #{type}"
24
+ end
25
+ end
26
+
27
+ def initialize(options = {})
28
+ self.key = options[:key].to_sym
29
+ self.name = options[:name] || options[:key].to_s.humanize
30
+ self.type = options[:type] if options[:type]
31
+
32
+ raise "Unknown type #{type}" if type && !VALID_TYPES.include?(type)
33
+ end
34
+
35
+ def to_s
36
+ name
37
+ end
38
+ end
39
+ end
@@ -1,52 +1,85 @@
1
- require 'rules/evaluators'
2
- require 'rules/parameters'
3
-
4
- module Rules
5
- class Rule < ActiveRecord::Base
6
- attr_accessible :rule_set, :evaluator, :lhs_parameter, :rhs_parameter
7
-
8
- belongs_to :rule_set, class_name: 'Rules::RuleSet'
9
-
10
- validates :evaluator, presence: true, inclusion: {in: Evaluators.list.keys.map(&:to_s)}
11
- validates :lhs_parameter, presence: true, inclusion: {in: ->(rule) { rule.valid_parameters }}
12
- validates :rhs_parameter, presence: true, if: ->(rule) { rule.get_evaluator.try(:requires_rhs?) }
13
- validates :rhs_parameter, inclusion: {in: [nil, ''], message: 'must be blank for this evaluation method'}, unless: ->(rule) { rule.get_evaluator.try(:requires_rhs?) }
14
-
15
- store :parameters, :accessors => [:lhs_parameter, :rhs_parameter]
16
-
17
- def evaluate(attributes = {})
18
- lhv = lhs_parameter_value(attributes)
19
- rhv = rhs_parameter_value if get_evaluator.requires_rhs?
20
- get_evaluator.evaluate(lhv, rhv)
21
- end
22
-
23
- def get_evaluator
24
- @get_evaluator ||= evaluator ? Evaluators.list[evaluator.to_sym] : nil
25
- end
26
-
27
- def lhs_parameter_value(attributes = {})
28
- lhs_parameter_object.try(:evaluate, attributes)
29
- end
30
-
31
- def rhs_parameter_value
32
- lhs_parameter_object ? lhs_parameter_object.cast(rhs_parameter) : rhs_parameter
33
- end
34
-
35
- def lhs_parameter_object
36
- @lhs_parameter_object ||= if lhs_parameter
37
- Parameters.constants[lhs_parameter.to_sym] \
38
- || valid_attributes[lhs_parameter.to_sym]
39
- else
40
- nil
41
- end
42
- end
43
-
44
- def valid_parameters
45
- Parameters.constants.keys.map(&:to_s) + valid_attributes.keys.map(&:to_s)
46
- end
47
-
48
- def valid_attributes
49
- rule_set ? rule_set.attributes : {}
50
- end
51
- end
52
- end
1
+ require 'rules/evaluators'
2
+ require 'rules/parameters'
3
+
4
+ module Rules
5
+ class Rule < ActiveRecord::Base
6
+ attr_accessible :rule_set, :evaluator_key, :lhs_parameter_key, :rhs_parameter_key, :rhs_parameter_raw
7
+
8
+ belongs_to :rule_set, class_name: 'Rules::RuleSet'
9
+
10
+ validates :evaluator_key, presence: true, inclusion: {in: Evaluators.list.keys.map(&:to_s)}
11
+ validates :lhs_parameter_key, parameter_key: true
12
+ validates :rhs_parameter_key, parameter_key: true, if: :rhs_parameter_key
13
+ validates :rhs_parameter_key, presence: true, if: :requires_rhs?, unless: :rhs_parameter_raw
14
+ validates :rhs_parameter_key, absence: true, unless: :requires_rhs?
15
+ validates :rhs_parameter_raw, absence: true, unless: :requires_rhs?
16
+ validates :rhs_parameter_key, absence: true, if: :rhs_parameter_raw
17
+
18
+ store :expression, :accessors => [:evaluator_key, :lhs_parameter_key, :rhs_parameter_key, :rhs_parameter_raw]
19
+
20
+ def evaluate(attributes = {})
21
+ lhv = lhs_parameter_value(attributes)
22
+ rhv = rhs_parameter_value(attributes) if evaluator.requires_rhs?
23
+ evaluator.evaluate(lhv, rhv)
24
+ end
25
+
26
+ def evaluator
27
+ @evaluator ||= evaluator_key ? Evaluators.list[evaluator_key.to_sym] : nil
28
+ end
29
+
30
+ def lhs_parameter_key
31
+ key_from_store :lhs_parameter_key
32
+ end
33
+
34
+ def rhs_parameter_key
35
+ key_from_store :rhs_parameter_key
36
+ end
37
+
38
+ def evaluator_key
39
+ key_from_store :evaluator_key
40
+ end
41
+
42
+ def lhs_parameter_value(attributes = {})
43
+ lhs_parameter.try(:evaluate, attributes)
44
+ end
45
+
46
+ def rhs_parameter_value(attributes = {})
47
+ if rhs_parameter.respond_to?(:evaluate)
48
+ rhs_parameter.try(:evaluate, attributes)
49
+ else
50
+ rhv = Parameters::Parameter.cast(rhs_parameter_raw, lhs_parameter.try(:type))
51
+ Parameters::Parameter.cast(rhv, evaluator.try(:type_for_rhs))
52
+ end
53
+ end
54
+
55
+ def rhs_parameter
56
+ rhs_parameter_key ? parameter_from_key(rhs_parameter_key) : rhs_parameter_raw
57
+ end
58
+
59
+ def lhs_parameter
60
+ @lhs_parameter ||= lhs_parameter_key ? parameter_from_key(lhs_parameter_key) : nil
61
+ end
62
+
63
+ def valid_parameter_keys
64
+ Parameters.constants.keys.map(&:to_s) + valid_attributes.keys.map(&:to_s)
65
+ end
66
+
67
+ def valid_attributes
68
+ rule_set ? rule_set.attributes : {}
69
+ end
70
+
71
+ def requires_rhs?
72
+ evaluator ? evaluator.requires_rhs? : false
73
+ end
74
+
75
+ private
76
+
77
+ def key_from_store(key)
78
+ expression[key].blank? ? nil : expression[key]
79
+ end
80
+
81
+ def parameter_from_key(key)
82
+ Parameters.constants[key.to_sym] || valid_attributes[key.to_sym]
83
+ end
84
+ end
85
+ end