solidus_paypal_commerce_platform 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +41 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.gitignore +20 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +56 -0
  8. data/Gemfile +33 -0
  9. data/LICENSE +26 -0
  10. data/README.md +173 -0
  11. data/Rakefile +6 -0
  12. data/app/assets/javascripts/spree/backend/solidus_paypal_commerce_platform.js +2 -0
  13. data/app/assets/javascripts/spree/backend/solidus_paypal_commerce_platform/namespace.js +1 -0
  14. data/app/assets/javascripts/spree/backend/solidus_paypal_commerce_platform/wizard.js +28 -0
  15. data/app/assets/javascripts/spree/frontend/solidus_paypal_commerce_platform.js +3 -0
  16. data/app/assets/javascripts/spree/frontend/solidus_paypal_commerce_platform/button_actions.js +191 -0
  17. data/app/assets/javascripts/spree/frontend/solidus_paypal_commerce_platform/buttons.js +26 -0
  18. data/app/assets/javascripts/spree/frontend/solidus_paypal_commerce_platform/namespace.js +1 -0
  19. data/app/assets/stylesheets/spree/backend/solidus_paypal_commerce_platform.css +4 -0
  20. data/app/assets/stylesheets/spree/frontend/solidus_paypal_commerce_platform.css +17 -0
  21. data/app/controllers/solidus_paypal_commerce_platform/orders_controller.rb +83 -0
  22. data/app/controllers/solidus_paypal_commerce_platform/payments_controller.rb +44 -0
  23. data/app/controllers/solidus_paypal_commerce_platform/paypal_orders_controller.rb +19 -0
  24. data/app/controllers/solidus_paypal_commerce_platform/shipping_rates_controller.rb +31 -0
  25. data/app/controllers/solidus_paypal_commerce_platform/wizard_controller.rb +47 -0
  26. data/app/decorators/solidus_paypal_commerce_platform/remove_required_phone_from_address.rb +13 -0
  27. data/app/helpers/solidus_paypal_commerce_platform/button_options_helper.rb +21 -0
  28. data/app/jobs/solidus_paypal_commerce_platform/application_job.rb +6 -0
  29. data/app/jobs/solidus_paypal_commerce_platform/webhook_job.rb +22 -0
  30. data/app/models/solidus_paypal_commerce_platform/gateway.rb +88 -0
  31. data/app/models/solidus_paypal_commerce_platform/payment_method.rb +75 -0
  32. data/app/models/solidus_paypal_commerce_platform/payment_source.rb +27 -0
  33. data/app/models/solidus_paypal_commerce_platform/paypal_address.rb +81 -0
  34. data/app/models/solidus_paypal_commerce_platform/paypal_order.rb +107 -0
  35. data/app/models/solidus_paypal_commerce_platform/pricing_options.rb +9 -0
  36. data/app/models/solidus_paypal_commerce_platform/wizard.rb +40 -0
  37. data/app/overrides/spree/admin/payment_methods/form/insert_email_verification_notification.rb +9 -0
  38. data/app/overrides/spree/admin/payment_methods/index/insert_setup_wizards.rb +17 -0
  39. data/app/overrides/spree/admin/payments/log_entries/add_paypal_debug_id.rb +9 -0
  40. data/app/overrides/spree/orders/edit/insert_cart_buttons.rb +9 -0
  41. data/app/overrides/spree/payments/payment/add_paypal_email_to_payment.rb +9 -0
  42. data/app/overrides/spree/products/cart_form/insert_product_buttons.rb +10 -0
  43. data/app/views/solidus_paypal_commerce_platform/admin/payment_methods/_available_to_users.html.erb +6 -0
  44. data/app/views/solidus_paypal_commerce_platform/admin/payment_methods/_paypal_wizard.html.erb +16 -0
  45. data/app/views/solidus_paypal_commerce_platform/admin/payment_methods/_setup_wizards.html.erb +16 -0
  46. data/app/views/solidus_paypal_commerce_platform/admin/payments/_log_entries.html.erb +32 -0
  47. data/bin/console +17 -0
  48. data/bin/rails +7 -0
  49. data/bin/rails-engine +13 -0
  50. data/bin/rails-sandbox +18 -0
  51. data/bin/rake +7 -0
  52. data/bin/sandbox +86 -0
  53. data/bin/setup +8 -0
  54. data/codecov.yml +14 -0
  55. data/config/locales/en.yml +37 -0
  56. data/config/routes.rb +12 -0
  57. data/db/migrate/20200521190038_add_paypal_commerce_platform_sources.rb +13 -0
  58. data/lib/generators/solidus_paypal_commerce_platform/install/install_generator.rb +38 -0
  59. data/lib/solidus_paypal_commerce_platform.rb +21 -0
  60. data/lib/solidus_paypal_commerce_platform/access_token_authorization_request.rb +21 -0
  61. data/lib/solidus_paypal_commerce_platform/client.rb +84 -0
  62. data/lib/solidus_paypal_commerce_platform/configuration.rb +64 -0
  63. data/lib/solidus_paypal_commerce_platform/engine.rb +45 -0
  64. data/lib/solidus_paypal_commerce_platform/factories.rb +14 -0
  65. data/lib/solidus_paypal_commerce_platform/fetch_merchant_credentials_request.rb +17 -0
  66. data/lib/solidus_paypal_commerce_platform/version.rb +5 -0
  67. data/lib/views/api/spree/api/payments/source_views/_paypal_commerce_platform.json.jbuilder +3 -0
  68. data/lib/views/backend/spree/admin/payments/source_forms/_paypal_commerce_platform.html.erb +1 -0
  69. data/lib/views/backend/spree/admin/payments/source_views/_paypal_commerce_platform.html.erb +18 -0
  70. data/lib/views/backend/spree/admin/shared/preference_fields/_paypal_select.html.erb +13 -0
  71. data/lib/views/frontend/solidus_paypal_commerce_platform/cart/_cart_buttons.html.erb +3 -0
  72. data/lib/views/frontend/solidus_paypal_commerce_platform/payments/_payment.html.erb +4 -0
  73. data/lib/views/frontend/solidus_paypal_commerce_platform/product/_product_buttons.html.erb +3 -0
  74. data/lib/views/frontend/spree/checkout/payment/_paypal_commerce_platform.html.erb +13 -0
  75. data/lib/views/frontend/spree/orders/payment/_paypal_commerce_platform.html.erb +17 -0
  76. data/lib/views/frontend/spree/products/payment/_paypal_commerce_platform.html.erb +15 -0
  77. data/solidus_paypal_commerce_platform.gemspec +39 -0
  78. data/spec/features/backend/new_payment_method_spec.rb +40 -0
  79. data/spec/features/frontend/cart_spec.rb +38 -0
  80. data/spec/features/frontend/checkout_spec.rb +81 -0
  81. data/spec/features/frontend/product_spec.rb +79 -0
  82. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/CHECKOUT.ORDER.COMPLETED.v2.json +121 -0
  83. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/CHECKOUT.ORDER.PROCESSED.v2.json +121 -0
  84. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.COMPLETED.v1.json +50 -0
  85. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.COMPLETED.v2.json +72 -0
  86. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.DENIED.v1.json +50 -0
  87. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.DENIED.v2.json +68 -0
  88. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.REFUNDED.v1.json +51 -0
  89. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.REFUNDED.v2.json +63 -0
  90. data/spec/jobs/solidus_paypal_commerce_platform/webhook_job_spec.rb +44 -0
  91. data/spec/lib/solidus_paypal_commerce_platform/client_spec.rb +21 -0
  92. data/spec/lib/solidus_paypal_commerce_platform/configuration_spec.rb +67 -0
  93. data/spec/models/solidus_paypal_commerce_platform/payment_method_spec.rb +102 -0
  94. data/spec/models/solidus_paypal_commerce_platform/payment_source_spec.rb +62 -0
  95. data/spec/models/solidus_paypal_commerce_platform/paypal_address_spec.rb +55 -0
  96. data/spec/models/solidus_paypal_commerce_platform/wizard_spec.rb +9 -0
  97. data/spec/requests/solidus_paypal_commerce_platform/orders_controller_spec.rb +36 -0
  98. data/spec/requests/solidus_paypal_commerce_platform/shipping_rates_controller_spec.rb +44 -0
  99. data/spec/requests/solidus_paypal_commerce_platform/wizard_controller_spec.rb +44 -0
  100. data/spec/spec_helper.rb +30 -0
  101. metadata +258 -0
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalCommercePlatform
4
+ class PaymentSource < SolidusSupport.payment_source_parent_class
5
+ self.table_name = "paypal_commerce_platform_sources"
6
+ validates :paypal_order_id, :payment_method_id, presence: true
7
+
8
+ def actions
9
+ %w(capture void credit)
10
+ end
11
+
12
+ def can_capture?(payment)
13
+ payment.pending? || payment.checkout?
14
+ end
15
+
16
+ def can_void?(payment)
17
+ can_capture?(payment)
18
+ end
19
+
20
+ def can_credit?(payment)
21
+ payment.completed? &&
22
+ payment.credit_allowed > 0 &&
23
+ capture_id &&
24
+ payment_method.gateway.get_order(paypal_order_id).status == "COMPLETED"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalCommercePlatform
4
+ class PaypalAddress
5
+ attr_reader :errors
6
+
7
+ def initialize(order)
8
+ @order = order
9
+ end
10
+
11
+ def simulate_update(paypal_address)
12
+ @order.update(ship_address: format_simulated_address(paypal_address))
13
+
14
+ return unless @order.ship_address.valid?
15
+
16
+ @order.ensure_updated_shipments
17
+ @order.email = "info@solidus.io" unless @order.email
18
+ @order.contents.advance
19
+ end
20
+
21
+ def update(paypal_address)
22
+ formatted_address = format_address(paypal_address)
23
+ new_address = @order.ship_address.dup || ::Spree::Address.new
24
+ new_address.assign_attributes(formatted_address)
25
+
26
+ add_email_to_order(paypal_address[:recipient]) unless @order.email
27
+
28
+ if new_address.save
29
+ @order.update(ship_address_id: new_address.id)
30
+ # Also setting billing address if it doesn't already exist
31
+ @order.update(bill_address_id: new_address.id) unless @order.bill_address
32
+ else
33
+ @errors = new_address.errors
34
+ end
35
+
36
+ new_address
37
+ end
38
+
39
+ private
40
+
41
+ def add_email_to_order(recipient)
42
+ @order.update(email: recipient[:email_address])
43
+ end
44
+
45
+ def format_simulated_address(paypal_address)
46
+ country = ::Spree::Country.find_by(iso: paypal_address[:country_code])
47
+ # Also adds fake information for a few fields, so validations can run
48
+ ::Spree::Address.new(
49
+ city: paypal_address[:city],
50
+ state: country.states.find_by(abbr: paypal_address[:state]),
51
+ state_name: paypal_address[:state],
52
+ zipcode: paypal_address[:postal_code],
53
+ country: country,
54
+ address1: "123 Fake St.",
55
+ phone: "123456789",
56
+ firstname: "Fake"
57
+ )
58
+ end
59
+
60
+ def format_address(paypal_address)
61
+ address = paypal_address[:updated_address]
62
+ recipient = paypal_address[:recipient]
63
+ country = ::Spree::Country.find_by(iso: address[:country_code])
64
+ state = country.states.where(abbr: address[:admin_area_1]).or(
65
+ country.states.where(name: address[:admin_area_1])
66
+ ).first
67
+
68
+ {
69
+ address1: address[:address_line_1],
70
+ address2: address[:address_line_2],
71
+ state: state,
72
+ state_name: address[:admin_area_1],
73
+ city: address[:admin_area_2],
74
+ country: country,
75
+ zipcode: address[:postal_code],
76
+ firstname: recipient[:name][:given_name],
77
+ lastname: recipient[:name][:surname]
78
+ }
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalCommercePlatform
4
+ class PaypalOrder
5
+ def initialize(order)
6
+ @order = order
7
+ end
8
+
9
+ def to_json(intent)
10
+ {
11
+ intent: intent,
12
+ purchase_units: purchase_units,
13
+ payer: (payer if @order.bill_address)
14
+ }
15
+ end
16
+
17
+ def to_replace_json
18
+ {
19
+ op: 'replace',
20
+ path: '/purchase_units/@reference_id==\'default\'',
21
+ value: purchase_units[0]
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ def get_address(address)
28
+ {
29
+ address_line_1: address.address1,
30
+ address_line_2: address.address2,
31
+ admin_area_1: address.state_text,
32
+ admin_area_2: address.city,
33
+ postal_code: address.zipcode,
34
+ country_code: address.country.iso,
35
+ }
36
+ end
37
+
38
+ def payer
39
+ {
40
+ name: name(@order.bill_address),
41
+ email_address: @order.email,
42
+ address: get_address(@order.bill_address)
43
+ }
44
+ end
45
+
46
+ def name(address)
47
+ {
48
+ given_name: address.firstname,
49
+ surname: address.lastname
50
+ }
51
+ end
52
+
53
+ def purchase_units
54
+ [
55
+ {
56
+ amount: amount,
57
+ items: line_items,
58
+ shipping: (shipping_info if @order.ship_address)
59
+ }
60
+ ]
61
+ end
62
+
63
+ def shipping_info
64
+ {
65
+ name: {
66
+ full_name: @order.ship_address.full_name
67
+ },
68
+ email_address: @order.email,
69
+ address: get_address(@order.ship_address)
70
+ }
71
+ end
72
+
73
+ def line_items
74
+ @order.line_items.map{ |line_item|
75
+ {
76
+ name: line_item.product.name,
77
+ unit_amount: price(line_item.price),
78
+ quantity: line_item.quantity
79
+ }
80
+ }
81
+ end
82
+
83
+ def amount
84
+ {
85
+ currency_code: @order.currency,
86
+ value: @order.total,
87
+ breakdown: breakdown
88
+ }
89
+ end
90
+
91
+ def breakdown
92
+ {
93
+ item_total: price(@order.item_total),
94
+ shipping: price(@order.shipment_total),
95
+ tax_total: price(@order.additional_tax_total),
96
+ discount: price(@order.all_adjustments.promotion.sum(&:amount).abs)
97
+ }
98
+ end
99
+
100
+ def price(amount)
101
+ {
102
+ currency_code: @order.currency,
103
+ value: amount
104
+ }
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusPaypalCommercePlatform
4
+ class PricingOptions < Spree::Variant::PricingOptions
5
+ def cache_key
6
+ SolidusPaypalCommercePlatform::PaymentMethod.active.map(&:cache_key_with_version).sort + [super]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module SolidusPaypalCommercePlatform
6
+ class Wizard
7
+ def name
8
+ I18n.t('start_paying_with_paypal')
9
+ end
10
+
11
+ def partial_name
12
+ '/solidus_paypal_commerce_platform/admin/payment_methods/paypal_wizard'
13
+ end
14
+
15
+ def button_url
16
+ parameters = {
17
+ product: "addipmt",
18
+ partnerId: SolidusPaypalCommercePlatform.config.partner_id,
19
+ partnerClientId: SolidusPaypalCommercePlatform.config.partner_client_id,
20
+ features: "PAYMENT,REFUND",
21
+ partnerLogoUrl: logo,
22
+ integrationType: "FO",
23
+ displayMode: "minibrowser",
24
+ sellerNonce: nonce,
25
+ }
26
+
27
+ URI("https://#{SolidusPaypalCommercePlatform.config.env_domain}/bizsignup/partner/entry?#{parameters.to_query}")
28
+ end
29
+
30
+ def nonce
31
+ @nonce ||= SecureRandom.alphanumeric(128)
32
+ end
33
+
34
+ private
35
+
36
+ def logo
37
+ ActionController::Base.helpers.image_url(::Spree::Config[:admin_interface_logo])
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ Deface::Override.new(
4
+ name: "payment_methods/form/insert_email_verification_notification",
5
+ virtual_path: "spree/admin/payment_methods/_form",
6
+ original: "471a995989ea93a58118c80be38fafcd1fd3ee69",
7
+ replace: "erb[loud]:contains('f.check_box :available_to_users')",
8
+ partial: "solidus_paypal_commerce_platform/admin/payment_methods/available_to_users"
9
+ )
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ Deface::Override.new(
4
+ name: "payment_methods/index/insert_setup_wizards#list",
5
+ virtual_path: "spree/admin/payment_methods/index",
6
+ original: "bf5a94b3b0967d88b88f72bc26131897ff85a1d0",
7
+ insert_after: "#listing_payment_methods",
8
+ partial: 'solidus_paypal_commerce_platform/admin/payment_methods/setup_wizards'
9
+ )
10
+
11
+ Deface::Override.new(
12
+ name: "payment_methods/index/insert_setup_wizards#empty",
13
+ virtual_path: "spree/admin/payment_methods/index",
14
+ original: "318d918b15df51842a5d98a6230bea96ffa05142",
15
+ insert_after: ".no-objects-found",
16
+ partial: 'solidus_paypal_commerce_platform/admin/payment_methods/setup_wizards'
17
+ )
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ Deface::Override.new(
4
+ name: "payments/log_entries/add_paypal_debug_id",
5
+ virtual_path: "spree/admin/payments/_log_entries",
6
+ original: "5eb2952fa4ba1484a63e5abf53e4ed735761a7b2",
7
+ replace: "#listing_log_entries",
8
+ partial: "solidus_paypal_commerce_platform/admin/payments/log_entries"
9
+ )
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ Deface::Override.new(
4
+ name: "orders/edit/insert_cart_buttons",
5
+ virtual_path: "spree/orders/edit",
6
+ original: "6af87f9fd7740e8f8493c7e8d875690c49fe2adb",
7
+ insert_after: "[data-hook='cart_items']",
8
+ partial: 'solidus_paypal_commerce_platform/cart/cart_buttons'
9
+ )
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ Deface::Override.new(
4
+ name: "payments/payment/add_paypal_email_to_payment",
5
+ virtual_path: "spree/payments/_payment",
6
+ original: "0b5b5ae53626059cb3a202ef95d10827dd399925",
7
+ insert_after: "erb[loud]:contains('content_tag(:span, payment.payment_method.name)')",
8
+ partial: "solidus_paypal_commerce_platform/payments/payment"
9
+ )
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ Deface::Override.new(
4
+ name: "products/cart_form/insert_product_buttons",
5
+ virtual_path: "spree/products/_cart_form",
6
+ insert_bottom: "[data-hook='product_price']",
7
+ original: '09b5c0504202d92adbfb0f92d6bf12fc17752295',
8
+ source: :partial,
9
+ partial: 'solidus_paypal_commerce_platform/product/product_buttons'
10
+ )
@@ -0,0 +1,6 @@
1
+ <% if @payment_method.type == "SolidusPaypalCommercePlatform::PaymentMethod" %>
2
+ <%= f.check_box :available_to_users %>
3
+ <%= f.field_hint :available_to_users %>
4
+ <% else %>
5
+ <%= f.check_box :available_to_users %>
6
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <a
2
+ target="_blank"
3
+ data-paypal-onboard-complete='SolidusPaypalCommercePlatform_onboardCallback'
4
+ data-paypal-button="true"
5
+ href="<%= wizard.button_url %>"
6
+ >
7
+ <button class="btn btn-primary">
8
+ <%= t('sign_up_for_paypal') %>
9
+ </button>
10
+ </a>
11
+ <script id="paypal-js" src="https://<%= SolidusPaypalCommercePlatform.config.env_domain %>/webapps/merchantboarding/js/lib/lightbox/partner.js"></script>
12
+
13
+ <script>
14
+ window.SolidusPaypalCommercePlatform.nonce = "<%= wizard.nonce %>"
15
+ window.SolidusPaypalCommercePlatform_onboardCallback = SolidusPaypalCommercePlatform.onboardCallback
16
+ </script>
@@ -0,0 +1,16 @@
1
+ <% if can?(:create, Spree::PaymentMethod) %>
2
+ <hr>
3
+ <div class="setup-wizards">
4
+ <% wizards = Rails.application.config.spree.payment_setup_wizards %>
5
+ <% wizards.map(&:new).each do |wizard| %>
6
+ <div class="setup-wizard">
7
+ <div class="setup-wizard-name">
8
+ <h5><%= wizard.name %></h5>
9
+ </div>
10
+ <div class="setup-wizard-body">
11
+ <%= render partial: wizard.partial_name, locals: { wizard: wizard } %>
12
+ </div>
13
+ </div>
14
+ <% end %>
15
+ </div>
16
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <table class='index' id='listing_log_entries'>
2
+ <colgroup>
3
+ <col style="width: 30%" />
4
+ <col style="width: 40%" />
5
+ <col style="width: 20%" />
6
+ <col style="width: 10%" />
7
+ </colgroup>
8
+ <thead>
9
+ <tr>
10
+ <th><%= Spree::LogEntry.human_attribute_name(:created_at) %></th>
11
+ <th><%= Spree::LogEntry.human_attribute_name(:details) %></th>
12
+ <th><%= I18n.t("paypal_debug_id") %></th>
13
+ <th></th>
14
+ </tr>
15
+ </thead>
16
+ <tbody>
17
+ <% log_entries.each do |entry| %>
18
+ <tr class="log_entry <%= entry.parsed_details.success? ? 'success' : 'fail' %>">
19
+ <td>
20
+ <%= pretty_time(entry.created_at) %>
21
+ </td>
22
+ <td>
23
+ <pre><%= entry.parsed_details.message %></pre>
24
+ </td>
25
+ <td><%= entry.parsed_details.params["paypal_debug_id"] %></td>
26
+ <td class="text-right">
27
+ <i class='fa fa-<%= entry.parsed_details.success? ? 'check' : 'remove' %>'></i>
28
+ </td>
29
+ </tr>
30
+ <% end %>
31
+ </tbody>
32
+ </table>
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require "bundler/setup"
6
+ require "solidus_paypal_commerce_platform"
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+ $LOAD_PATH.unshift(*Dir["#{__dir__}/../app/*"])
11
+
12
+ # (If you use this, don't forget to add pry to your Gemfile!)
13
+ # require "pry"
14
+ # Pry.start
15
+
16
+ require "irb"
17
+ IRB.start(__FILE__)
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if %w[g generate].include? ARGV.first
4
+ exec "#{__dir__}/rails-engine", *ARGV
5
+ else
6
+ exec "#{__dir__}/rails-sandbox", *ARGV
7
+ end