shoppper 0.1.0

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 (267) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/config/shopper_manifest.js +2 -0
  6. data/app/assets/javascripts/shopper/application.js +15 -0
  7. data/app/assets/stylesheets/shopper/application.css +15 -0
  8. data/app/commands/shopper/cart_page/add_product.rb +16 -0
  9. data/app/commands/shopper/cart_page/update_cart.rb +21 -0
  10. data/app/commands/shopper/cart_page/update_coupon.rb +51 -0
  11. data/app/commands/shopper/cart_page/update_order_items.rb +31 -0
  12. data/app/commands/shopper/checkout_page/add_checkout_addresses.rb +62 -0
  13. data/app/commands/shopper/checkout_page/add_checkout_delivery.rb +18 -0
  14. data/app/commands/shopper/checkout_page/add_checkout_payment.rb +19 -0
  15. data/app/commands/shopper/checkout_page/place_order.rb +40 -0
  16. data/app/commands/shopper/checkout_page/proceed_checkout.rb +14 -0
  17. data/app/commands/shopper/orders_page/get_orders.rb +34 -0
  18. data/app/controllers/shopper/cart_controller.rb +29 -0
  19. data/app/controllers/shopper/checkout_controller.rb +68 -0
  20. data/app/controllers/shopper/order_item_controller.rb +12 -0
  21. data/app/controllers/shopper/orders_controller.rb +19 -0
  22. data/app/controllers/shopper/shopper_controller.rb +4 -0
  23. data/app/decorators/concerns/shopper/caller_attachable.rb +24 -0
  24. data/app/decorators/concerns/shopper/view_helpers.rb +15 -0
  25. data/app/decorators/shopper/cart_page/coupon_decorator.rb +23 -0
  26. data/app/decorators/shopper/checkout_page/credit_card_decorator.rb +9 -0
  27. data/app/decorators/shopper/checkout_page/delivery_decorator.rb +28 -0
  28. data/app/decorators/shopper/items_table/item_decorator.rb +46 -0
  29. data/app/decorators/shopper/order_details/address_decorator.rb +19 -0
  30. data/app/decorators/shopper/order_details/order_decorator.rb +45 -0
  31. data/app/decorators/shopper/order_summary/order_decorator.rb +38 -0
  32. data/app/decorators/shopper/orders_page/order_decorator.rb +31 -0
  33. data/app/forms/shopper/address_form.rb +32 -0
  34. data/app/forms/shopper/coupon_form.rb +12 -0
  35. data/app/forms/shopper/credit_card_form.rb +36 -0
  36. data/app/helpers/shopper/application_helper.rb +4 -0
  37. data/app/jobs/shopper/application_job.rb +4 -0
  38. data/app/mailers/shopper/checkout_mailer.rb +9 -0
  39. data/app/models/concerns/shopper/order_aasm.rb +35 -0
  40. data/app/models/concerns/shopper/order_arithmetic_helpers.rb +24 -0
  41. data/app/models/concerns/shopper/order_item_arithmetic_helpers.rb +7 -0
  42. data/app/models/concerns/shopper/order_number.rb +13 -0
  43. data/app/models/concerns/shopper/order_product_helpers.rb +15 -0
  44. data/app/models/shopper/address.rb +12 -0
  45. data/app/models/shopper/application_record.rb +5 -0
  46. data/app/models/shopper/billing_address.rb +4 -0
  47. data/app/models/shopper/country.rb +11 -0
  48. data/app/models/shopper/coupon.rb +13 -0
  49. data/app/models/shopper/credit_card.rb +9 -0
  50. data/app/models/shopper/delivery.rb +24 -0
  51. data/app/models/shopper/order.rb +27 -0
  52. data/app/models/shopper/order_item.rb +18 -0
  53. data/app/models/shopper/shipping_address.rb +4 -0
  54. data/app/presenters/shopper/cart_page/cart_presenter.rb +34 -0
  55. data/app/presenters/shopper/checkout_page/address_step_presenter.rb +55 -0
  56. data/app/presenters/shopper/checkout_page/complete_step_presenter.rb +34 -0
  57. data/app/presenters/shopper/checkout_page/confirm_step_presenter.rb +20 -0
  58. data/app/presenters/shopper/checkout_page/delivery_step_presenter.rb +16 -0
  59. data/app/presenters/shopper/checkout_page/payment_step_presenter.rb +16 -0
  60. data/app/presenters/shopper/checkout_page/progress_presenter.rb +37 -0
  61. data/app/presenters/shopper/orders_page/order_presenter.rb +28 -0
  62. data/app/presenters/shopper/orders_page/orders_presenter.rb +17 -0
  63. data/app/services/shopper/checkout_manager.rb +55 -0
  64. data/app/validators/shopper/phone_validator.rb +11 -0
  65. data/app/views/shopper/cart/add_product.js.haml +3 -0
  66. data/app/views/shopper/cart/show.html.haml +22 -0
  67. data/app/views/shopper/checkout/_address_fields.html.haml +7 -0
  68. data/app/views/shopper/checkout/_layout.html.haml +12 -0
  69. data/app/views/shopper/checkout/address.html.haml +37 -0
  70. data/app/views/shopper/checkout/complete.html.haml +17 -0
  71. data/app/views/shopper/checkout/confirm.html.haml +6 -0
  72. data/app/views/shopper/checkout/delivery.html.haml +35 -0
  73. data/app/views/shopper/checkout/payment.html.haml +20 -0
  74. data/app/views/shopper/checkout_mailer/complete.html.haml +5 -0
  75. data/app/views/shopper/order_item/_table.html.haml +4 -0
  76. data/app/views/shopper/order_item/_table_desktop.html.haml +40 -0
  77. data/app/views/shopper/order_item/_table_phone.html.haml +30 -0
  78. data/app/views/shopper/orders/_details.html.haml +21 -0
  79. data/app/views/shopper/orders/_summary.html.haml +27 -0
  80. data/app/views/shopper/orders/index.html.haml +32 -0
  81. data/app/views/shopper/orders/show.html.haml +4 -0
  82. data/config/routes.rb +11 -0
  83. data/db/migrate/20170126000000_create_shopper_countries.rb +11 -0
  84. data/db/migrate/20170127194802_create_shopper_addresses.rb +19 -0
  85. data/db/migrate/20170127194999_create_shopper_deliveries.rb +12 -0
  86. data/db/migrate/20170129160931_create_shopper_orders.rb +14 -0
  87. data/db/migrate/20170129162148_create_shopper_order_items.rb +14 -0
  88. data/db/migrate/20170220181741_create_shopper_coupons.rb +11 -0
  89. data/db/migrate/20170224112113_add_state_to_shopper_orders.rb +5 -0
  90. data/db/migrate/20170224174654_create_shopper_credit_cards.rb +15 -0
  91. data/lib/shopper.rb +18 -0
  92. data/lib/shopper/checkout_wizard.rb +43 -0
  93. data/lib/shopper/configuration.rb +22 -0
  94. data/lib/shopper/current_order.rb +47 -0
  95. data/lib/shopper/engine.rb +21 -0
  96. data/lib/shopper/model_methods.rb +35 -0
  97. data/lib/shopper/version.rb +3 -0
  98. data/lib/tasks/shopper_tasks.rake +4 -0
  99. data/spec/commands/add_product_spec.rb +39 -0
  100. data/spec/controllers/cart_controller_spec.rb +34 -0
  101. data/spec/controllers/order_item_controller_spec.rb +18 -0
  102. data/spec/dummy/Rakefile +6 -0
  103. data/spec/dummy/app/assets/config/manifest.js +5 -0
  104. data/spec/dummy/app/assets/images/fallback/product_default.png +0 -0
  105. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  106. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  107. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  108. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  109. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  110. data/spec/dummy/app/controllers/application_controller.rb +20 -0
  111. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  112. data/spec/dummy/app/jobs/application_job.rb +2 -0
  113. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  114. data/spec/dummy/app/models/ability.rb +7 -0
  115. data/spec/dummy/app/models/application_record.rb +3 -0
  116. data/spec/dummy/app/models/customer.rb +3 -0
  117. data/spec/dummy/app/models/product.rb +21 -0
  118. data/spec/dummy/app/views/layouts/application.html.haml +17 -0
  119. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  120. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  121. data/spec/dummy/app/views/root/index.html +1 -0
  122. data/spec/dummy/bin/bundle +3 -0
  123. data/spec/dummy/bin/rails +4 -0
  124. data/spec/dummy/bin/rake +4 -0
  125. data/spec/dummy/bin/setup +34 -0
  126. data/spec/dummy/bin/update +29 -0
  127. data/spec/dummy/config.ru +5 -0
  128. data/spec/dummy/config/application.rb +21 -0
  129. data/spec/dummy/config/boot.rb +5 -0
  130. data/spec/dummy/config/cable.yml +9 -0
  131. data/spec/dummy/config/database.ci.yml +7 -0
  132. data/spec/dummy/config/database.yml +22 -0
  133. data/spec/dummy/config/environment.rb +5 -0
  134. data/spec/dummy/config/environments/development.rb +54 -0
  135. data/spec/dummy/config/environments/production.rb +86 -0
  136. data/spec/dummy/config/environments/test.rb +42 -0
  137. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  138. data/spec/dummy/config/initializers/assets.rb +11 -0
  139. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  140. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  141. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  142. data/spec/dummy/config/initializers/inflections.rb +16 -0
  143. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  144. data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
  145. data/spec/dummy/config/initializers/session_store.rb +3 -0
  146. data/spec/dummy/config/initializers/simple_form.rb +169 -0
  147. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  148. data/spec/dummy/config/locales/en.yml +23 -0
  149. data/spec/dummy/config/locales/simple_form.en.yml +31 -0
  150. data/spec/dummy/config/puma.rb +47 -0
  151. data/spec/dummy/config/routes.rb +4 -0
  152. data/spec/dummy/config/secrets.yml +22 -0
  153. data/spec/dummy/config/spring.rb +6 -0
  154. data/spec/dummy/db/migrate/20170405091757_create_products.rb +10 -0
  155. data/spec/dummy/db/migrate/20170405160406_create_customers.rb +11 -0
  156. data/spec/dummy/db/schema.rb +33 -0
  157. data/spec/dummy/lib/templates/erb/scaffold/_form.html.erb +13 -0
  158. data/spec/dummy/log/development.log +378 -0
  159. data/spec/dummy/public/404.html +67 -0
  160. data/spec/dummy/public/422.html +67 -0
  161. data/spec/dummy/public/500.html +66 -0
  162. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  163. data/spec/dummy/public/apple-touch-icon.png +0 -0
  164. data/spec/dummy/public/favicon.ico +0 -0
  165. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-5/-5Q4Ub_11IMVDpGB1eI8cCVFFsUlJ8XHRzneSdfWwvA.cache +0 -0
  166. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-u/-uuoXn7BP51SPd9jCpfonUaE46sb8x-w0b34wwdHt9E.cache +1 -0
  167. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/0T/0T0Tt9dfVU3GpS08jbSmEA-3V9YEKDfIoSeIPwG-V5M.cache +2 -0
  168. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/0X/0XswbOgGjXmH1fo5xq93jNgHe48bthciRdbD6tnWCvo.cache +3 -0
  169. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/1c/1ctUiWcG10n8KMVoiqAlIapKLbW5MDV1opGFwHsx34o.cache +1 -0
  170. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/2Z/2ZWFIlXKLDgIwGlnU7FU5aLf_Y1-m4KLYDXG0GdOvcc.cache +0 -0
  171. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/2c/2cgrQmg7ldfRivjXyyPNEH2O9Kps6UbeJH-Y9H1GmAs.cache +1 -0
  172. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/2l/2ltamKE3DUsifk0Gd4gmMxsgm5yKUZiNdhnSVHq_rVs.cache +0 -0
  173. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/31/312SvTp1rlNjttuCrjMR-sOiWZ8_7XmiagVfUeNAWfU.cache +1 -0
  174. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4G/4GIZLDG2a1lnDZF2QESc0VLN9oHXztGnjE-LhJAssbk.cache +1 -0
  175. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4R/4RE2lQ9Q_Khn7V4lGNDAW4xVGALXbiqnfOx-mXE831Q.cache +2 -0
  176. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/6U/6UKVmfPR7t8yUajbBceuWKaXogETyIV6Tbp0Mo8sR8A.cache +2 -0
  177. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/6Z/6Zs5ocm3aNgIQhqxQonxR7DfNzImkwFVBFsFSoXJ9nM.cache +1 -0
  178. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/6b/6bbfebueG2G5YjKNjtH5pwuc1LEJjKmfpIL4IREIArw.cache +1 -0
  179. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/7n/7ncqaz_mmyCQD_AQHpvMs2GXmVS9ETbTrWhH7RWvuYg.cache +2 -0
  180. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/7v/7vrAes6c_ihwe8jMx1yxyRDgWgAmDYBRMlsqqjlOA24.cache +3 -0
  181. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8t/8tcTiIy9nbujTSsrbowd_m1ZDK-XWxe5KBzu4iWULdg.cache +1 -0
  182. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/9H/9H8beH0fozTusUWWhP9agRzmmCJigE63QS3yN6USIVY.cache +1 -0
  183. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/BX/BXeClZZ7pe07EVX5rjMY_bO_Ec1T114oejXtrS7DYLY.cache +1 -0
  184. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/DN/DN6DwzYk58CqQliQANOOcf4LZ9NfwuROLMpG_aIn1iA.cache +0 -0
  185. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/EF/EFDkZUV83NReWDmYJPAbH4QssV1WH7dhLAafE0jMdLE.cache +0 -0
  186. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F4/F4HRhWWKLcUlE_9fUDcNFndpclhItP7pzduMZ0u0zEg.cache +0 -0
  187. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F9/F95lTcrn4OEdV2-8CpWRA7Vo_g8Cyy46-JWOiLmNuZ8.cache +1 -0
  188. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/G5/G52PYwE_K5U0GKtgEfZMMMxYvZn20YV8JnDsE8miA8U.cache +0 -0
  189. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/GY/GYoI8lC94xLeXahTKQmXCYQmEcBTeJVY3rw4Ow29mAk.cache +1 -0
  190. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/HI/HIQD3918XDGcbRA7DQnrUx7Ij6_KmqX7A6-Xg8cTgvs.cache +1 -0
  191. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/IG/IGBxZd8bfUKXZ0qApnsHIDYrpmlGh4_stvNLwNClo-Q.cache +0 -0
  192. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/JZ/JZvskjPGda8b9BWf--ABB7K8u8eZWLEY_IGmIwN-lsM.cache +0 -0
  193. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/LZ/LZtqwTyBeV780reoWMqU06rIHTuSggj_moLE9aZl4zc.cache +1 -0
  194. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/MH/MHuhgGwsUMEUsXy-NMZLnq_3FmpwQ6Hr-x9Pj2Lg6jQ.cache +1 -0
  195. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/MW/MWqLf-tbtXiYTRmuVMVRXCVjAEX6LaEZGlVxS74nM-E.cache +0 -0
  196. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/OK/OKLY4QbXl_c1lHz1gm6tyYrpRIFU-014RSUQpb9fwyw.cache +1 -0
  197. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ok/OkpxMmj1ovAbbyXiexCJnj0apz54kz8iqahL4c1aXDo.cache +1 -0
  198. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QI/QI3sr401dtpp_Wc_mTpdvAI5bpraLUIIf8Mv2feBvEw.cache +1 -0
  199. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/R0/R0TSIcvnfF3-QoBBBWQ32DiJLIR8D676f-SkUhprDK8.cache +0 -0
  200. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/RU/RUX4wpSRbADt7-aSjvb97ICl-fCJjPZxe1MNF6pcGOo.cache +1 -0
  201. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/To/To0QY39VWI3mZb6XXZAgOQCJcEj0tv0BJmIiu2oNdbg.cache +1 -0
  202. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/W9/W9krhTTy1IFXT5Q_rF4CifVRj4AVwKeE_F6pln8Rcmg.cache +1 -0
  203. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ws/Wsgy75qmOuCK1Vp25X17Py-pML-11GG3F3e-OYOsfGk.cache +4 -0
  204. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Yb/YbBCKCjwVLMTjLflIx-p2w001jnZ2kVbDwnbDTh20ps.cache +1 -0
  205. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Zi/ZiHXd8AlVOwGmpnKoVXVS3EKEBdo1Ac-Zcp4eiCPnRQ.cache +2 -0
  206. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/_C/_CHENopb4h3hRBPr0mRRTtNBvd64WDGI5v5hMC7PYgs.cache +0 -0
  207. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/a9/a94GSaPUyrAw0VlSAmwgl4iwDPatPtP7tsesOf07Fuo.cache +1 -0
  208. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/bk/bkyxSPKtV8sC84dH7iq0OkFMQzLCJ7DjdUoC6wYHaAA.cache +1 -0
  209. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/cE/cEQDNdSRl1tLTsWUUnyDWWXECh-MtIchaMG7PA2kuM8.cache +0 -0
  210. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ch/chgbSC87FkXFMbNWMZJAtU_euvqnCKdUcV4CAjuRZXg.cache +1 -0
  211. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/dG/dGcYyTZ1x7nZldIv9CU9P6FILL3BZ_pHeJUEBXOFDtk.cache +1 -0
  212. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/eH/eHNL12KXLctBrvH2X9flxNGA4GRJGoyLwqRrxOWLHss.cache +1 -0
  213. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/fm/fmL1Nfucs6nwuozNi61KJ6nEHU8mb3F2SieWSl1sgeY.cache +1 -0
  214. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gg/ggoM7JVi5PFoOTHQGrMmhFwnOSjd_RsNYmV9SpLGNPY.cache +2 -0
  215. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/iH/iHBHhxxOEBtpSeS0MFX4NL9vEfZMaQ72PxSXtbLy4pQ.cache +3 -0
  216. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/kQ/kQZ9KBvn5nS686f0v-15x3q-XPK9Puu03Od4FRDV_DY.cache +1 -0
  217. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ln/ln7-McEMuq67tYE86JS_5anWVQE-o7kjVzTfmJ-pfYU.cache +0 -0
  218. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/n9/n9fyL8DlM6Ze117bMHweLE2x-4YM5aAW8cT3XYw-de8.cache +0 -0
  219. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/nM/nMZGQCxEPqJUI3praZMAm-YNu7TdqkdDhx3WG6J1KLY.cache +1 -0
  220. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/n_/n_xYqQYhwEMQknb3jFQnjlxxBE9TzMNHCdJ-bEyZFIw.cache +2 -0
  221. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/nx/nxTv3sKVUQZADJyM3dPaVmUA78MIsMLD_K279yN_GsI.cache +2 -0
  222. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/op/opCcAQpH-enBbSXpi0u1AEuA_O2KBuVSFHXlgdw8dNg.cache +1 -0
  223. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/qC/qCvelfVAvK45T3Goi7dqiicmsAKQoF9MkLAZtA4LVC0.cache +0 -0
  224. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/qI/qIdIIo1-efJi3gqwWbkhmcDlc4qTwuolMYutSBGYrxw.cache +2 -0
  225. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/qP/qPmv5snMrDw830S6hSICDcnIy7kVEWoFKXhGKT38lG4.cache +2 -0
  226. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/qx/qxYROkyCYJzti8DI00LfLhgV7FZgHCmaUnCjB3ayfWM.cache +1 -0
  227. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wd/wdwXC49DcebjF5LPaEs1vqDXCWlq-32hHXfiKvjifno.cache +1 -0
  228. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wg/wgn7xxwAmTkEhfWm8ujbjnId_c4MherEJuyXwkipOIg.cache +1 -0
  229. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/x7/x7PYh8DJvPykcEqpVab2vcY9-GFz-3cqtoMlRAu94Uc.cache +2 -0
  230. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xg/xgrrLe9BN5muIDu9b9ADzAJHJtZFmr-PlDUBSkdJTDM.cache +3 -0
  231. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/z7/z79WysQah2YzrP7757RmZSO9M_8yzxDJPheGOhMjyXU.cache +1 -0
  232. data/spec/factories/addresses.rb +14 -0
  233. data/spec/factories/countries.rb +9 -0
  234. data/spec/factories/coupons.rb +10 -0
  235. data/spec/factories/credit_cards.rb +9 -0
  236. data/spec/factories/customer.rb +12 -0
  237. data/spec/factories/deliveries.rb +8 -0
  238. data/spec/factories/order_items.rb +7 -0
  239. data/spec/factories/orders.rb +42 -0
  240. data/spec/factories/product.rb +6 -0
  241. data/spec/features/cart_spec.rb +66 -0
  242. data/spec/features/checkout_spec.rb +53 -0
  243. data/spec/forms/credit_card_form_spec.rb +56 -0
  244. data/spec/models/customer_spec.rb +9 -0
  245. data/spec/models/product_spec.rb +7 -0
  246. data/spec/models/shopper/address_spec.rb +14 -0
  247. data/spec/models/shopper/billing_address_spec.rb +7 -0
  248. data/spec/models/shopper/country_spec.rb +10 -0
  249. data/spec/models/shopper/coupon_spec.rb +9 -0
  250. data/spec/models/shopper/credit_card_spec.rb +7 -0
  251. data/spec/models/shopper/delivery_spec.rb +28 -0
  252. data/spec/models/shopper/order_item_spec.rb +18 -0
  253. data/spec/models/shopper/order_spec.rb +105 -0
  254. data/spec/models/shopper/shipping_address_spec.rb +7 -0
  255. data/spec/rails_helper.rb +52 -0
  256. data/spec/services/checkout_manager_spec.rb +106 -0
  257. data/spec/spec_helper.rb +99 -0
  258. data/spec/support/capybara.rb +23 -0
  259. data/spec/support/database_cleaner.rb +44 -0
  260. data/spec/support/factory_girl.rb +3 -0
  261. data/spec/support/macroses/shared_macroses.rb +32 -0
  262. data/spec/support/macroses/stub_current_order.rb +18 -0
  263. data/spec/support/rails-controller-testing.rb +7 -0
  264. data/spec/support/shared_examples/addressable.rb +22 -0
  265. data/spec/support/shoulda_matchers.rb +4 -0
  266. data/spec/support/show_me_the_cookies.rb +3 -0
  267. metadata +855 -0
