boutique 0.0.11 → 0.1.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.
@@ -0,0 +1,123 @@
1
+ .boutique,
2
+ .boutique .start,
3
+ .boutique .subscribed,
4
+ .boutique .confirmed,
5
+ .boutique .unsubscribed {
6
+ display: none;
7
+ }
8
+ .boutique.start .start,
9
+ .boutique.subscribed .subscribed,
10
+ .boutique.confirmed .confirmed,
11
+ .boutique.unsubscribed .unsubscribed {
12
+ display: block;
13
+ }
14
+ .boutique {
15
+ position: fixed;
16
+ width: 100%;
17
+ height: 100%;
18
+ left: 0;
19
+ top: 0;
20
+ background: rgba(0,0,0,0.5);
21
+ text-align: center;
22
+ overflow-y: auto;
23
+ }
24
+ .boutique-modal {
25
+ background: #f9f9f9;
26
+ font-size: 16px;
27
+ max-width: 25em;
28
+ position: relative;
29
+ border-radius: 2px;
30
+ margin: 5em auto;
31
+ text-align: left;
32
+ box-shadow: #333 0 0 2px;
33
+ }
34
+ .boutique-close {
35
+ font-family: helvetica, sans-serif;
36
+ position: absolute;
37
+ top: -0.7em;
38
+ left: -0.7em;
39
+ color: #fff;
40
+ background: #000;
41
+ border-radius: 1.4em;
42
+ border: 2px solid #fff;
43
+ line-height: 1.3em;
44
+ cursor: pointer;
45
+ height: 1.4em;
46
+ width: 1.4em;
47
+ text-align: center;
48
+ }
49
+ .boutique-head {
50
+ background: #111;
51
+ border-top-left-radius: 2px;
52
+ border-top-right-radius: 2px;
53
+ text-align: center;
54
+ color: #fff;
55
+ text-shadow: #111 1px 1px 1px;
56
+ padding: 1em 0.5em 1.1em;
57
+ font-size: 1.5em;
58
+ line-height: 1.3em;
59
+ }
60
+ .boutique-body {
61
+ color: #333;
62
+ padding: 2em;
63
+ line-height: 1.6em;
64
+ }
65
+ .boutique-body p {
66
+ line-height: 1.6em;
67
+ margin: 1.5em 0;
68
+ }
69
+ .boutique-body p:first-child {
70
+ margin-top: 0;
71
+ }
72
+ .boutique-body p:last-child {
73
+ margin-bottom: 0;
74
+ }
75
+ .boutique-body form {
76
+ margin-bottom: 0.5em;
77
+ }
78
+ .boutique-body form label {
79
+ font-weight: bold;
80
+ }
81
+ .boutique-body form input[type=text], .boutique-body form input[type=email] {
82
+ border: 1px solid #ccc;
83
+ border-radius: 2px;
84
+ padding: 0.5em 2%;
85
+ font-size: 1em;
86
+ width: 95%;
87
+ }
88
+ .boutique-body form input[type=submit],
89
+ .boutique-body a.boutique-button {
90
+ background: #ed3b19;
91
+ border: 1px solid #ed3b19;
92
+ border-radius: 4px;
93
+ display: inline-block;
94
+ color: #fff;
95
+ font-size: 1em;
96
+ font-weight: bold;
97
+ cursor: pointer;
98
+ padding: 0.5em 0;
99
+ text-align: center;
100
+ text-decoration: none;
101
+ width: 100%;
102
+ }
103
+ .boutique-body form input[type=submit].inlined,
104
+ .boutique-body a.boutique-button.inlined {
105
+ margin-top: 1em;
106
+ }
107
+ .boutique-body form input[type=submit]:hover,
108
+ .boutique-body a.boutique-button:hover {
109
+ background: #fe5b3b;
110
+ }
111
+ .boutique-body form input[type=submit][disabled],
112
+ .boutique-body form input[type=submit][disabled]:hover,
113
+ .boutique-body a.boutique-button[disabled],
114
+ .boutique-body a.boutique-button[disabled]:hover {
115
+ border: 1px solid #777;
116
+ background: #777;
117
+ cursor: default;
118
+ }
119
+ @media screen and (max-width: 420px) {
120
+ .boutique-modal {
121
+ margin: 1em;
122
+ }
123
+ }
@@ -1,39 +1,60 @@
1
- require File.expand_path('helper', File.dirname(__FILE__))
1
+ require_relative 'helper'
2
2
 
