shoppper 0.1.0

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