From 4d39dba85a461508b376456b9d2fbe672ca371ce Mon Sep 17 00:00:00 2001 From: Luuk Veenis Date: Tue, 22 Nov 2016 10:50:43 -0800 Subject: [PATCH 1/7] Make the gem a mountable engine to isolate routes Currently we are appending all our routes to the host application's routes, which can cause conflicts with existing ones. This change makes it so the gem's engine should be mounted in the host application's config/routes.rb and keeps the engine's routes inside of their own namespace. The host application can still use the gem's routes, but it must refer to them explicitly using the routing proxy. ie: `solidus_paypal_braintree.store_configuration_path` vs `store_configuration_path`. One issue encountered is that Solidus doesn't always use the `spree.` routing proxy explicitly. If a page is rendered from a controller inside the gem, any url helpers that don't explicitly use a routing proxy will be looked up in the gem's routes causing errors. The RoutesHelper module was added to deal with that. When included in a controller, the method_missing will retry the url helper using the spree routing proxy and return that result if it exists, otherwise it will raise the error. --- .../solidus_paypal_braintree/routes_helper.rb | 15 +++++++++++++++ config/routes.rb | 10 ++++------ .../install/install_generator.rb | 6 ++++++ lib/solidus_paypal_braintree/engine.rb | 3 +-- .../checkouts_controller_spec.rb | 2 ++ .../client_tokens_controller_spec.rb | 2 ++ .../transactions_controller_spec.rb | 2 ++ 7 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 app/helpers/solidus_paypal_braintree/routes_helper.rb diff --git a/app/helpers/solidus_paypal_braintree/routes_helper.rb b/app/helpers/solidus_paypal_braintree/routes_helper.rb new file mode 100644 index 00000000..60dfcb10 --- /dev/null +++ b/app/helpers/solidus_paypal_braintree/routes_helper.rb @@ -0,0 +1,15 @@ +module SolidusPaypalBraintree + module RoutesHelper + def method_missing(method_sym, *arguments, &block) + if spree.respond_to?(method_sym) + spree.send(method_sym, arguments) + else + super + end + end + + def respond_to_missing?(method_sym, include_private = false) + spree.respond_to?(method_sym) || super + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 8cd58dd6..8aa8f8e3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,5 @@ -Rails.application.routes.draw do - namespace :solidus_paypal_braintree do - resource :checkout, only: [:update, :edit] - resource :client_token, only: [:create], format: :json - resource :transactions, only: [:create] - end +SolidusPaypalBraintree::Engine.routes.draw do + resource :checkout, only: [:update, :edit] + resource :client_token, only: [:create], format: :json + resource :transactions, only: [:create] end diff --git a/lib/generators/solidus_paypal_braintree/install/install_generator.rb b/lib/generators/solidus_paypal_braintree/install/install_generator.rb index 85597ac0..4990ef4a 100644 --- a/lib/generators/solidus_paypal_braintree/install/install_generator.rb +++ b/lib/generators/solidus_paypal_braintree/install/install_generator.rb @@ -18,6 +18,12 @@ def add_migrations run 'bundle exec rake railties:install:migrations FROM=solidus_paypal_braintree' end + def mount_engine + insert_into_file File.join('config', 'routes.rb'), after: "Rails.application.routes.draw do\n" do + "mount SolidusPaypalBraintree::Engine, at: '/solidus_paypal_braintree'" + end + end + def run_migrations run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]')) if run_migrations diff --git a/lib/solidus_paypal_braintree/engine.rb b/lib/solidus_paypal_braintree/engine.rb index bcd356ee..d50b5453 100644 --- a/lib/solidus_paypal_braintree/engine.rb +++ b/lib/solidus_paypal_braintree/engine.rb @@ -1,7 +1,6 @@ module SolidusPaypalBraintree class Engine < Rails::Engine - require 'spree/core' - isolate_namespace Spree + isolate_namespace SolidusPaypalBraintree engine_name 'solidus_paypal_braintree' # use rspec for tests diff --git a/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb b/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb index e99839dc..cfca2b31 100644 --- a/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb +++ b/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb @@ -2,6 +2,8 @@ require 'support/order_ready_for_payment' RSpec.describe SolidusPaypalBraintree::CheckoutsController, type: :controller do + routes { SolidusPaypalBraintree::Engine.routes } + include_context 'order ready for payment' describe 'PATCH update', vcr: { cassette_name: 'checkout/update' } do diff --git a/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb b/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb index e400d478..79fc67de 100644 --- a/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb +++ b/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe SolidusPaypalBraintree::ClientTokensController do + routes { SolidusPaypalBraintree::Engine.routes } + cassette_options = { cassette_name: "braintree/token" } describe "POST create", vcr: cassette_options do let!(:gateway) { create_gateway } diff --git a/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb b/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb index 6159a5e8..f468b66f 100644 --- a/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb +++ b/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' RSpec.describe SolidusPaypalBraintree::TransactionsController, type: :controller do + routes { SolidusPaypalBraintree::Engine.routes } + include_context "order ready for payment" let(:payment_method) { create_gateway } From 33991645f84319f2b499b8cb2be9440830f7bc8b Mon Sep 17 00:00:00 2001 From: Luuk Veenis Date: Mon, 14 Nov 2016 15:19:38 -0800 Subject: [PATCH 2/7] Add Braintree Configuration model --- app/models/solidus_paypal_braintree/configuration.rb | 5 +++++ app/models/spree/store_decorator.rb | 11 +++++++++++ ..._create_solidus_paypal_braintree_configurations.rb | 11 +++++++++++ 3 files changed, 27 insertions(+) create mode 100644 app/models/solidus_paypal_braintree/configuration.rb create mode 100644 app/models/spree/store_decorator.rb create mode 100644 db/migrate/20161114231422_create_solidus_paypal_braintree_configurations.rb diff --git a/app/models/solidus_paypal_braintree/configuration.rb b/app/models/solidus_paypal_braintree/configuration.rb new file mode 100644 index 00000000..bddbc798 --- /dev/null +++ b/app/models/solidus_paypal_braintree/configuration.rb @@ -0,0 +1,5 @@ +class SolidusPaypalBraintree::Configuration < ApplicationRecord + belongs_to :store, class_name: 'Spree::Store' + + validates :store, presence: true +end diff --git a/app/models/spree/store_decorator.rb b/app/models/spree/store_decorator.rb new file mode 100644 index 00000000..82349416 --- /dev/null +++ b/app/models/spree/store_decorator.rb @@ -0,0 +1,11 @@ +Spree::Store.class_eval do + has_one :braintree_configuration, class_name: "SolidusPaypalBraintree::Configuration" + + before_create :build_default_configuration + + private + + def build_default_configuration + build_braintree_configuration + end +end diff --git a/db/migrate/20161114231422_create_solidus_paypal_braintree_configurations.rb b/db/migrate/20161114231422_create_solidus_paypal_braintree_configurations.rb new file mode 100644 index 00000000..bde4a1e5 --- /dev/null +++ b/db/migrate/20161114231422_create_solidus_paypal_braintree_configurations.rb @@ -0,0 +1,11 @@ +class CreateSolidusPaypalBraintreeConfigurations < ActiveRecord::Migration + def change + create_table :solidus_paypal_braintree_configurations do |t| + t.boolean :paypal, null: false, default: false + t.boolean :apple_pay, null: false, default: false + t.integer :store_id, null: false, index: true, foreign_key: { references: :spree_stores } + + t.timestamps null: false + end + end +end From eba73f603ed6b00a813547c805ce3a1396d64c98 Mon Sep 17 00:00:00 2001 From: Luuk Veenis Date: Tue, 15 Nov 2016 14:22:05 -0800 Subject: [PATCH 3/7] Add empty configurations controller and routes --- config/routes.rb | 2 ++ .../solidus_paypal_braintree/configurations_controller.rb | 4 ++++ lib/solidus_paypal_braintree/engine.rb | 8 ++++++++ .../configurations_controller_spec.rb | 4 ++++ 4 files changed, 18 insertions(+) create mode 100644 lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb create mode 100644 spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb diff --git a/config/routes.rb b/config/routes.rb index 8aa8f8e3..4ebc28cb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,4 +2,6 @@ resource :checkout, only: [:update, :edit] resource :client_token, only: [:create], format: :json resource :transactions, only: [:create] + + resources :configurations, only: [] end diff --git a/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb b/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb new file mode 100644 index 00000000..ec612116 --- /dev/null +++ b/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb @@ -0,0 +1,4 @@ +module SolidusPaypalBraintree + class ConfigurationsController < Spree::Admin::BaseController + end +end diff --git a/lib/solidus_paypal_braintree/engine.rb b/lib/solidus_paypal_braintree/engine.rb index d50b5453..a5a7fddc 100644 --- a/lib/solidus_paypal_braintree/engine.rb +++ b/lib/solidus_paypal_braintree/engine.rb @@ -24,6 +24,10 @@ def self.frontend_available? defined?(Spree::Frontend::Engine) == "constant" end + def self.backend_available? + defined?(Spree::Backend::Engine) == "constant" + end + if frontend_available? config.assets.precompile += [ 'spree/frontend/solidus_paypal_braintree', @@ -32,5 +36,9 @@ def self.frontend_available? paths["app/controllers"] << "lib/controllers/frontend" paths["app/views"] << "lib/views/frontend" end + + if backend_available? + paths["app/controllers"] << "lib/controllers/backend" + end end end diff --git a/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb b/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb new file mode 100644 index 00000000..dcaba639 --- /dev/null +++ b/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb @@ -0,0 +1,4 @@ +require 'spec_helper' + +RSpec.describe SolidusPaypalBraintree::CheckoutsController, type: :controller do +end From dad16aaff5844858c2bdc14c832cc47e8493d0c5 Mon Sep 17 00:00:00 2001 From: Luuk Veenis Date: Wed, 23 Nov 2016 11:41:29 -0800 Subject: [PATCH 4/7] Add list and update actions/views for configs To simplify the interface for Braintree configurations, I added one page that lists the configurations for all stores and allows users to update them all at the same time. --- config/locales/en.yml | 4 ++ config/routes.rb | 7 +- .../configurations_controller.rb | 26 +++++++ lib/solidus_paypal_braintree/engine.rb | 1 + .../configurations/list.html.erb | 26 +++++++ .../configurations_controller_spec.rb | 68 ++++++++++++++++++- 6 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb diff --git a/config/locales/en.yml b/config/locales/en.yml index cdec84f8..b22fe3e8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -5,3 +5,7 @@ en: payment_type: apple_pay_card: Apple Pay pay_pal_account: PayPal + configurations: + title: Braintree Configurations + update_success: Successfully updated Braintree configurations. + update_error: An error occurred while updating Braintree configurations. diff --git a/config/routes.rb b/config/routes.rb index 4ebc28cb..89fb2aed 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,5 +3,10 @@ resource :client_token, only: [:create], format: :json resource :transactions, only: [:create] - resources :configurations, only: [] + resources :configurations do + collection do + get :list + post :update + end + end end diff --git a/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb b/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb index ec612116..410f0908 100644 --- a/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb +++ b/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb @@ -1,4 +1,30 @@ module SolidusPaypalBraintree class ConfigurationsController < Spree::Admin::BaseController + helper RoutesHelper + + def list + authorize! :list, SolidusPaypalBraintree::Configuration + + @configurations = Spree::Store.all.map(&:braintree_configuration) + end + + def update + authorize! :update, SolidusPaypalBraintree::Configuration + + params = configurations_params[:configuration_fields] + if SolidusPaypalBraintree::Configuration.update(params.keys, params.values) + flash[:success] = t('update_success', scope: 'solidus_paypal_braintree.configurations') + else + flash[:error] = t('update_error', scope: 'solidus_paypal_braintree.configurations') + end + redirect_to action: :list + end + + private + + def configurations_params + params.require(:configurations). + permit(configuration_fields: [:paypal, :apple_pay]) + end end end diff --git a/lib/solidus_paypal_braintree/engine.rb b/lib/solidus_paypal_braintree/engine.rb index a5a7fddc..5a2c9b2e 100644 --- a/lib/solidus_paypal_braintree/engine.rb +++ b/lib/solidus_paypal_braintree/engine.rb @@ -39,6 +39,7 @@ def self.backend_available? if backend_available? paths["app/controllers"] << "lib/controllers/backend" + paths["app/views"] << "lib/views/backend" end end end diff --git a/lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb b/lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb new file mode 100644 index 00000000..12128af2 --- /dev/null +++ b/lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb @@ -0,0 +1,26 @@ +<% content_for :page_title do %> + <%= t(:title, scope: 'solidus_paypal_braintree.configurations') %> +<% end %> + +<%= form_for :configurations, url: solidus_paypal_braintree.configurations_path do |f| %> + <% @configurations.each do |config| %> +
+
+

<%= config.store.name %>

+ + <%= f.fields_for 'configuration_fields[]', config do |c| %> +
+ <%= c.label :paypal %> + <%= c.check_box :paypal %> +
+
+ <%= c.label :apple_pay %> + <%= c.check_box :apple_pay %> +
+ <% end %> +
+
+ <% end %> + + <%= submit_tag %> +<% end %> diff --git a/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb b/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb index dcaba639..87b3e6cb 100644 --- a/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb +++ b/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb @@ -1,4 +1,70 @@ require 'spec_helper' -RSpec.describe SolidusPaypalBraintree::CheckoutsController, type: :controller do +describe SolidusPaypalBraintree::ConfigurationsController, type: :controller do + routes { SolidusPaypalBraintree::Engine.routes } + + let!(:store_1) { create :store } + let!(:store_2) { create :store } + let(:store_1_config) { store_1.braintree_configuration } + let(:store_2_config) { store_2.braintree_configuration } + + stub_authorization! + + describe "GET #list" do + subject { get :list } + + it "assigns all store's configurations as @configurations" do + subject + expect(assigns(:configurations)). + to eq [store_1.braintree_configuration, store_2.braintree_configuration] + end + + it "renders the correct view" do + expect(subject).to render_template :list + end + end + + describe "POST #update" do + let(:configurations_params) do + { + configurations: { + configuration_fields: { + store_1_config.id.to_s => { paypal: true, apple_pay: true }, + store_2_config.id.to_s => { paypal: true, apple_pay: false } + } + } + } + end + + subject { post :update, configurations_params } + + context "with valid parameters" do + it "updates the configuration" do + expect { subject }.to change { store_1_config.reload.paypal }. + from(false).to(true) + end + + it "displays a success message to the user" do + subject + expect(flash[:success]).to eq "Successfully updated Braintree configurations." + end + + it "displays all configurations" do + expect(subject).to redirect_to action: :list + end + end + + context "with invalid parameters" do + before { allow(SolidusPaypalBraintree::Configuration).to receive(:update) { false } } + + it "displays an error message to the user" do + subject + expect(flash[:error]).to eq "An error occurred while updating Braintree configurations." + end + + it "returns the user to the edit page" do + expect(subject).to redirect_to action: :list + end + end + end end From 3118f4175eb4347fa2798f42342bf5321cc921a3 Mon Sep 17 00:00:00 2001 From: Luuk Veenis Date: Thu, 24 Nov 2016 14:39:42 -0800 Subject: [PATCH 5/7] Override admin nav to include Braintree tab This adds a "Braintree" tab to the admin settings sub-menu. --- app/overrides/admin_navigation_menu.rb | 6 ++++++ config/locales/en.yml | 5 +++++ .../configurations/_admin_tab.html.erb | 3 +++ 3 files changed, 14 insertions(+) create mode 100644 app/overrides/admin_navigation_menu.rb create mode 100644 lib/views/backend/solidus_paypal_braintree/configurations/_admin_tab.html.erb diff --git a/app/overrides/admin_navigation_menu.rb b/app/overrides/admin_navigation_menu.rb new file mode 100644 index 00000000..ca2fe726 --- /dev/null +++ b/app/overrides/admin_navigation_menu.rb @@ -0,0 +1,6 @@ +Deface::Override.new( + virtual_path: "spree/admin/shared/_settings_sub_menu", + name: "solidus_paypal_braintree_admin_navigation_configuration", + insert_bottom: "[data-hook='admin_settings_sub_tabs']", + partial: "solidus_paypal_braintree/configurations/admin_tab" +) diff --git a/config/locales/en.yml b/config/locales/en.yml index b22fe3e8..4ea86026 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,4 +1,8 @@ en: + spree: + admin: + tab: + braintree: Braintree solidus_paypal_braintree: nonce: Nonce token: Token @@ -7,5 +11,6 @@ en: pay_pal_account: PayPal configurations: title: Braintree Configurations + tab: Braintree update_success: Successfully updated Braintree configurations. update_error: An error occurred while updating Braintree configurations. diff --git a/lib/views/backend/solidus_paypal_braintree/configurations/_admin_tab.html.erb b/lib/views/backend/solidus_paypal_braintree/configurations/_admin_tab.html.erb new file mode 100644 index 00000000..1145efc2 --- /dev/null +++ b/lib/views/backend/solidus_paypal_braintree/configurations/_admin_tab.html.erb @@ -0,0 +1,3 @@ +<%= tab :braintree, match_path: /braintree\/configurations/, + url: solidus_paypal_braintree.list_configurations_path +%> From 54148ca38474529e0ea6ebfcfaaa6384057fac85 Mon Sep 17 00:00:00 2001 From: Luuk Veenis Date: Fri, 25 Nov 2016 09:25:16 -0800 Subject: [PATCH 6/7] Add default configuration to stores in a migration --- ...161125172005_add_braintree_configuration_to_stores.rb | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 db/migrate/20161125172005_add_braintree_configuration_to_stores.rb diff --git a/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb b/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb new file mode 100644 index 00000000..cd61993f --- /dev/null +++ b/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb @@ -0,0 +1,9 @@ +class AddBraintreeConfigurationToStores < ActiveRecord::Migration + def up + Spree::Store.all.each(&:create_braintree_configuration) + end + + def down + SolidusPaypalBraintree::Configuration.joins(:store).destroy_all + end +end From 6f72a04b39dbb7b9e27ec82aa02bb45c69a68103 Mon Sep 17 00:00:00 2001 From: Luuk Veenis Date: Mon, 9 Jan 2017 10:44:43 -0800 Subject: [PATCH 7/7] Update README with store configuration description --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index a5ddb884..98f10447 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,23 @@ bundle bundle exec rails g solidus_paypal_braintree:install ``` +Store Configuration +------------------- + +This gem adds a configuration model - `SolidusPaypalBraintree::Configuration` - +that belongs to `Spree::Store` as `braintree_configuration`. In multi-store +Solidus applications, this model allows admins to enable/disable payment types +on a per-store basis. + +The migrations for this gem will add a default configuration to all stores that +has each payment type disabled. It also adds a `before_create` callback to +`Spree::Store` that builds a default configuration. You can customize the +default configuration that gets created by overriding the private +`build_default_configuration` method on `Spree::Store`. + +A view override is provided that adds a `Braintree` tab to the admin settings +submenu. Admins can go here to edit the configuration for each store. + Testing -------