3
3
  class AppTest < BoutiqueTest
4
4
  include Rack::Test::Methods
5
5
 
6
6
  def test_subscribe
7
7
  list = new_list
8
- post "/subscribe/#{list.key}", email: 'john@mailinator.com'
8
+ get "/subscribe/#{list.key}", email: 'john@mailinator.com', jsonp: 'callback'
9
9
  assert(last_response.ok?)
10
+ assert_equal('"callback()"', last_response.body.inspect)
10
11
  assert_equal(1, Boutique::Subscriber.count)
11
12
  assert_equal(1, Boutique::Email.count)
12
13
  refute_nil(Pony.last_mail)
13
14
  subscriber = Boutique::Subscriber.first
14
- refute(subscriber.confirmed?)
15
+ refute(subscriber.confirmed)
16
+ end
17
+
18
+ def test_subscribe_invalid
19
+ list = new_list
20
+ get "/subscribe/#{list.key}", email: 'invalid-email'
21
+ assert_equal(400, last_response.status)
22
+ end
23
+
24
+ def test_subscribe_error
25
+ list = new_list
26
+ find_or_create = lambda { |*args| raise('Stubbed Error') }
27
+ Boutique::Subscriber.stub(:find_or_create, find_or_create) do
28
+ assert_raises(RuntimeError) do
29
+ get "/subscribe/#{list.key}", email: 'john@mailinator.com'
30
+ end
31
+ end
32
+ mail = Pony.last_mail
33
+ assert_equal('dev@localhost', mail[:to])
34
+ assert_equal('[Boutique Error] Stubbed Error', mail[:subject])
35
+ assert(mail[:body])
15
36
  end
16
37
 
17
38
  def test_confirm
18
39
  list = new_list
19
- subscriber = Boutique::Subscriber.create(
20
- list_key: list.key, email: 'john@mailinator.com')
21
- refute(subscriber.confirmed?)
22
- post "/confirm/#{list.key}/#{subscriber.id}/#{subscriber.secret}"
40
+ subscriber = Boutique::Subscriber.create(list_key: list.key, email: 'john@mailinator.com')
41
+ refute(subscriber.confirmed)
42
+ get "/confirm/#{list.key}/#{subscriber.id}/#{subscriber.secret}"
23
43
  assert(last_response.ok?)
24
- subscriber = Boutique::Subscriber.get(subscriber.id)
25
- assert(subscriber.confirmed?)
44
+ subscriber = Boutique::Subscriber[subscriber.id]
45
+ assert(subscriber.confirmed)
26
46
  end
27
47
 
28
48
  def test_unsubscribe
29
49
  list = new_list
30
- subscriber = Boutique::Subscriber.create(
31
- list_key: list.key, email: 'john@mailinator.com', confirmed: true)
32
- assert(subscriber.confirmed?)
33
- post "/unsubscribe/#{list.key}/#{subscriber.id}/#{subscriber.secret}"
50
+ subscriber = Boutique::Subscriber.new(list_key: list.key, email: 'john@mailinator.com')
51
+ subscriber.confirmed = true
52
+ subscriber.save
53
+ assert(subscriber.confirmed)
54
+ get "/unsubscribe/#{list.key}/#{subscriber.id}/#{subscriber.secret}"
34
55
  assert(last_response.ok?)
35
- subscriber = Boutique::Subscriber.get(subscriber.id)
36
- refute(subscriber.confirmed?)
56
+ subscriber = Boutique::Subscriber[subscriber.id]
57
+ refute(subscriber.confirmed)
37
58
  end
38
59
 
39
60
  private
@@ -1,8 +1,8 @@
1
- require File.expand_path('helper', File.dirname(__FILE__))
1
+ require_relative 'helper'
2
2
 
3
3
  class ConfigTest < BoutiqueTest
4
4
  def test_db
5
- assert_equal('dev@localhost', Boutique.config.dev_email)
5
+ assert_equal('dev@localhost', Boutique.config.error_email)
6
6
  assert_equal(
7
7
  'sk_test_abcdefghijklmnopqrstuvwxyz',
8
8
  Boutique.config.stripe_api_key)
@@ -10,13 +10,7 @@ class ConfigTest < BoutiqueTest
10
10
  assert_equal(
11
11
  File.expand_path('../temp', File.dirname(__FILE__)),
12
12
  Boutique.config.download_dir)
