disco_app 0.5.6 → 0.6.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/disco_app/icon.svg +1 -0
  3. data/app/assets/stylesheets/disco_app/{bootstrap-custom.scss → bootstrap/_custom.scss} +3 -4
  4. data/app/assets/stylesheets/disco_app/bootstrap/_variables.scss +872 -0
  5. data/app/assets/stylesheets/disco_app/disco/_buttons.scss +31 -0
  6. data/app/assets/stylesheets/disco_app/disco/_cards.scss +43 -0
  7. data/app/assets/stylesheets/disco_app/disco/_forms.scss +23 -0
  8. data/app/assets/stylesheets/disco_app/disco/_sections.scss +61 -0
  9. data/app/assets/stylesheets/disco_app/disco/_type.scss +21 -0
  10. data/app/assets/stylesheets/disco_app/disco/mixins/_flexbox.scss +394 -0
  11. data/app/assets/stylesheets/disco_app/disco_app.scss +12 -6
  12. data/app/controllers/disco_app/authenticated_controller.rb +1 -1
  13. data/app/controllers/disco_app/install_controller.rb +1 -1
  14. data/app/controllers/disco_app/webhooks_controller.rb +11 -9
  15. data/app/jobs/disco_app/app_installed_job.rb +1 -1
  16. data/app/jobs/disco_app/app_uninstalled_job.rb +2 -17
  17. data/app/jobs/disco_app/concerns/app_uninstalled_job.rb +19 -0
  18. data/app/jobs/disco_app/shop_job.rb +1 -1
  19. data/app/jobs/disco_app/shop_update_job.rb +1 -1
  20. data/app/models/disco_app/concerns/plan.rb +14 -0
  21. data/app/models/disco_app/concerns/shop.rb +62 -0
  22. data/app/models/disco_app/concerns/subscription.rb +14 -0
  23. data/app/models/disco_app/plan.rb +3 -0
  24. data/app/models/disco_app/session_storage.rb +18 -0
  25. data/app/models/disco_app/shop.rb +2 -43
  26. data/app/models/disco_app/subscription.rb +3 -0
  27. data/app/services/disco_app/subscription_service.rb +25 -0
  28. data/app/services/disco_app/webhook_service.rb +30 -0
  29. data/app/views/disco_app/shared/_card.html.erb +16 -0
  30. data/app/views/disco_app/shared/_section.html.erb +17 -0
  31. data/{lib/generators/disco_app/templates → app}/views/layouts/embedded_app.html.erb +14 -6
  32. data/db/migrate/20150525000000_create_shops_if_not_existent.rb +15 -0
  33. data/db/migrate/20151017231302_create_disco_app_plans.rb +13 -0
  34. data/db/migrate/20151017232027_create_disco_app_subscriptions.rb +15 -0
  35. data/db/migrate/20151017234409_move_shop_to_disco_app_engine.rb +5 -0
  36. data/lib/disco_app/engine.rb +5 -0
  37. data/lib/disco_app/version.rb +1 -1
  38. data/lib/generators/disco_app/disco_app_generator.rb +31 -54
  39. data/lib/generators/disco_app/mailify/mailify_generator.rb +55 -0
  40. data/lib/generators/disco_app/templates/initializers/shopify_session_repository.rb +7 -0
  41. data/test/controllers/disco_app/install_controller_test.rb +50 -0
  42. data/test/controllers/disco_app/webhooks_controller_test.rb +58 -0
  43. data/test/controllers/home_controller_test.rb +61 -0
  44. data/test/dummy/app/assets/javascripts/application.js +4 -0
  45. data/test/dummy/app/assets/stylesheets/application.scss +5 -0
  46. data/test/dummy/app/controllers/application_controller.rb +1 -0
  47. data/test/dummy/app/controllers/home_controller.rb +7 -0
  48. data/test/dummy/app/jobs/disco_app/app_uninstalled_job.rb +11 -0
  49. data/test/dummy/app/models/disco_app/shop.rb +15 -0
  50. data/test/dummy/app/views/home/index.html.erb +2 -0
  51. data/test/dummy/config/application.rb +11 -0
  52. data/test/dummy/config/environments/production.rb +7 -1
  53. data/test/dummy/config/initializers/disco_app.rb +1 -0
  54. data/test/dummy/config/initializers/omniauth.rb +9 -0
  55. data/test/dummy/config/initializers/shopify_app.rb +7 -0
  56. data/test/dummy/config/initializers/shopify_session_repository.rb +7 -0
  57. data/test/dummy/config/routes.rb +5 -1
  58. data/test/dummy/config/secrets.yml +2 -2
  59. data/test/dummy/db/schema.rb +70 -0
  60. data/test/fixtures/api/widget_store/shop.json +46 -0
  61. data/test/fixtures/disco_app/plans.yml +32 -0
  62. data/test/fixtures/disco_app/shops.yml +10 -0
  63. data/test/fixtures/disco_app/subscriptions.yml +26 -0
  64. data/test/fixtures/webhooks/app_uninstalled.json +46 -0
  65. data/test/jobs/disco_app/app_installed_job_test.rb +29 -0
  66. data/test/jobs/disco_app/app_uninstalled_job_test.rb +32 -0
  67. data/test/models/disco_app/plan_test.rb +5 -0
  68. data/test/models/disco_app/shop_test.rb +26 -0
  69. data/test/models/disco_app/subscription_test.rb +6 -0
  70. data/test/services/disco_app/subscription_service_test.rb +28 -0
  71. data/test/support/test_file_fixtures.rb +29 -0
  72. data/test/test_helper.rb +32 -1
  73. metadata +148 -30
  74. data/app/assets/stylesheets/disco_app/bootstrap-variables.scss +0 -12
  75. data/lib/generators/disco_app/templates/jobs/app_installed_job.rb +0 -2
  76. data/lib/generators/disco_app/templates/jobs/app_uninstalled_job.rb +0 -2
  77. data/lib/generators/disco_app/templates/jobs/shop_update_job.rb +0 -2
  78. data/lib/generators/disco_app/templates/models/shop.rb +0 -3
  79. data/test/dummy/README.rdoc +0 -28
  80. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  81. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  82. data/test/lib/generators/disco_app/disco_app_generator_test.rb +0 -16
  83. /data/{lib/generators/disco_app/templates → app}/views/layouts/application.html.erb +0 -0
  84. /data/{lib/generators/disco_app/templates → app}/views/sessions/new.html.erb +0 -0
