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,20 @@
1
+ module Shopper
2
+ module CheckoutPage
3
+ class ConfirmStepPresenter < Rectify::Presenter
4
+ def initialize(order)
5
+ @order_summary = OrderSummary::OrderDecorator
6
+ .new(order, deficit_method: :hide, position: :right)
7
+
8
+ @order_details = OrderDetails::OrderDecorator
9
+ .new(order, edit_link: true)
10
+ .attach(self)
11
+
12
+ @items = ItemsTable::ItemDecorator
13
+ .for_collection(order.order_items, editable: false)
14
+
15
+ end
16
+
17
+ attr_reader :items, :order_summary, :order_details
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module Shopper
2
+ module CheckoutPage
3
+ class DeliveryStepPresenter < Rectify::Presenter
4
+ def initialize(order)
5
+ current_delivery_id = order.delivery&.id
6
+ @deliveries = CheckoutPage::DeliveryDecorator
7
+ .for_collection(Delivery.all, current_delivery_id: current_delivery_id)
8
+
9
+ @order_summary = OrderSummary::OrderDecorator
10
+ .new(order, deficit_method: :hide, position: :left)
11
+ end
12
+
13
+ attr_reader :deliveries, :order_summary
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Shopper
2
+ module CheckoutPage
3
+ class PaymentStepPresenter < Rectify::Presenter
4
+ def initialize(order, credit_card_form = nil)
5
+ credit_card = credit_card_form || order.credit_card || CreditCard.new
6
+ @card = CheckoutPage::CreditCardDecorator.new(credit_card)
7
+
8
+ @order_summary = OrderSummary::OrderDecorator.new(order,
9
+ deficit_method: :hide,
10
+ position: :right)
11
+ end
12
+
13
+ attr_reader :card, :order_summary
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ module Shopper
2
+ module CheckoutPage
3
+ class ProgressPresenter < Rectify::Presenter
4
+ def initialize(steps, current_step, select_up_to_step)
5
+ @steps = steps
6
+ @current_step = current_step
7
+ @select_up_to_step_index = @steps.index select_up_to_step
8
+ end
9
+
10
+ def steps
11
+ @steps.each_with_index.map do |s, index|
12
+ classes = 'step'
13
+ classes += ' active' if active?(index)
14
+
15
+ number = index + 1
16
+
17
+ name = s.capitalize
18
+
19
+ divider = !last?(index)
20
+
21
+ { classes: classes, number: number, name: name, divider: divider }
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def last?(index)
28
+ @last_index ||= @steps.length - 1
29
+ index == @last_index
30
+ end
31
+
32
+ def active?(index)
33
+ index <= @select_up_to_step_index
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ module Shopper
2
+ module OrdersPage
3
+ class OrderPresenter < Rectify::Presenter
4
+ def initialize(order)
5
+ @order = order
6
+ end
7
+
8
+ def order_number
9
+ @order.number[1..-1]
10
+ end
11
+
12
+ def items_table
13
+ @items ||= ItemsTable::ItemDecorator.for_collection(@order.order_items, editable: false)
14
+ end
15
+
16
+ def order_details
17
+ @order_details ||= OrderDetails::OrderDecorator
18
+ .new(@order, edit_link: false)
19
+ .attach(self)
20
+ end
21
+
22
+ def order_summary
23
+ @order_summary ||= OrderSummary::OrderDecorator
24
+ .new(@order, deficit_method: :hide, position: :right)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ module Shopper
2
+ module OrdersPage
3
+ class OrdersPresenter < Rectify::Presenter
4
+ def initialize(orders, filter_methods, current_filter_method)
5
+ @orders = OrdersPage::OrderDecorator.for_collection(orders)
6
+
7
+ @filter_methods = filter_methods.map do |method|
8
+ { key: method, title: t("order.states.#{method}") }
9
+ end
10
+
11
+ @current_filter_method = t("order.states.#{current_filter_method}")
12
+ end
13
+
14
+ attr_reader :orders, :filter_methods, :current_filter_method
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,55 @@
1
+ module Shopper
2
+ class CheckoutManager
3
+ def initialize(order)
4
+ @order = order
5
+ end
6
+
7
+ # if we on confirm stage and editing previous - all submitting will redirect to :confirm
8
+ def next_step
9
+ return :confirm if minimal_accessible_step == :confirm
10
+ steps.at(minimal_accessible_step_index + 1)
11
+ end
12
+
13
+ def can_access?(step)
14
+ current_step_index = steps_with_completeness.find_index { |key, _| key == step }
15
+ current_step_index <= minimal_accessible_step_index
16
+ end
17
+
18
+ def minimal_accessible_step
19
+ steps_with_completeness.keys[minimal_accessible_step_index]
20
+ end
21
+
22
+ private
23
+
24
+ def minimal_accessible_step_index
25
+ @minimal_accessible_step_index ||=
26
+ steps_with_completeness.find_index { |_, value| !value }
27
+ end
28
+
29
+ def steps
30
+ steps_with_completeness.keys
31
+ end
32
+
33
+ def steps_with_completeness
34
+ @steps_with_completeness ||= {
35
+ address: has_addresses?,
36
+ delivery: has_delivery?,
37
+ payment: has_payment?,
38
+ confirm: false
39
+ }
40
+ end
41
+
42
+ def has_addresses?
43
+ shipping = @order.use_billing ? true : @order.shipping_address
44
+ @order.billing_address && shipping
45
+ end
46
+
47
+ def has_delivery?
48
+ @order.delivery
49
+ end
50
+
51
+ def has_payment?
52
+ @order.credit_card
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ module Shopper
2
+ class PhoneValidator < ActiveModel::EachValidator
3
+ PHONE_REGEX = /\+\d{3}\ \d{2}\ \d{3}\ \d{4}/
4
+
5
+ def validate_each(record, attribute, value)
6
+ return if PHONE_REGEX.match? value
7
+ message = options[:message] || :phone
8
+ record.errors.add attribute, message
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ - partial = render partial: 'layouts/shared/cart'
2
+ :plain
3
+ $('#cart').html('#{escape_javascript(partial)}');
@@ -0,0 +1,22 @@
1
+ %h1.general-title-margin Cart
2
+ = form_tag cart_path, method: :put do
3
+ - if presenter.cart_empty?
4
+ %h2.text-center.mt-10.mb-40 Cart is empty
5
+ - else
6
+ = render partial: 'shopper/order_item/table', locals: { items: presenter.items }
7
+ .general-order-wrap
8
+ .row
9
+ .col-sm-4
10
+ = fields_for 'coupon', presenter.coupon do |coupon_fields|
11
+ .form-group.general-input-group{class: presenter.coupon.error_class}
12
+ = label_tag 'coupon_code', 'Enter Your Coupon Code', class: 'control-label'
13
+ = coupon_fields.text_field :code, placeholder: 'Enter Your Coupon Code', class: 'form-control'
14
+ - if presenter.coupon.helper?
15
+ %span.help-block= presenter.coupon.helper
16
+ %div
17
+ = button_tag 'Update Cart', class: 'btn btn-primary mt-30 mb-15 res-block'
18
+ .col-sm-8
19
+ = render partial: 'shopper/orders/summary', locals: { order: presenter.order_summary }
20
+ = link_to 'Checkout', presenter.checkout_path, class: 'btn btn-default mb-20 visible-xs-inline-block'
21
+ - unless presenter.cart_empty?
22
+ = button_to 'Checkout', presenter.checkout_path, method: :get, class: 'btn btn-default mb-20 hidden-xs center-block'
@@ -0,0 +1,7 @@
1
+ - [:first_name, :last_name, :street, :city, :zip].each do |field|
2
+ = sf.input field
3
+
4
+ = sf.input :country_id, collection: presenter.countries, selected: presenter.selected_country_id(type)
5
+
6
+ = sf.input :phone, input_html: { 'data-inputmask': "'mask': '+999 99 999 9999'" }
7
+
@@ -0,0 +1,12 @@
1
+ %h1.general-title-margin Checkout
2
+
3
+ %ul.steps.list-inline
4
+ - presenter(:progress).steps.each do |step|
5
+ %li{ class: step[:classes] }
6
+ %span.step-number= step[:number]
7
+ %span.step-text.hidden-xs= step[:name]
8
+ - if step[:divider]
9
+ %li.step-divider
10
+
11
+ = yield
12
+
@@ -0,0 +1,37 @@
1
+ = render layout: 'shopper/checkout/layout' do
2
+ = form_for current_order, url: wizard_path, method: :put, class: 'res-mb-100' do |f|
3
+ .hidden-xs.hidden-sm
4
+ .row
5
+ .col-md-5
6
+ %h3.general-subtitle= t('billing_address')
7
+ %p.general-info-text= t('simple_form.titles.required_fields')
8
+ .col-md-5.col-md-offset-1
9
+ %h3.general-subtitle= t('shipping_address')
10
+ .row
11
+ .col-md-5.mb-40
12
+ .visible-xs.visible-sm
13
+ %h3.general-subtitle= t('billing_address')
14
+ %p.general-info-text= t('simple_form.titles.required_fields')
15
+ = f.simple_fields_for :billing, presenter.billing do |sf|
16
+ = render partial: 'address_fields', locals: { sf: sf, type: :billing }
17
+ .col-md-5.col-md-offset-1.mb-60
18
+ .visible-xs.visible-sm
19
+ %h3.general-subtitle= t('shipping_address')
20
+ = f.simple_fields_for :shipping, presenter.shipping do |sf|
21
+ #shipping_address_fields{style: presenter.shipping_fields_style}
22
+ = render partial: 'address_fields', locals: { sf: sf, type: :shipping }
23
+ = fields_for 'order' do |ub|
24
+ .form-group.checkbox{'data-slide-toggle-for': '#shipping_address_fields'}
25
+ %label.font-weight-light.pl-0
26
+ = ub.check_box 'use_billing',
27
+ checked: presenter.use_billing,
28
+ class: 'checkbox-input',
29
+ hidden: true
30
+ %span.checkbox-icon
31
+ %i.fa.fa-check
32
+ %span.checkbox-text= t('simple_form.labels.address.use_billing')
33
+
34
+ .general-text-align.mb-60
35
+ = render partial: 'shopper/orders/summary', locals: { order: presenter.order_summary }
36
+ = f.submit t('simple_form.titles.save_and_continue'),
37
+ class: 'btn btn-default center-block mb-20'
@@ -0,0 +1,17 @@
1
+ = render layout: 'shopper/checkout/layout' do
2
+ .text-center.mb-40
3
+ %h3.general-subtitle= t('checkout.complete.thankyou')
4
+ %p.fw-300= t('checkout.complete.sent_to', email: presenter.email)
5
+ .row.mb-20
6
+ .col-sm-6
7
+ .visible-xs
8
+ %p.general-order-number.mb-0= presenter.order_number
9
+ %p.general-address.mb-30= presenter.order_date
10
+ %p.general-address= presenter.address
11
+ .col-sm-6.hidden-xs.text-right
12
+ %p.general-order-number.mb-0= presenter.order_number
13
+ %p.general-address.mb-30= presenter.order_date
14
+
15
+ = render partial: 'shopper/order_item/table', locals: { items: presenter.items }
16
+ = render partial: 'shopper/orders/summary', locals: { order: presenter.order_summary }
17
+
@@ -0,0 +1,6 @@
1
+ = render layout: 'shopper/checkout/layout' do
2
+ = render partial: 'shopper/orders/details', locals: { order: presenter.order_details }
3
+ = render partial: 'shopper/order_item/table', locals: { items: presenter.items }
4
+ = render partial: 'shopper/orders/summary', locals: { order: presenter.order_summary }
5
+ .text-center
6
+ = link_to t('simple_form.titles.place_order'), checkout_path(:complete), class: 'btn btn-default mb-20'
@@ -0,0 +1,35 @@
1
+ = render layout: 'shopper/checkout/layout' do
2
+ %h3.general-subtitle= t('shipping_method')
3
+ .hidden-xs.mb-res-50
4
+ = form_for current_order, url: wizard_path, method: :put, class: 'res-mb-100' do |f|
5
+ %table.table.table-hover
6
+ %thead
7
+ %tr
8
+ %th.col-method
9
+ %span.in-grey-600.ml-40= t('delivery.title')
10
+ %th
11
+ %span.in-grey-600= t('delivery.days')
12
+ %th
13
+ %span.in-grey-600= t('delivery.price')
14
+ %tbody
15
+ - presenter.deliveries.each do |delivery|
16
+ %tr
17
+ %td
18
+ .form-group.radio.mt-0.mb-0
19
+ %label.radio-label
20
+ = radio_button_tag :delivery_id,
21
+ delivery.id,
22
+ delivery.selected?,
23
+ class: 'radio-input',
24
+ hidden: true
25
+ %span.radio-icon
26
+ %span.radio-text= delivery.title
27
+ %td
28
+ %span= delivery.days
29
+ %td
30
+ %span.font-16.in-gold-500= delivery.price
31
+ .general-text-align
32
+ = render partial: 'shopper/orders/summary', locals: { order: presenter.order_summary }
33
+ .text-center
34
+ = f.submit t('simple_form.titles.save_and_continue'),
35
+ class: 'btn btn-default center-block mb-20'
@@ -0,0 +1,20 @@
1
+ = render layout: 'shopper/checkout/layout' do
2
+ %h3.general-subtitle Credit Card
3
+ = form_for current_order, url: wizard_path, method: :put, class: 'max-600 mb-80' do |f|
4
+ = f.simple_fields_for :card, presenter.card do |sf|
5
+ = sf.input :number, input_html: { 'data-inputmask': "'mask': '9999 9999 9999 9999'" }
6
+ .row
7
+ .col-sm-6
8
+ = sf.input :name
9
+ .col-sm-3
10
+ = sf.input :expiration_date, as: :string, input_html: { 'data-inputmask': "'mask': '99/99'" }
11
+ .col-sm-3
12
+ = sf.input :cvv, wrapper_html: { class: 'relative' } do
13
+ = sf.input_field :cvv, class: 'form-control', as: :string, 'data-inputmask': "'mask': '999'"
14
+ %i.fa.fa-question-circle.general-form-help.font-18{'data-tipsy': true, 'title': t('checkout.cvv-help')}
15
+ .general-text-align
16
+ = render partial: 'shopper/orders/summary', locals: { order: presenter.order_summary }
17
+ .text-center
18
+ = f.submit t('simple_form.titles.save_and_continue'),
19
+ class: 'btn btn-default center-block mb-20'
20
+
@@ -0,0 +1,5 @@
1
+ %p Hello #{@user.email}
2
+ %p You have just completed order #{@order.number}
3
+ %p Book will be soon delivered
4
+
5
+ = link_to 'Order details', @order
@@ -0,0 +1,4 @@
1
+ -# .hidden-xs
2
+ = render partial: 'shopper/order_item/table_desktop', locals: { items: items }
3
+ -# .visible-xs
4
+ -# = render partial: 'shopper/order_item/table_phone', locals: { items: items }
@@ -0,0 +1,40 @@
1
+ %table.table.table-hover
2
+ %tr.no-hover
3
+ %th.col-pic.pl-0
4
+ %span.in-grey-600 Product
5
+ %th.col-title
6
+ %th.col-price
7
+ %span.in-grey-600 Price
8
+ %th.col-quantity
9
+ %span.in-grey-600 Quantity
10
+ %th.col-total
11
+ %span.in-grey-600 SubTotal
12
+ %th.col-close
13
+ - items.each do |item|
14
+ = fields_for 'items[]', item do |item_fields|
15
+ %tr
16
+ %td
17
+ .general-img-wrap-table
18
+ = image_tag item.cover, class: 'cart-img-shadow pull-left width-full'
19
+ %td
20
+ %p.general-title= item.title
21
+ - if item.description
22
+ %p.in-grey-600.font-weight-light= item.description
23
+ %td
24
+ %span.font-16.in-gold-500= item.price
25
+ %td
26
+ .input-group.general-position{class: item.error_class, data: {quantity: true}}
27
+ - if item.editable
28
+ %a.input-link{href: "#"}
29
+ %i.fa.fa-minus.line-height-40
30
+ = item_fields.text_field :quantity, class: 'form-control quantity-input'
31
+ %a.input-link{href: "#"}
32
+ %i.fa.fa-plus.line-height-40
33
+ - else
34
+ %span.ml-30= item.quantity
35
+ %td
36
+ %span.font-16.in-gold-500= item.subtotal
37
+ - if item.editable
38
+ %td
39
+ = link_to order_item_path(item), method: :delete, class: 'close general-cart-close' do
40
+ %span{'aria-hidden': "true"} &times;