sinatra-embedded-shopify-app 0.5.16
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.
- checksums.yaml +7 -0
- data/.circleci/config.yml +15 -0
- data/.github/dependabot.yml +14 -0
- data/.gitignore +2 -0
- data/.ruby-version +1 -0
- data/CHANGELOG +96 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +118 -0
- data/LICENSE +20 -0
- data/README.md +172 -0
- data/bin/ci.sh +10 -0
- data/bin/sinatra-embedded-shopify-app-generator +85 -0
- data/example/.gitignore +3 -0
- data/example/.ruby-version +1 -0
- data/example/Gemfile +28 -0
- data/example/Gemfile.lock +126 -0
- data/example/Procfile +1 -0
- data/example/README.md +4 -0
- data/example/Rakefile +19 -0
- data/example/config/database.yml +12 -0
- data/example/config.ru +3 -0
- data/example/db/migrate/20140413221328_create_shops.rb +14 -0
- data/example/db/migrate/20140414042317_add_index_to_shops.rb +11 -0
- data/example/db/schema.rb +20 -0
- data/example/db/seeds.rb +3 -0
- data/example/public/icon.png +0 -0
- data/example/public/legend.gif +0 -0
- data/example/src/app.rb +57 -0
- data/example/test/app_test.rb +81 -0
- data/example/test/test_helper.rb +31 -0
- data/example/views/_flash_messages.erb +25 -0
- data/example/views/_top_bar.erb +10 -0
- data/example/views/home.erb +11 -0
- data/example/views/layouts/application.erb +26 -0
- data/example/views/login.erb +42 -0
- data/lib/sinatra/sinatra-embedded-shopify-app.rb +275 -0
- data/sinatra-embedded-shopify-app.gemspec +32 -0
- metadata +272 -0
@@ -0,0 +1,275 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
require 'sinatra/activerecord'
|
5
|
+
require 'active_support/all'
|
6
|
+
require 'shopify_api'
|
7
|
+
require 'omniauth-shopify-oauth2'
|
8
|
+
|
9
|
+
# Sinatra
|
10
|
+
module Sinatra
|
11
|
+
# Shopify
|
12
|
+
module Shopify
|
13
|
+
# Methods
|
14
|
+
module Methods
|
15
|
+
# designed to be overridden
|
16
|
+
def after_shopify_auth; end
|
17
|
+
|
18
|
+
# for the app bridge initializer
|
19
|
+
def shop_host
|
20
|
+
(session[:shopify][:host]).to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def shopify_session
|
24
|
+
return_to = request.path
|
25
|
+
return_params = request.params
|
26
|
+
|
27
|
+
if no_session?
|
28
|
+
authenticate(return_to, return_params)
|
29
|
+
|
30
|
+
elsif different_shop?
|
31
|
+
clear_session
|
32
|
+
authenticate(return_to, return_params)
|
33
|
+
|
34
|
+
else
|
35
|
+
shop_name = session[:shopify][:shop]
|
36
|
+
token = session[:shopify][:token]
|
37
|
+
activate_shopify_api(shop_name, token)
|
38
|
+
yield shop_name
|
39
|
+
end
|
40
|
+
rescue ActiveResource::UnauthorizedAccess
|
41
|
+
clear_session
|
42
|
+
|
43
|
+
shop = Shop.find_by(shopify_domain: shop_name)
|
44
|
+
shop.shopify_token = nil
|
45
|
+
shop.save
|
46
|
+
|
47
|
+
redirect request.path
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def authenticate(_return_to = '/', return_params = nil)
|
53
|
+
session[:return_params] = return_params if return_params
|
54
|
+
|
55
|
+
if (shop_name = sanitized_shop_param(params))
|
56
|
+
redirect "/login?shop=#{shop_name}"
|
57
|
+
else
|
58
|
+
redirect '/login'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def base_url
|
63
|
+
request_protocol = request.secure? ? 'https' : 'http'
|
64
|
+
"#{request_protocol}://#{request.env['HTTP_HOST']}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def no_session?
|
68
|
+
!session.key?(:shopify)
|
69
|
+
end
|
70
|
+
|
71
|
+
def different_shop?
|
72
|
+
params[:shop].present? && session[:shopify][:shop] != sanitized_shop_param(params)
|
73
|
+
end
|
74
|
+
|
75
|
+
def clear_session
|
76
|
+
session.delete(:shopify)
|
77
|
+
session.clear
|
78
|
+
end
|
79
|
+
|
80
|
+
def activate_shopify_api(shop_name, token)
|
81
|
+
api_session = ShopifyAPI::Session.new(domain: shop_name, token: token, api_version: settings.api_version)
|
82
|
+
ShopifyAPI::Base.activate_session(api_session)
|
83
|
+
end
|
84
|
+
|
85
|
+
def receive_webhook
|
86
|
+
return unless verify_shopify_webhook
|
87
|
+
|
88
|
+
shop_name = request.env['HTTP_X_SHOPIFY_SHOP_DOMAIN']
|
89
|
+
webhook_body = ActiveSupport::JSON.decode(request.body.read.to_s)
|
90
|
+
yield shop_name, webhook_body
|
91
|
+
status 200
|
92
|
+
end
|
93
|
+
|
94
|
+
def sanitized_shop_param(params)
|
95
|
+
return unless params[:shop].present?
|
96
|
+
|
97
|
+
name = params[:shop].to_s.strip
|
98
|
+
name += '.myshopify.com' if !name.include?('myshopify.com') && !name.include?('.')
|
99
|
+
name.gsub!('https://', '')
|
100
|
+
name.gsub!('http://', '')
|
101
|
+
|
102
|
+
u = URI("http://#{name}")
|
103
|
+
u.host.ends_with?('.myshopify.com') ? u.host : nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def verify_shopify_webhook
|
107
|
+
data = request.body.read.to_s
|
108
|
+
digest = OpenSSL::Digest.new('sha256')
|
109
|
+
calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, settings.shared_secret, data)).strip
|
110
|
+
request.body.rewind
|
111
|
+
|
112
|
+
if calculated_hmac == request.env['HTTP_X_SHOPIFY_HMAC_SHA256']
|
113
|
+
true
|
114
|
+
else
|
115
|
+
puts 'Shopify Webhook verification failed!'
|
116
|
+
false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# needs to be dynamic to incude the current shop
|
122
|
+
class ContentSecurityPolicy < Rack::Protection::Base
|
123
|
+
def csp_policy(env)
|
124
|
+
"frame-ancestors #{current_shop(env)} https://admin.shopify.com;"
|
125
|
+
end
|
126
|
+
|
127
|
+
def call(env)
|
128
|
+
status, headers, body = @app.call(env)
|
129
|
+
header = 'Content-Security-Policy'
|
130
|
+
headers[header] ||= csp_policy(env) if html? headers
|
131
|
+
[status, headers, body]
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def current_shop(env)
|
137
|
+
s = session(env)
|
138
|
+
if s.has_key?('return_params')
|
139
|
+
"https://#{s['return_params']['shop']}"
|
140
|
+
elsif s.has_key?(:shopify)
|
141
|
+
"https://#{s[:shopify][:shop]}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def html?(headers)
|
146
|
+
return false unless (header = headers.detect { |k, _v| k.downcase == 'content-type' })
|
147
|
+
|
148
|
+
options[:html_types].include? header.last[%r{^\w+/\w+}]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def shopify_webhook(route, &blk)
|
153
|
+
settings.webhook_routes << route
|
154
|
+
post(route) do
|
155
|
+
receive_webhook(&blk)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.registered(app)
|
160
|
+
app.helpers Shopify::Methods
|
161
|
+
app.register Sinatra::ActiveRecordExtension
|
162
|
+
|
163
|
+
app.set :database_file, File.expand_path('config/database.yml')
|
164
|
+
|
165
|
+
app.set :erb, layout: :'layouts/application'
|
166
|
+
app.set :views, File.expand_path('views')
|
167
|
+
app.set :public_folder, File.expand_path('public')
|
168
|
+
app.enable :inline_templates
|
169
|
+
|
170
|
+
app.set :protection, except: :frame_options
|
171
|
+
|
172
|
+
app.set :api_version, '2019-07'
|
173
|
+
app.set :scope, 'read_products, read_orders'
|
174
|
+
|
175
|
+
app.set :api_key, ENV['SHOPIFY_API_KEY']
|
176
|
+
app.set :shared_secret, ENV['SHOPIFY_SHARED_SECRET']
|
177
|
+
app.set :secret, ENV['SECRET']
|
178
|
+
|
179
|
+
# csrf needs to be disabled for webhook routes
|
180
|
+
app.set :webhook_routes, ['/uninstall']
|
181
|
+
|
182
|
+
# add support for put/patch/delete
|
183
|
+
app.use Rack::MethodOverride
|
184
|
+
|
185
|
+
app.use Rack::Session::Cookie, key: 'rack.session',
|
186
|
+
path: '/',
|
187
|
+
secure: true,
|
188
|
+
same_site: 'None',
|
189
|
+
secret: app.settings.secret,
|
190
|
+
expire_after: 60 * 30 # half an hour in seconds
|
191
|
+
|
192
|
+
app.use Shopify::ContentSecurityPolicy
|
193
|
+
|
194
|
+
app.use Rack::Protection::AuthenticityToken, allow_if: lambda { |env|
|
195
|
+
app.settings.webhook_routes.include?(env['PATH_INFO'])
|
196
|
+
}
|
197
|
+
|
198
|
+
OmniAuth.config.allowed_request_methods = [:post]
|
199
|
+
|
200
|
+
app.use OmniAuth::Builder do
|
201
|
+
provider :shopify,
|
202
|
+
app.settings.api_key,
|
203
|
+
app.settings.shared_secret,
|
204
|
+
scope: app.settings.scope,
|
205
|
+
setup: lambda { |env|
|
206
|
+
shop = if env['REQUEST_METHOD'] == 'POST'
|
207
|
+
env['rack.request.form_hash']['shop']
|
208
|
+
else
|
209
|
+
Rack::Utils.parse_query(env['QUERY_STRING'])['shop']
|
210
|
+
end
|
211
|
+
|
212
|
+
site_url = "https://#{shop}"
|
213
|
+
env['omniauth.strategy'].options[:client_options][:site] = site_url
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
ShopifyAPI::Session.setup(
|
218
|
+
api_key: app.settings.api_key,
|
219
|
+
secret: app.settings.shared_secret
|
220
|
+
)
|
221
|
+
|
222
|
+
app.get '/login' do
|
223
|
+
erb :login, layout: false
|
224
|
+
end
|
225
|
+
|
226
|
+
app.get '/logout' do
|
227
|
+
clear_session
|
228
|
+
redirect '/login'
|
229
|
+
end
|
230
|
+
|
231
|
+
app.get '/auth/shopify/callback' do
|
232
|
+
shop_name = params['shop']
|
233
|
+
token = request.env['omniauth.auth']['credentials']['token']
|
234
|
+
host = params['host']
|
235
|
+
|
236
|
+
shop = Shop.find_or_initialize_by(shopify_domain: shop_name)
|
237
|
+
shop.shopify_token = token
|
238
|
+
shop.save!
|
239
|
+
|
240
|
+
session[:shopify] = {
|
241
|
+
shop: shop_name,
|
242
|
+
host: host,
|
243
|
+
token: token
|
244
|
+
}
|
245
|
+
|
246
|
+
after_shopify_auth
|
247
|
+
|
248
|
+
return_params = session[:return_params]
|
249
|
+
session.delete(:return_params)
|
250
|
+
|
251
|
+
return_to = '/'
|
252
|
+
return_to += "?#{return_params.to_query}" if return_params.present?
|
253
|
+
|
254
|
+
redirect return_to
|
255
|
+
end
|
256
|
+
|
257
|
+
app.get '/auth/failure' do
|
258
|
+
erb "<h1>Authentication Failed:</h1>
|
259
|
+
<h3>message:<h3> <pre>#{params}</pre>", layout: false
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
register Shopify
|
265
|
+
end
|
266
|
+
|
267
|
+
# Shop
|
268
|
+
class Shop < ActiveRecord::Base
|
269
|
+
def self.secret
|
270
|
+
@secret ||= ENV['SECRET']
|
271
|
+
end
|
272
|
+
|
273
|
+
validates_presence_of :shopify_domain
|
274
|
+
validates_presence_of :shopify_token, on: :create
|
275
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'sinatra-embedded-shopify-app'
|
5
|
+
s.version = '0.5.16'
|
6
|
+
|
7
|
+
s.summary = 'A classy shopify embeded app'
|
8
|
+
s.description = 'A Sinatra extension for building Shopify Embedded Apps. Akin to the shopify_app gem but for Sinatra'
|
9
|
+
|
10
|
+
s.authors = ['Whisker Side']
|
11
|
+
s.email = 'whiskerside@gmail.com'
|
12
|
+
s.homepage = 'https://github.com/whiskerside/sinatra-embedded-shopify-app/'
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.executables << 'sinatra-embedded-shopify-app-generator'
|
17
|
+
|
18
|
+
s.add_runtime_dependency 'activesupport'
|
19
|
+
s.add_runtime_dependency 'sinatra', '>= 2.2', '< 3.1'
|
20
|
+
s.add_runtime_dependency 'sinatra-activerecord', '~> 2.0.9'
|
21
|
+
|
22
|
+
s.add_runtime_dependency 'omniauth', '~> 2.0', '>= 2.0.4'
|
23
|
+
s.add_runtime_dependency 'omniauth-shopify-oauth2', '~> 2.3', '>= 2.3.2'
|
24
|
+
s.add_runtime_dependency 'shopify_api', '9.5.1'
|
25
|
+
|
26
|
+
s.add_development_dependency 'fakeweb'
|
27
|
+
s.add_development_dependency 'minitest'
|
28
|
+
s.add_development_dependency 'mocha'
|
29
|
+
s.add_development_dependency 'rack-test', '~> 2.0.2'
|
30
|
+
s.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3'
|
31
|
+
s.add_development_dependency 'sqlite3', '~> 1.6.2'
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sinatra-embedded-shopify-app
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.16
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Whisker Side
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-05-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sinatra
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.2'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '3.1'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.2'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.1'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: sinatra-activerecord
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.0.9
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 2.0.9
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: omniauth
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2.0'
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 2.0.4
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '2.0'
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 2.0.4
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: omniauth-shopify-oauth2
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '2.3'
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 2.3.2
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '2.3'
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 2.3.2
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: shopify_api
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - '='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 9.5.1
|
108
|
+
type: :runtime
|
109
|
+
prerelease: false
|
110
|
+
version_requirements: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - '='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 9.5.1
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: fakeweb
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
type: :development
|
123
|
+
prerelease: false
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
- !ruby/object:Gem::Dependency
|
130
|
+
name: minitest
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
type: :development
|
137
|
+
prerelease: false
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
- !ruby/object:Gem::Dependency
|
144
|
+
name: mocha
|
145
|
+
requirement: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
- !ruby/object:Gem::Dependency
|
158
|
+
name: rack-test
|
159
|
+
requirement: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - "~>"
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: 2.0.2
|
164
|
+
type: :development
|
165
|
+
prerelease: false
|
166
|
+
version_requirements: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - "~>"
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: 2.0.2
|
171
|
+
- !ruby/object:Gem::Dependency
|
172
|
+
name: rake
|
173
|
+
requirement: !ruby/object:Gem::Requirement
|
174
|
+
requirements:
|
175
|
+
- - "~>"
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: '12.3'
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: 12.3.3
|
181
|
+
type: :development
|
182
|
+
prerelease: false
|
183
|
+
version_requirements: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '12.3'
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: 12.3.3
|
191
|
+
- !ruby/object:Gem::Dependency
|
192
|
+
name: sqlite3
|
193
|
+
requirement: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - "~>"
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: 1.6.2
|
198
|
+
type: :development
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
requirements:
|
202
|
+
- - "~>"
|
203
|
+
- !ruby/object:Gem::Version
|
204
|
+
version: 1.6.2
|
205
|
+
description: A Sinatra extension for building Shopify Embedded Apps. Akin to the shopify_app
|
206
|
+
gem but for Sinatra
|
207
|
+
email: whiskerside@gmail.com
|
208
|
+
executables:
|
209
|
+
- sinatra-embedded-shopify-app-generator
|
210
|
+
extensions: []
|
211
|
+
extra_rdoc_files: []
|
212
|
+
files:
|
213
|
+
- ".circleci/config.yml"
|
214
|
+
- ".github/dependabot.yml"
|
215
|
+
- ".gitignore"
|
216
|
+
- ".ruby-version"
|
217
|
+
- CHANGELOG
|
218
|
+
- Gemfile
|
219
|
+
- Gemfile.lock
|
220
|
+
- LICENSE
|
221
|
+
- README.md
|
222
|
+
- bin/ci.sh
|
223
|
+
- bin/sinatra-embedded-shopify-app-generator
|
224
|
+
- example/.gitignore
|
225
|
+
- example/.ruby-version
|
226
|
+
- example/Gemfile
|
227
|
+
- example/Gemfile.lock
|
228
|
+
- example/Procfile
|
229
|
+
- example/README.md
|
230
|
+
- example/Rakefile
|
231
|
+
- example/config.ru
|
232
|
+
- example/config/database.yml
|
233
|
+
- example/db/migrate/20140413221328_create_shops.rb
|
234
|
+
- example/db/migrate/20140414042317_add_index_to_shops.rb
|
235
|
+
- example/db/schema.rb
|
236
|
+
- example/db/seeds.rb
|
237
|
+
- example/public/icon.png
|
238
|
+
- example/public/legend.gif
|
239
|
+
- example/src/app.rb
|
240
|
+
- example/test/app_test.rb
|
241
|
+
- example/test/test_helper.rb
|
242
|
+
- example/views/_flash_messages.erb
|
243
|
+
- example/views/_top_bar.erb
|
244
|
+
- example/views/home.erb
|
245
|
+
- example/views/layouts/application.erb
|
246
|
+
- example/views/login.erb
|
247
|
+
- lib/sinatra/sinatra-embedded-shopify-app.rb
|
248
|
+
- sinatra-embedded-shopify-app.gemspec
|
249
|
+
homepage: https://github.com/whiskerside/sinatra-embedded-shopify-app/
|
250
|
+
licenses:
|
251
|
+
- MIT
|
252
|
+
metadata: {}
|
253
|
+
post_install_message:
|
254
|
+
rdoc_options: []
|
255
|
+
require_paths:
|
256
|
+
- lib
|
257
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
258
|
+
requirements:
|
259
|
+
- - ">="
|
260
|
+
- !ruby/object:Gem::Version
|
261
|
+
version: '0'
|
262
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
263
|
+
requirements:
|
264
|
+
- - ">="
|
265
|
+
- !ruby/object:Gem::Version
|
266
|
+
version: '0'
|
267
|
+
requirements: []
|
268
|
+
rubygems_version: 3.1.6
|
269
|
+
signing_key:
|
270
|
+
specification_version: 4
|
271
|
+
summary: A classy shopify embeded app
|
272
|
+
test_files: []
|