13
- assert_equal({
14
- adapter: 'sqlite3',
15
- host: 'localhost',
16
- username: 'root',
17
- password: 'secret',
18
- database: 'db.sqlite3'},
19
- Boutique.config.db_options)
13
+ assert_equal('sqlite::memory:', Boutique.config.db_options)
20
14
  assert_equal({via: :sendmail}, Boutique.config.email_options)
21
15
  end
22
16
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path('helper', File.dirname(__FILE__))
1
+ require_relative 'helper'
2
2
  require 'redcarpet'
3
3
 
4
4
  class EmailerTest < BoutiqueTest
@@ -45,7 +45,7 @@ class EmailerTest < BoutiqueTest
45
45
  subscriber.save
46
46
  assert_equal(0, subscriber.drip_day)
47
47
  @emailer.drip
48
- subscriber = Boutique::Subscriber.get(subscriber.id)
48
+ subscriber = Boutique::Subscriber[subscriber.id]
49
49
  assert_equal(1, subscriber.drip_day)
50
50
  refute_nil(Pony.last_mail)
51
51
  assert_equal(1, Boutique::Email.count)
@@ -53,7 +53,7 @@ class EmailerTest < BoutiqueTest
53
53
 
54
54
  Pony.mail(nil)
55
55
  @emailer.drip
56
- subscriber = Boutique::Subscriber.get(subscriber.id)
56
+ subscriber = Boutique::Subscriber[subscriber.id]
57
57
  assert_equal(1, subscriber.drip_day)
58
58
  assert_nil(Pony.last_mail)
59
59
  assert_equal(1, Boutique::Email.count)
@@ -61,7 +61,9 @@ class EmailerTest < BoutiqueTest
61
61
 
62
62
  private
63
63
  def create_subscriber
64
- Boutique::Subscriber.create(
65
- list_key: @list.key, email: 'john@mailinator.com', confirmed: true)
64
+ subscriber = Boutique::Subscriber.new(list_key: @list.key, email: 'john@mailinator.com')
65
+ subscriber.confirmed = true
66
+ subscriber.save
67
+ subscriber
66
68
  end
67
69
  end
@@ -1,13 +1,22 @@
1
- require File.expand_path('../lib/boutique', File.dirname(__FILE__))
2
- require 'dm-migrations'
1
+ ENV['RACK_ENV'] = 'test'
2
+
3
+ require_relative '../lib/boutique'
3
4
  require 'minitest/autorun'
4
5
  require 'rack'
5
6
  require 'rack/test'
6
7
  require 'rack/server'
7
8
  require 'fileutils'
9
+ require 'sequel'
8
10
 
9
- DataMapper.setup(:default, 'sqlite::memory:')
10
- DataMapper.auto_migrate!
11
+ Boutique.configure do |c|
12
+ c.error_email 'dev@localhost'
13
+ c.stripe_api_key 'sk_test_abcdefghijklmnopqrstuvwxyz'
14
+ c.download_path '/download'
15
+ c.download_dir File.expand_path('../temp', File.dirname(__FILE__))
16
+ c.db_options 'sqlite::memory:'
17
+ c.email_options via: :sendmail
18
+ end
19
+ Boutique::Migrate.run
11
20
 
12
21
  module Pony
13
22
  def self.mail(fields)
@@ -19,25 +28,13 @@ module Pony
19
28
  end
20
29
  end
21
30
 
22
- class BoutiqueTest < MiniTest::Test
31
+ class BoutiqueTest < Minitest::Test
23
32
  def setup
24
- Boutique::Purchase.all.destroy
25
- Boutique::Subscriber.all.destroy
26
- Boutique::Email.all.destroy
33
+ # Boutique::Purchase.all.delete
34
+ Boutique::Email.select_all.delete
35
+ Boutique::Subscriber.select_all.delete
27
36
  Boutique::List.reset_db
28
37
  Boutique::Product.reset_db
29
- Boutique.configure(false) do |c|
30
- c.dev_email 'dev@localhost'
31
- c.stripe_api_key 'sk_test_abcdefghijklmnopqrstuvwxyz'
32
- c.download_path '/download'
33
- c.download_dir File.expand_path('../temp', File.dirname(__FILE__))
34
- c.db_options(adapter: 'sqlite3',
35
- host: 'localhost',
36
- username: 'root',
37
- password: 'secret',
38
- database: 'db.sqlite3')
39
- c.email_options(via: :sendmail)
40
- end
41
38
  Pony.mail(nil)