@@ -0,0 +1,19 @@
1
+ module DiscoApp::Concerns::AppUninstalledJob
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+
6
+ before_enqueue { @shop.awaiting_uninstall! }
7
+ before_perform { @shop.uninstalling! }
8
+ after_perform { @shop.uninstalled! }
9
+
10
+ end
11
+
12
+ def perform(domain, shop_data)
13
+ # Mark the shop's charge status as "cancelled" unless charges have been waived.
14
+ unless @shop.charge_waived?
15
+ @shop.charge_cancelled!
16
+ end
17
+ end
18
+
19
+ end
@@ -16,7 +16,7 @@ module DiscoApp
16
16
  private
17
17
 
18
18
  def find_shop(job)
19
- @shop ||= ::Shop.find_by!(shopify_domain: job.arguments.first)
19
+ @shop ||= Shop.find_by!(shopify_domain: job.arguments.first)
20
20
  end
21
21
 
22
22
  def shop_context(job, block)
@@ -9,7 +9,7 @@ module DiscoApp
9
9
  shop_data = HashWithIndifferentAccess.new(shop_data)
10
10
 
11
11
  # Update model attributes present in both our model and the data hash.
12
- @shop.update_attributes(shop_data.except(:id, :created_at).slice(*::Shop.column_names))
12
+ @shop.update_attributes(shop_data.except(:id, :created_at).slice(*DiscoApp::Shop.column_names))
13
13
  end
