disco_app 0.5.6 → 0.6.0

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