42
39
  end
43
40
 
@@ -55,13 +52,13 @@ class BoutiqueTest < MiniTest::Test
55
52
  Boutique::List['learn-icon']
56
53
  end
57
54
 
58
- def ebook_product
59
- Boutique::Product.new(
60
- :code => 'ebook',
61
- :name => 'Ebook',
62
- :files => [File.expand_path('../README.md', File.dirname(__FILE__))],
63
- :price => 10.5,
64
- :return_url => 'http://zincmade.com',
65
- :support_email => 'support@zincmade.com')
66
- end
55
+ #def ebook_product
56
+ # Boutique::Product.new(
57
+ # :code => 'ebook',
58
+ # :name => 'Ebook',
59
+ # :files => [File.expand_path('../README.md', File.dirname(__FILE__))],
60
+ # :price => 10.5,
61
+ # :return_url => 'http://zincmade.com',
62
+ # :support_email => 'support@zincmade.com')
63
+ #end
67
64
  end
@@ -1,6 +1,11 @@
1
- require File.expand_path('helper', File.dirname(__FILE__))
1
+ require_relative 'helper'
2
2
 
3
3
  class ModelTest < BoutiqueTest
4
+ def test_migrate
5
+ versions = Boutique.database.select(:version).from(:schema_info).all
6
+ assert_equal([{version: 1}], versions)
7
+ end
8
+
4
9
  def test_product
5
10
  Boutique.product('icon-set') do |p|
6
11
  p.from 'support@zincmade.com'
@@ -32,7 +37,7 @@ class ModelTest < BoutiqueTest
32
37
  list_key: 'learn-icon',
33
38
  email: 'john@mailinator.com')
34
39
  subscriber.save
35
- refute(subscriber.confirmed?)
40
+ refute(subscriber.confirmed)
36
41
  refute_nil(subscriber.secret)
37
42
  assert_equal(1, list.subscribers.count)
38
43
  assert_equal(subscriber, list.subscribers.first)
@@ -49,7 +54,7 @@ class ModelTest < BoutiqueTest
49
54
  end
50
55
 
51
56
  def test_subscriber_email_validation
52
- list = new_list
57
+ list = new_list
53
58
  subscriber = Boutique::Subscriber.new(
54
59
  list_key: 'learn-icon',
55
60
  email: 'invalid-email')
metadata CHANGED
@@ -1,176 +1,155 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boutique
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Hugh Bien
9
8
  autorequire:
10
- bindir: .
9
+ bindir: bin
11
10
  cert_chain: []
12
- date: 2013-09-11 00:00:00.000000000 Z
11
+ date: 2014-08-04 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: sinatra
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
- version: '0'
19
+ version: '1.4'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: '0'
26
+ version: '1.4'
30
27
  - !ruby/object:Gem::Dependency
31
- name: data_mapper
28
+ name: sequel
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - "~>"
36
32
  - !ruby/object:Gem::Version
37
- version: '0'
33
+ version: '4.12'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - "~>"
44
39
  - !ruby/object:Gem::Version
45
- version: '0'
40
+ version: '4.12'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: pony
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - "~>"
52
46
  - !ruby/object:Gem::Version
53
- version: '0'
47
+ version: '1.10'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - "~>"
60
53
  - !ruby/object:Gem::Version
61
- version: '0'
54
+ version: '1.10'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: tilt
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - "~>"
68
60
  - !ruby/object:Gem::Version
69
- version: '0'
61
+ version: '1.4'
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - "~>"
76
67
  - !ruby/object:Gem::Version
77
- version: '0'
68
+ version: '1.4'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: preamble
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - "~>"
84
74
  - !ruby/object:Gem::Version
85
- version: '0'
75
+ version: '0.0'
86
76
  type: :runtime
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - "~>"
92
81
  - !ruby/object:Gem::Version
93
- version: '0'
82
+ version: '0.0'
94
83
  - !ruby/object:Gem::Dependency
95
- name: minitest
84
+ name: shotgun
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - "~>"
100
88
  - !ruby/object:Gem::Version
101
- version: '0'
89
+ version: '0.0'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - "~>"
108
95
  - !ruby/object:Gem::Version
109
- version: '0'
96
+ version: '0.0'
110
97
  - !ruby/object:Gem::Dependency
111
- name: shotgun
98
+ name: sqlite3
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - "~>"
116
102
  - !ruby/object:Gem::Version