14
14
 
15
15
  end
@@ -0,0 +1,14 @@
1
+ module DiscoApp::Concerns::Plan
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+
6
+ has_many :subscriptions
7
+ has_many :shops, through: :subscriptions
8
+
9
+ enum status: [:available, :unavailable, :hidden]
10
+
11
+ scope :available, -> { where status: statuses[:available] }
12
+
13
+ end
14
+ end
@@ -0,0 +1,62 @@
1
+ module DiscoApp::Concerns::Shop
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include ShopifyApp::Shop
6
+
7
+ # Define relationships to plans and subscriptions.
8
+ has_many :subscriptions
9
+ has_many :plans, through: :subscriptions
10
+
11
+ # Define possible installation statuses as an enum.
12
+ enum status: [:never_installed, :awaiting_install, :installing, :installed, :awaiting_uninstall, :uninstalling, :uninstalled]
13
+
14
+ # Define possible charge statuses as an enum.
15
+ enum charge_status: [:charge_none, :charge_pending, :charge_accepted, :charge_declined, :charge_active, :charge_cancelled, :charge_waived]
16
+
17
+ # Define some useful scopes.
18
+ scope :status, -> (status) { where status: status }
19
+ scope :installed, -> { where status: statuses[:installed] }
20
+ scope :has_active_shopify_plan, -> { where.not(plan_name: [:cancelled, :frozen]) }
21
+
22
+ # Alias 'with_shopify_session' as 'temp', as per our existing conventions.
23
+ alias_method :temp, :with_shopify_session
24
+
25
+ # Return a hash of attributes that should be used to create a new charge for this shop.
26
+ # This method can be overridden by the inheriting Shop class in order to provide charges
27
+ # customised to a particular shop. Otherwise, the default settings configured in application.rb
28
+ # will be used.
29
+ def new_charge_attributes
30
+ {
31
+ type: Rails.configuration.x.shopify_charges_default_type,
32
+ name: Rails.configuration.x.shopify_app_name,
33
+ price: Rails.configuration.x.shopify_charges_default_price,
34
+ trial_days: Rails.configuration.x.shopify_charges_default_trial_days,
35
+ }
36
+ end
37
+
38
+ # Update this Shop's charge_status attribute based on the given Shopify charge object.
39
+ def update_charge_status(shopify_charge)
40
+ status_update_method_name = "charge_#{shopify_charge.status}!"
41
+ self.public_send(status_update_method_name) if self.respond_to? status_update_method_name
42
+ end
43
+
44
+ # Convenience method to get the currently active subscription for this Shop.
45
+ def current_subscription
46
+ subscriptions.active.first
47
+ end
48
+
49
+ # Return the absolute URL to the shop's storefront.
50
+ # @TODO: Account for HTTPS.
51
+ def url
52
+ "http://#{domain}"
53
+ end
54
+
55
+ # Return the absolute URL to the shop's admin.
56
+ def admin_url
57
+ "https://#{shopify_domain}/admin"
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,14 @@
1
+ module DiscoApp::Concerns::Subscription
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+
6
+ belongs_to :shop
7
+ belongs_to :plan
8
+
9
+ enum status: [:active, :replaced, :cancelled]
10
+
11
+ scope :active, -> { where status: statuses[:active] }
12
+
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ class DiscoApp::Plan < ActiveRecord::Base
2
+ include DiscoApp::Concerns::Plan
3
+ end
@@ -0,0 +1,18 @@
1
+ module DiscoApp
2
+ class SessionStorage
3
+ def self.store(session)
4
+ shop = Shop.find_or_initialize_by(shopify_domain: session.url)
5
+ shop.shopify_token = session.token
6
+ shop.save!
7
+ shop.id
8
+ end
9
+
10
+ def self.retrieve(id)
11
+ return unless id
12
+ shop = Shop.find(id)
13
+ ShopifyAPI::Session.new(shop.shopify_domain, shop.shopify_token)
14
+ rescue ActiveRecord::RecordNotFound
15
+ nil
16
+ end
17
+ end
18
+ end
@@ -1,44 +1,3 @@
1
- module DiscoApp
2
- module Shop
3
- extend ActiveSupport::Concern
4
-
5
- # Include the base ShopifyApp functionality.
6
- include ShopifyApp::Shop
7
-
8
- included do
9
- # Define possible installation statuses as an enum.
10
- enum status: [:never_installed, :awaiting_install, :installing, :installed, :awaiting_uninstall, :uninstalling, :uninstalled]
11
-
12
- # Define possible charge statuses as an enum.
13
- enum charge_status: [:charge_none, :charge_pending, :charge_accepted, :charge_declined, :charge_active, :charge_cancelled, :charge_waived]
14
-
15
- # Define some useful scopes.
16
- scope :status, -> (status) { where status: status }
17
- scope :installed, -> { where status: ::Shop.statuses[:installed] }
18
- scope :has_active_shopify_plan, -> { where.not(plan_name: [:cancelled, :frozen]) }
19
-
20
- # Alias 'with_shopify_session' as 'temp', as per our existing conventions.
21
- alias_method :temp, :with_shopify_session
22
- end
23
-
24
- # Return a hash of attributes that should be used to create a new charge for this shop.
25
- # This method can be overridden by the inheriting Shop class in order to provide charges
26
- # customised to a particular shop. Otherwise, the default settings configured in application.rb
27
- # will be used.
28
- def new_charge_attributes
29
- {
30
- type: Rails.configuration.x.shopify_charges_default_type,
31
- name: Rails.configuration.x.shopify_app_name,
32
- price: Rails.configuration.x.shopify_charges_default_price,
33
- trial_days: Rails.configuration.x.shopify_charges_default_trial_days,
34
- }
35
- end
36
-
37
- # Update this Shop's charge_status attribute based on the given Shopify charge object.
38
- def update_charge_status(shopify_charge)
39
- status_update_method_name = "charge_#{shopify_charge.status}!"
40
- self.public_send(status_update_method_name) if self.respond_to? status_update_method_name
41
- end
42
-
43
- end
1
+ class DiscoApp::Shop < ActiveRecord::Base
2
+ include DiscoApp::Concerns::Shop
44
3
  end