@@ -0,0 +1,29 @@
1
+ module Shopper
2
+ class CartController < ShopperController
3
+ respond_to :js, only: [:add_product]
4
+
5
+ def show
6
+ present CartPage::CartPresenter.new(current_order)
7
+ end
8
+
9
+ def update
10
+ CartPage::UpdateCart.call(params, current_order) do
11
+ on(:invalid_coupon) { flash[:error] = 'Invalid coupon code' }
12
+ on(:invalid_product) { flash[:error] = 'Invalid product quantity' }
13
+ on(:validate) do |*attr|
14
+ present CartPage::CartPresenter.new(*attr)
15
+ render 'show'
16
+ end
17
+ on(:ok) { redirect_to cart_path, flash: { success: 'Cart was updated successfully' } }
18
+ end
19
+ end
20
+
21
+ def add_product
22
+ CartPage::AddProduct.call(params) do
23
+ on(:invalid_product) do
24
+ redirect_to main_app.root_path, flash: { error: 'Invalid product' }, js: true
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,68 @@
1
+ module Shopper
2
+ class CheckoutController < ShopperController
3
+ include Shopper::CheckoutWizard
4
+
5
+ before_action :fast_authenticate_customer!
6
+ before_action :check_empty_cart
7
+ before_action :check_accesability, except: :complete
8
+
9
+ before_action :set_progress_presenter
10
+
11
+ rescue_from Wicked::Wizard::InvalidStepError do |_|
12
+ redirect_to cart_path, alert: t('checkout.failure.invalid_step')
13
+ end
14
+
15
+ rescue_from Shopper::CheckoutWizard::EmptyCardError do |_|
16
+ redirect_to cart_path, alert: t('checkout.failure.cart_empty')
17
+ end
18
+
19
+ rescue_from Shopper::CheckoutWizard::CantAccessError do |_|
20
+ redirect_to checkout_path(minimal_accessible_step),
21
+ flash: { error: t('checkout.failure.must_fill_previous') }
22
+ end
23
+
24
+ def show
25
+ present step_presenter.new(current_order)
26
+ render_wizard
27
+ end
28
+
29
+ def update
30
+ Shopper::CheckoutPage::ProceedCheckout.call(current_order, params, step) do
31
+ on(:invalid) do |*attrs|
32
+ present step_presenter.new(*attrs)
33
+ render_wizard
34
+ end
35
+ on(:ok) { redirect_to_next_step }
36
+ end
37
+ end
38
+
39
+ def complete
40
+ Shopper::CheckoutPage::PlaceOrder.call(current_order, params) do
41
+ on(:invalid) { redirect_to cart_path, alert: t('checkout.failure.invalid_step') }
42
+ on(:ok) do |old_order|
43
+ present step_presenter.new(old_order)
44
+ render_wizard
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def set_progress_presenter
52
+ select_up_to_step = if step == :complete
53
+ :complete
54
+ else
55
+ minimal_accessible_step
56
+ end
57
+ present Shopper::CheckoutPage::ProgressPresenter.new(
58
+ steps,
59
+ step,
60
+ select_up_to_step
61
+ ), for: :progress
62
+ end
63
+
64
+ def step_presenter
65
+ "Shopper::CheckoutPage::#{step.capitalize}StepPresenter".constantize
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,12 @@
1
+ module Shopper
2
+ class OrderItemController < ShopperController
3
+ def destroy
4
+ @item = OrderItem.find(params[:id])
5
+ authorize! :destroy, @item
6
+ @item.destroy
7
+ redirect_to cart_path
8
+ rescue CanCan::AccessDenied
9
+ redirect_to cart_path, alert: t('auth.access_denied')
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ module Shopper
2
+ class OrdersController < ShopperController
3
+ before_action :authenticate_user!
4
+ load_and_authorize_resource only: :show
5
+
6
+ def index
7
+ OrdersPage::GetOrders.call do
8
+ on(:invalid_fiter) { flash[:error] = 'Invalid filter' }
9
+ on(:ok) do |*attrs|
10
+ present OrdersPage::OrdersPresenter.new(*attrs)
11
+ end
12
+ end
13
+ end
14
+
15
+ def show
16
+ present OrdersPage::OrderPresenter.new(@order)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ module Shopper
2
+ class ShopperController < Shopper.parent_controller.constantize
3
+ end
4
+ end
@@ -0,0 +1,24 @@
1
+ module Shopper
2
+ module CallerAttachable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def attach(caller)
7
+ @caller = caller
8
+ self
9
+ end
10
+
11
+ def method_missing(method_name, *args, &block)
12
+ if @caller&.respond_to?(method_name, true)
13
+ @caller.send(method_name, *args, &block)
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ def respond_to_missing?(method_name, include_private = false)
20
+ @caller&.respond_to?(method_name, include_private) || super
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ module Shopper
2
+ module ViewHelpers
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def urls
7
+ Rails.application.routes.url_helpers
8
+ end
9
+
10
+ def helpers
11
+ ActionController::Base.helpers
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module Shopper
2
+ module CartPage
3
+ class CouponDecorator < SimpleDelegator
4
+ def error_class
5
+ 'has-error' if error_message
6
+ end
7
+
8
+ def helper?
9
+ error_message
10
+ end
11
+
12
+ def helper
13
+ error_message
14
+ end
15
+
16
+ private
17
+
18
+ def error_message
19
+ @error_message ||= __getobj__.errors&.full_messages_for(:code)&.first
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ module Shopper
2
+ module CheckoutPage
3
+ class CreditCardDecorator < SimpleDelegator
4
+ def expiration_date
5
+ __getobj__.expiration_date&.strftime(CreditCardForm::DATE_FORMAT)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ module Shopper
2
+ module CheckoutPage
3
+ class DeliveryDecorator < SimpleDelegator
4
+ include Shopper::ViewHelpers
5
+
6
+ class << self
7
+ def for_collection(objects, current_delivery_id:)
8
+ @current_delivery_id = current_delivery_id
9
+ objects.map { |object| new(object) }
10
+ end
11
+
12
+ attr_reader :current_delivery_id
13
+ end
14
+
15
+ def days
16
+ "#{min_days} to #{max_days} days"
17
+ end
18
+
19
+ def price
20
+ helpers.number_to_currency(__getobj__.price)
21
+ end
22
+
23
+ def selected?
24
+ __getobj__.id == self.class.current_delivery_id
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,46 @@
1
+ module Shopper
2
+ module ItemsTable
3
+ class ItemDecorator < SimpleDelegator
4
+ class << self
5
+ def for_collection(objects, editable: true, description: true)
6
+ @editable = editable
7
+ @description = description
8
+ objects.map { |object| new(object) }
9
+ end
10
+
11
+ attr_reader :editable, :description
12
+ end
13
+
14
+ include Shopper::ViewHelpers
15
+
16
+ def initialize(order_item)
17
+ @order_item = order_item
18
+ super(order_item.product)
19
+ end
20
+
21
+ delegate :id, :quantity, :to_param, to: :order_item
22
+ delegate :editable, to: :class
23
+ attr_reader :order_item
24
+
25
+ def cover
26
+ cover_url_or_default(version: :thumb)
27
+ end
28
+
29
+ def description
30
+ __getobj__.description.truncate(50) if self.class.description
31
+ end
32
+
33
+ def error_class
34
+ 'has-error' if @order_item.errors&.any?
35
+ end
36
+
37
+ def price
38
+ helpers.number_to_currency(__getobj__.price)
39
+ end
40
+
41
+ def subtotal
42
+ helpers.number_to_currency(@order_item.subtotal)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ module Shopper
2
+ module OrderDetails
3
+ class AddressDecorator < SimpleDelegator
4
+ include Shopper::ViewHelpers
5
+
6
+ BR = '<br />'.html_safe.freeze
7
+
8
+ def to_html
9
+ full_name = "#{first_name} #{last_name}"
10
+ middle = helpers.safe_join([street, city, country.name], BR)
11
+
12
+ phone_label = I18n.t('simple_form.labels.address.phone')
13
+ phone = helpers.safe_join([phone_label, __getobj__.phone], ' ')
14
+
15
+ helpers.safe_join([full_name, middle, phone], BR)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ module Shopper
2
+ module OrderDetails
3
+ class OrderDecorator < SimpleDelegator
4
+ include Shopper::CallerAttachable
5
+
6
+ STARS = '** ** ** '.freeze
7
+ BR = '<br />'.html_safe.freeze
8
+
9
+ def initialize(order, edit_link:)
10
+ @edit_link = edit_link
11
+ super order
12
+ end
13
+
14
+ def edit_link?
15
+ @edit_link
16
+ end
17
+
18
+ def edit_link(step)
19
+ link_to t('checkout.edit'), @caller.wizard_path(step), class: 'general-edit'
20
+ end
21
+
22
+ def billing_address
23
+ @billing_address ||= OrderDetails::AddressDecorator.new(__getobj__.billing_address).to_html
24
+ end
25
+
26
+ def shipping_address
27
+ return billing_address if __getobj__.use_billing?
28
+ OrderDetails::AddressDecorator.new(__getobj__.shipping_address).to_html
29
+ end
30
+
31
+ def shipments
32
+ delivery = __getobj__.delivery
33
+ price = number_to_currency(delivery.price)
34
+ safe_join([price, delivery.title], BR)
35
+ end
36
+
37
+ def payment
38
+ card = __getobj__.credit_card
39
+ number = STARS + card.number.last(4)
40
+ date = card.expiration_date.strftime '%m/%Y'
41
+ safe_join [number, date], BR
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ module Shopper
2
+ module OrderSummary
3
+ class OrderDecorator < SimpleDelegator
4
+ include Shopper::ViewHelpers
5
+
6
+ def initialize(order, deficit_method: :show_zero, position: :right)
7
+ @deficit_method = deficit_method
8
+ @position = position
9
+ super order
10
+ end
11
+
12
+ attr_reader :position
13
+
14
+ def subtotal
15
+ @subtotal = wrap(__getobj__.subtotal)
16
+ end
17
+
18
+ def coupon
19
+ @coupon = wrap(__getobj__.saved_by_coupon)
20
+ end
21
+
22
+ def delivery
23
+ @delivery = wrap(__getobj__.delivery_price)
24
+ end
25
+
26
+ def order_total
27
+ @order_total = wrap(__getobj__.total)
28
+ end
29
+
30
+ private
31
+
32
+ def wrap(input)
33
+ return false if input.zero? && @deficit_method == :hide
34
+ helpers.number_to_currency(input)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ module Shopper
2
+ module OrdersPage
3
+ class OrderDecorator < SimpleDelegator
4
+ include Shopper::ViewHelpers
5
+
6
+ def self.for_collection(objects)
7
+ objects.map { |object| new(object) }
8
+ end
9
+
10
+ def completed_at
11
+ time = __getobj__.completed_at
12
+ return 'Unknown' if time.nil?
13
+ time.strftime '%Y-%m-%d'
14
+ end
15
+
16
+ def state
17
+ I18n.t("order.states.#{__getobj__.state}")
18
+ end
19
+
20
+ def state_classes
21
+ classes = 'font-16 font-weight-light'
22
+ classes + (__getobj__.delivered? ? 'text-success' : 'in-grey-900')
23
+ end
24
+
25
+ def total
26
+ price = __getobj__.order_items.inject(0) { |sum, item| sum + item.subtotal }
27
+ helpers.number_to_currency(price)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ module Shopper
2
+ class AddressForm < Rectify::Form
3
+ attribute :first_name, String
4
+ attribute :last_name, String
5
+ attribute :street, String
6
+ attribute :city, String
7
+ attribute :phone, String
8
+ attribute :phone, String
9
+ attribute :zip, Integer
10
+
11
+ attribute :country_id, Integer
12
+
13
+ validates :first_name,
14
+ :last_name,
15
+ :street,
16
+ :city,
17
+ :zip,
18
+ :phone,
19
+ :country_id,
20
+ presence: true
21
+
22
+ validates :zip, numericality: true
23
+ validates :phone, phone: true
24
+
25
+ validate :check_country
26
+
27
+ def check_country
28
+ return if Shopper::Country.exists?(country_id)
29
+ errors.add(:country_id, 'don\' exitst')
30
+ end
31
+ end
32
+ end