117
- version: '0'
103
+ version: '1.3'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - "~>"
124
109
  - !ruby/object:Gem::Version
125
- version: '0'
110
+ version: '1.3'
126
111
  - !ruby/object:Gem::Dependency
127
112
  name: rack-test
128
113
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
114
  requirements:
131
- - - ! '>='
115
+ - - "~>"
132
116
  - !ruby/object:Gem::Version
133
- version: '0'
117
+ version: '0.6'
134
118
  type: :development
135
119
  prerelease: false
136
120
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
121
  requirements:
139
- - - ! '>='
122
+ - - "~>"
140
123
  - !ruby/object:Gem::Version
141
- version: '0'
124
+ version: '0.6'
142
125
  - !ruby/object:Gem::Dependency
143
- name: dm-sqlite-adapter
126
+ name: redcarpet
144
127
  requirement: !ruby/object:Gem::Requirement
145
- none: false
146
128
  requirements:
147
- - - ! '>='
129
+ - - "~>"
148
130
  - !ruby/object:Gem::Version
149
- version: '0'
131
+ version: '3.1'
150
132
  type: :development
151
133
  prerelease: false
152
134
  version_requirements: !ruby/object:Gem::Requirement
153
- none: false
154
135
  requirements:
155
- - - ! '>='
136
+ - - "~>"
156
137
  - !ruby/object:Gem::Version
157
- version: '0'
138
+ version: '3.1'
158
139
  - !ruby/object:Gem::Dependency
159
- name: redcarpet
140
+ name: minitest
160
141
  requirement: !ruby/object:Gem::Requirement
161
- none: false
162
142
  requirements:
163
- - - ! '>='
143
+ - - "~>"
164
144
  - !ruby/object:Gem::Version
165
- version: '0'
145
+ version: '5.4'
166
146
  type: :development
167
147
  prerelease: false
168
148
  version_requirements: !ruby/object:Gem::Requirement
169
- none: false
170
149
  requirements:
171
- - - ! '>='
150
+ - - "~>"
172
151
  - !ruby/object:Gem::Version
173
- version: '0'
152
+ version: '5.4'
174
153
  description: A Sinatra app that adds product checkouts and drip emails support to
175
154
  any websites (both UI + backend).
176
155
  email:
@@ -182,40 +161,44 @@ extra_rdoc_files: []
182
161
  files:
183
162
  - LICENSE.md
184
163
  - README.md
164
+ - bin/boutique
185
165
  - config.ru
186
- - boutique
187
166
  - lib/boutique.rb
167
+ - lib/boutique/app.rb
168
+ - lib/boutique/config.rb
169
+ - lib/boutique/emailer.rb
170
+ - lib/boutique/model.rb
171
+ - lib/boutique/version.rb
172
+ - migrate/001_init_schema.rb
173
+ - public/boutique/script.js
174
+ - public/boutique/styles.css
175
+ - test/app_test.rb
176
+ - test/config_test.rb
177
+ - test/emailer_test.rb
188
178
  - test/helper.rb
189
179
  - test/model_test.rb
190
- - test/emailer_test.rb
191
- - test/config_test.rb
192
- - test/app_test.rb
193
- - ./boutique
194
180
  homepage: https://github.com/hughbien/boutique
195
- licenses: []
181
+ licenses:
182
+ - BSD
183
+ metadata: {}
196
184
  post_install_message:
197
185
  rdoc_options: []
198
186
  require_paths:
199
187
  - lib
200
188
  required_ruby_version: !ruby/object:Gem::Requirement
201
- none: false
202
189
  requirements:
203
- - - ! '>='
190
+ - - ">="
204
191
  - !ruby/object:Gem::Version
205
192
  version: '0'
206
- segments:
207
- - 0
208
- hash: -1016337613
209
193
  required_rubygems_version: !ruby/object:Gem::Requirement
210
- none: false
211
194
  requirements:
212
- - - ! '>='
195
+ - - ">="
213
196
  - !ruby/object:Gem::Version
214
- version: 1.3.6
197
+ version: '0'
215
198
  requirements: []
216
199
  rubyforge_project:
217
- rubygems_version: 1.8.23
200
+ rubygems_version: 2.2.0
218
201
  signing_key:
219
- specification_version: 3
220
- summary: Sinatra app product checkouts and drip emails
202
+ specification_version: 4
203
+ summary: Sinatra app for product checkouts and drip emails
221
204
  test_files: []