@@ -0,0 +1,3 @@
1
+ class DiscoApp::Subscription < ActiveRecord::Base
2
+ include DiscoApp::Concerns::Subscription
3
+ end
@@ -0,0 +1,25 @@
1
+ class DiscoApp::SubscriptionService
2
+
3
+ # Subscribe the given shop to the given plan.
4
+ def self.subscribe(shop, plan)
5
+ # Mark all existing active subscriptions as replaced.
6
+ shop.subscriptions.active.update_all(status: DiscoApp::Subscription.statuses[:replaced])
7
+
8
+ # Add the new subscription.
9
+ DiscoApp::Subscription.create!(
10
+ shop: shop,
11
+ plan: plan,
12
+ status: DiscoApp::Subscription.statuses[:active],
13
+ name: plan.name,
14
+ charge_type: plan.charge_type,
15
+ price: plan.default_price,
16
+ trial_days: plan.default_trial_days
17
+ )
18
+ end
19
+
20
+ # Cancel any active subscription for the given shop.
21
+ def self.cancel(shop)
22
+ shop.subscriptions.active.update_all(status: DiscoApp::Subscription.statuses[:cancelled])
23
+ end
24
+
25
+ end
@@ -0,0 +1,30 @@
1
+ class DiscoApp::WebhookService
2
+
3
+ # Return true iff the provided hmac_to_verify matches that calculated from the
4
+ # give data and secret.
5
+ def self.is_valid_hmac?(body, secret, hmac_to_verify)
6
+ self.calculated_hmac(body, secret) == hmac_to_verify
7
+ end
8
+
9
+ # Calculate the HMAC for the given data and secret.
10
+ def self.calculated_hmac(body, secret)
11
+ digest = OpenSSL::Digest.new('sha256')
12
+ Base64.encode64(OpenSSL::HMAC.digest(digest, secret, body)).strip
13
+ end
14
+
15
+ # Try to find a job class for the given webhook topic.
16
+ def self.find_job_class(topic)
17
+ begin
18
+ # First try to find a top-level matching job class.
19
+ "#{topic}_job".gsub('/', '_').classify.constantize
20
+ rescue NameError
21
+ # If that fails, try to find a DiscoApp:: prefixed job class.
22
+ begin
23
+ %Q{DiscoApp::#{"#{topic}_job".gsub('/', '_').classify}}.constantize
24
+ rescue NameError
25
+ nil
26
+ end
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,16 @@
1
+ <% disabled ||= false %>
2
+ <div class="next-card <% if disabled %>next-card--disabled<% end %>">
3
+ <% if content_for?(:card_header) %>
4
+ <header class="next-card__header">
5
+ <%= content_for :card_header %>
6
+ </header>
7
+ <% end %>
8
+ <section class="next-card__section">
9
+ <%= content_for :card_content %>
10
+ </section>
11
+ <% if content_for?(:card_footer) %>
12
+ <footer class="next-card__footer">
13
+ <%= content_for :card_footer %>
14
+ </footer>
15
+ <% end %>
16
+ </div>
@@ -0,0 +1,17 @@
1
+ <section class="section">
2
+ <div class="layout-content">
3
+
4
+ <aside class="layout-content__sidebar layout-content__first">
5
+ <% if content_for?(:section_summary) %>
6
+ <div class="section-summary">
7
+ <%= content_for :section_summary %>
8
+ </div>
9
+ <% end %>
10
+ </aside>
11
+
12
+ <section class="layout-content__main">
13
+ <%= content_for :section_content %>
14
+ </section>
15
+
16
+ </div>
17
+ </section>
@@ -5,17 +5,12 @@
5
5
 
6
6
  <script src="//cdn.shopify.com/s/assets/external/app.js?<%= Time.now.strftime('%Y%m%d%H') %>"></script>
7
7
  <script type="text/javascript">
8
+ // Initialise the Shopify App.
8
9
  ShopifyApp.init({
9
10
  "apiKey": "<%= ShopifyApp.configuration.api_key %>",
10
11
  "shopOrigin": "<%= "https://#{ @shop_session.url }" if @shop_session %>",
11
12
  "debug": <%= Rails.env.development? ? 'true' : 'false' %>
12
13
  });
13
-
14
- ShopifyApp.ready(function() {
15
- ShopifyApp.Bar.initialize({
16
- title: "<%= yield(:title) %>"
17
- });
18
- });
19
14
  </script>
20
15
 
21
16
  <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
@@ -25,9 +20,22 @@
25
20
  <%= yield :extra_head %>
26
21
  </head>
27
22
  <body>
23
+ <script type="text/javascript">
24
+ ShopifyApp.Bar.initialize({
25
+ title: "<%= yield(:title) %>",
26
+ icon: "<%= image_url("disco_app/icon.svg") %>",
27
+ buttons: <%= content_for?(:buttons) ? content_for(:buttons) : '{}' %>
28
+ });
29
+ </script>
28
30
 
29
31
  <%= yield %>
30
32
 
33
+ <% flash.each do |key, message| %>
34
+ <script type="text/javascript">
35
+ ShopifyApp.flash<%= (key == 'error') ? 'Error' : 'Notice' %>('<%= message %>');
36
+ </script>
37
+ <% end %>
38
+
31
39
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
32
40
  </body>
33
41
  </html>
@@ -0,0 +1,15 @@
1
+ class CreateShopsIfNotExistent < ActiveRecord::Migration
2
+
3
+ def change
4
+ unless table_exists? :shops or table_exists? :disco_app_shops
5
+ create_table :shops do |t|
6
+ t.string :shopify_domain, null: false
7
+ t.string :shopify_token, null: false
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :shops, :shopify_domain, unique: true
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,13 @@
1
+ class CreateDiscoAppPlans < ActiveRecord::Migration
2
+ def change
3
+ create_table :disco_app_plans do |t|
4
+ t.integer :status
5
+ t.string :name
6
+ t.integer :charge_type
7
+ t.decimal :default_price
8
+ t.integer :default_trial_days
9
+
10
+ t.timestamps null: false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ class CreateDiscoAppSubscriptions < ActiveRecord::Migration
2
+ def change
3
+ create_table :disco_app_subscriptions do |t|
4
+ t.belongs_to :shop, index: true
5
+ t.belongs_to :plan, index: true
6
+ t.integer :status
7
+ t.string :name
8
+ t.integer :charge_type
9
+ t.decimal :price
10
+ t.integer :trial_days
11
+
12
+ t.timestamps null: false
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ class MoveShopToDiscoAppEngine < ActiveRecord::Migration
2
+ def change
3
+ rename_table :shops, :disco_app_shops
4
+ end
5
+ end
@@ -1,3 +1,8 @@
1
+ require 'shopify_app'
2
+ require 'bootstrap-sass'
3
+ require 'jquery-rails'
4
+ require 'turbolinks'
5
+
1
6
  module DiscoApp
2
7
  class Engine < ::Rails::Engine
3
8
  isolate_namespace DiscoApp
@@ -1,3 +1,3 @@
1
1
  module DiscoApp
2
- VERSION = "0.5.6"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -27,14 +27,14 @@ class DiscoAppGenerator < Rails::Generators::Base
27
27
  gsub_file 'Gemfile', /^# Use sqlite3 as the database for Active Record\ngem 'sqlite3'/m, ''
28
28
 
29
29
  # Add gems common to all environments.
30
- gem 'shopify_app', '~> 6.1.0'
31
- gem 'sidekiq', '~> 3.5.0'
32
- gem 'puma', '~> 2.11.3'
30
+ gem 'shopify_app', '~> 6.2.0'
31
+ gem 'sidekiq', '~> 3.5.1'
32
+ gem 'puma', '~> 2.14.0'
33
33
  gem 'bootstrap-sass', '~> 3.3.5.1'
34
34
 
35
35
  # Add gems for development and testing only.
36
36
  gem_group :development, :test do
37
- gem 'sqlite3', '~> 1.3.10'
37
+ gem 'sqlite3', '~> 1.3.11'
38
38
  gem 'dotenv-rails', '~> 2.0.2'
39
39
  gem 'minitest-reporters', '~> 1.0.19'
40
40
  gem 'guard', '~> 2.13.0'
@@ -43,7 +43,7 @@ class DiscoAppGenerator < Rails::Generators::Base
43
43
 
44
44
  # Add gems for production only.
45
45
  gem_group :production do
46
- gem 'pg', '~> 0.18.2'
46
+ gem 'pg', '~> 0.18.3'
47
47
  gem 'rails_12factor', '~> 0.0.3'
48
48
  end
49
49
  end
@@ -86,45 +86,18 @@ class DiscoAppGenerator < Rails::Generators::Base
86
86
  # Create Rakefiles
87
87
  def create_rakefiles
88
88
  rakefile 'start.rake' do
89
- %Q{
89
+ <<-RAKEFILE.strip_heredoc
90
90
  task start: :environment do
91
91
  system 'bundle exec rails server -b 127.0.0.1 -p 3000'
92
92
  end
93
- }
93
+ RAKEFILE
94
94
  end
95
95
  rakefile 'console.rake' do
96
- %Q{
96
+ <<-RAKEFILE.strip_heredoc
97
97
  task console: :environment do
98
98
  system 'bundle exec rails console'
99
99
  end
100
- }
101
- end
102
- end
103
-
104
- # Run shopify_app:install and shopify_app:shop_model
105
- def shopify_app_install
106
- generate 'shopify_app:install'
107
- generate 'shopify_app:shop_model'
108
- end
109
-
110
- # Set up initializers, overriding some of the defaults generated by shopify_app:install and shopify_app:shop_model
111
- def setup_initializers
112
- ['shopify_app', 'disco_app'].each do |initializer_name|
113
- copy_file "initializers/#{initializer_name}.rb", "config/initializers/#{initializer_name}.rb"
114
- end
115
- end
116
-
117
- # Set up models, overriding some of the defaults generated by shopify_app:install and shopify_app:shop_model
118
- def setup_models
119
- ['shop'].each do |model_name|
120
- copy_file "models/#{model_name}.rb", "app/models/#{model_name}.rb"
121
- end
122
- end
123
-
124
- # Set up default jobs.
125
- def setup_jobs
126
- ['app_installed', 'app_uninstalled', 'shop_update'].each do |job_name|
127
- copy_file "jobs/#{job_name}_job.rb", "app/jobs/#{job_name}_job.rb"
100
+ RAKEFILE
128
101
  end
129
102
  end
130
103
 
@@ -133,29 +106,33 @@ class DiscoAppGenerator < Rails::Generators::Base
133
106
  route "mount DiscoApp::Engine, at: '/'"
134
107
  end
135
108
 
136
- # Set up controllers.
137
- def setup_controllers
138
- ['home'].each do |controller_name|
139
- copy_file "controllers/#{controller_name}_controller.rb", "app/controllers/#{controller_name}_controller.rb"
140
- end
109
+ # Run shopify_app:install
110
+ def shopify_app_install
111
+ generate 'shopify_app:install'
141
112
  end
142
113
 
143
- # Set up views.
144
- def setup_views
145
- ['layouts/application', 'layouts/embedded_app', 'home/index', 'sessions/new'].each do |view_name|
146
- copy_file "views/#{view_name}.html.erb", "app/views/#{view_name}.html.erb"
147
- end
148
- end
114
+ # Copy template files to the appropriate location. In some cases, we'll be
115
+ # overwriting or removing existing files or those created by ShopifyApp.
116
+ def copy_and_remove_files
117
+ # Copy initializers
118
+ copy_file 'initializers/shopify_app.rb', 'config/initializers/shopify_app.rb'
119
+ copy_file 'initializers/disco_app.rb', 'config/initializers/disco_app.rb'
120
+ copy_file 'initializers/shopify_session_repository.rb', 'config/initializers/shopify_session_repository.rb'
149
121
 
150
- # Set up assets.
151
- def setup_assets
152
- # Copy over our default assets.
153
- ['javascripts/application.js', 'stylesheets/application.scss'].each do |asset_name|
154
- copy_file "assets/#{asset_name}", "app/assets/#{asset_name}"
155
- end
122
+ # Copy default home controller and view
123
+ copy_file 'controllers/home_controller.rb', 'app/controllers/home_controller.rb'
124
+ copy_file 'views/home/index.html.erb', 'app/views/home/index.html.erb'
156
125
 
157
- # Make sure application.css is removed.
126
+ # Copy assets
127
+ copy_file 'assets/javascripts/application.js', 'app/assets/javascripts/application.js'
128
+ copy_file 'assets/stylesheets/application.scss', 'app/assets/stylesheets/application.scss'
129
+
130
+ # Remove application.css
158
131
  remove_file 'app/assets/stylesheets/application.css'
132
+
133
+ # Remove the layout files created by ShopifyApp
134
+ remove_file 'app/views/layout/application.html.erb'
135
+ remove_file 'app/views/layout/embedded_app.html.erb'
159
136
  end
160
137
 
161
138
  # Copy engine migrations over.
@@ -0,0 +1,55 @@
1
+ module DiscoApp
2
+ module Generators
3
+ class MailifyGenerator < Rails::Generators::Base
4
+
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ # Install the react-rails gem and run its setup.
8
+ def install_gem
9
+ # Add premailer gem to Gemfile.
10
+ gem 'premailer-rails', '~> 1.8.2'
11
+
12
+ # Add explicit dependency on Nokogiri
13
+ gem 'nokogiri', '~> 1.6.6.1'
14
+
15
+ # Add the Mailgun rails gem (production only)
16
+ gem_group :production do
17
+ gem 'mailgun_rails', '~> 0.7.0'
18
+ end
19
+
20
+ # Install gem.
21
+ Bundler.with_clean_env do
22
+ run 'bundle install'
23
+ end
24
+ end
25
+
26
+ # Set application configuration
27
+ def configure_application
28
+ configuration = <<-CONFIG.strip_heredoc
29
+
30
+ # Configure ActionMailer to use MailGun
31
+ if ENV['MAILGUN_API_KEY']
32
+ config.action_mailer.delivery_method = :mailgun
33
+ config.action_mailer.mailgun_settings = {
34
+ api_key: ENV['MAILGUN_API_KEY'],
35
+ domain: ENV['MAILGUN_API_DOMAIN']
36
+ }
37
+ end
38
+ CONFIG
39
+ application configuration, env: :production
40
+ end
41
+
42
+ # Add entries to .env and .env.sample
43
+ def add_env_variables
44
+ configuration = <<-CONFIG.strip_heredoc
45
+
46
+ MAILGUN_API_KEY=
47
+ MAILGUN_API_DOMAIN=
48
+ CONFIG
49
+ append_to_file '.env', configuration
50
+ append_to_file '.env.sample', configuration
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,7 @@
1
+ if Rails.configuration.cache_classes
2
+ ShopifyApp::SessionRepository.storage = DiscoApp::SessionStorage
3
+ else
4
+ ActionDispatch::Reloader.to_prepare do
5
+ ShopifyApp::SessionRepository.storage = DiscoApp::SessionStorage
6
+ end
7
+ end
@@ -0,0 +1,50 @@
1
+ require 'test_helper'
2
+
3
+ class DiscoApp::InstallControllerTest < ActionController::TestCase
4
+ include ActiveJob::TestHelper
5
+
6
+ def setup
7
+ @shop = disco_app_shops(:widget_store)
8
+ @routes = DiscoApp::Engine.routes
9
+ log_in_as(@shop)
10
+ end
11
+
12
+ def teardown
13
+ @shop = nil
14
+ end
15
+
16
+ test 'logged-in but uninstalled user triggers installation from install page' do
17
+ get(:install)
18
+ assert_redirected_to :installing
19
+ assert_enqueued_jobs 1
20
+ @shop.reload
21
+ assert @shop.awaiting_install?
22
+ end
23
+
24
+ test 'logged-in and installed user is redirected to installing url for install/uninstalling actions' do
25
+ @shop.installed!
26
+ [:install, :uninstalling].each do |action|
27
+ get(:install)
28
+ assert_redirected_to :installing
29
+ end
30
+ end
31
+
32
+ test 'logged-in and installed user is redirected to root url for installing' do
33
+ @shop.installed!
34
+ get(:installing)
35
+ assert_redirected_to Rails.application.routes.url_helpers.root_path
36
+ end
37
+
38
+ test 'logged-in and uninstalling user sees uninstalling page' do
39
+ @shop.uninstalling!
40
+ get(:uninstalling)
41
+ assert_response :success
42
+ end
43
+
44
+ test 'logged-in and uninstalled user starts install process again' do
45
+ @shop.uninstalled!
46
+ get(:uninstalling)
47
+ assert_redirected_to :install
48
+ end
49
+
50
+ end