stockr 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -6
- data/Gemfile.lock +6 -1
- data/README.rdoc +67 -0
- data/VERSION +1 -1
- data/lib/stockr/assets/bundle.js +1554 -0
- data/lib/stockr/part.rb +19 -4
- data/lib/stockr/store.rb +1 -1
- data/lib/stockr/web.rb +62 -119
- metadata +27 -14
- data/lib/stockr/assets/knockout.js +0 -63
data/lib/stockr/part.rb
CHANGED
@@ -12,6 +12,7 @@ module Stockr
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def save
|
15
|
+
return false unless name && !name.empty?
|
15
16
|
Store.write(name, { :qty => qty, :price => price })
|
16
17
|
end
|
17
18
|
|
@@ -30,21 +31,34 @@ module Stockr
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def price=(pr)
|
33
|
-
|
34
|
+
# BigDecimal.new()
|
35
|
+
@price = pr.kind_of?(Numeric) ? pr.to_f : pr.gsub(",", ".").to_f
|
34
36
|
end
|
35
37
|
|
36
|
-
def
|
38
|
+
def price_total
|
39
|
+
price * qty
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_json
|
43
|
+
"{name: '#{name}', qty: '#{qty}', price: '%.3f', total_price: '%.3f'}" % [price, price_total]
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.find_or_create(q, name, pr=0, incr = false)
|
37
47
|
part = search(name, true) || new(name)
|
38
|
-
part.qty += q.to_i
|
48
|
+
incr ? part.qty += q.to_i : part.qty = q.to_i
|
39
49
|
part.price = pr
|
40
50
|
part.save
|
41
51
|
part
|
42
52
|
end
|
43
53
|
|
54
|
+
def self.create_or_increment(q, name, pr=0)
|
55
|
+
find_or_create(q, name, pr, true)
|
56
|
+
end
|
57
|
+
|
44
58
|
def self.search(txt, exact = false)
|
45
59
|
if res = Store.find(exact ? txt : "*#{txt}*")
|
46
60
|
objs = res.map do |k, r|
|
47
|
-
new(k, r["qty"])
|
61
|
+
new(k, r["qty"], r["price"])
|
48
62
|
end
|
49
63
|
exact ? objs[0] : objs # FIXME: better way?
|
50
64
|
else
|
@@ -62,6 +76,7 @@ module Stockr
|
|
62
76
|
all.select { |p| p.qty <= 0 }
|
63
77
|
end
|
64
78
|
|
79
|
+
|
65
80
|
end
|
66
81
|
|
67
82
|
|
data/lib/stockr/store.rb
CHANGED
data/lib/stockr/web.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
|
1
2
|
require "sinatra"
|
3
|
+
unless Module.const_defined?("Stockr")
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__) + "/../"))
|
5
|
+
require "stockr"
|
6
|
+
end
|
2
7
|
include Stockr
|
3
|
-
# unless const_defined?("Stockr")
|
4
8
|
|
5
|
-
# require "stockr"
|
6
|
-
|
7
|
-
get '/hi' do
|
8
|
-
"Hello World!"
|
9
|
-
end
|
10
9
|
|
11
10
|
get '/' do
|
12
|
-
|
11
|
+
parts = Part.all
|
12
|
+
@parts = parts.map(&:to_json)
|
13
|
+
@sum = parts.reduce(0) { |n, p| n += p.price_total }
|
14
|
+
p @parts
|
13
15
|
erb <<INDEX
|
14
16
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
15
17
|
<html class="cufon-active cufon-ready"><head>
|
@@ -17,13 +19,10 @@ get '/' do
|
|
17
19
|
|
18
20
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
19
21
|
<title>Stockr : My Stock</title>
|
20
|
-
|
21
|
-
<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="../css/ie6ie7.css" /><![endif]-->
|
22
|
-
<!--[if lte IE 6]><link rel="stylesheet" type="text/css" href="../css/ie6.css" /><![endif]-->
|
23
|
-
<script type="text/javascript" src="knockout.js"></script>
|
22
|
+
<script type="text/javascript" src="bundle.js"></script>
|
24
23
|
|
25
|
-
|
26
|
-
/*
|
24
|
+
<style type="text/css">
|
25
|
+
/* CSS ************************************************************************/
|
27
26
|
.content { font-size: 1.1em !important; }
|
28
27
|
.content p + ul { margin-top: -0.8em; }
|
29
28
|
.sticker .content h1 { font-size: 1.6em; margin: 0.4em 0 0.4em 0; }
|
@@ -34,23 +33,6 @@ get '/' do
|
|
34
33
|
|
35
34
|
/* Basics */
|
36
35
|
a { text-decoration: none; }
|
37
|
-
code { font-family: Consolas, Monaco, "Courier New", mono-space, monospace !important; }
|
38
|
-
.content .syntaxhighlighter table, .content .syntaxhighlighter td { border-width: 0; }
|
39
|
-
.content .syntaxhighlighter table { margin-bottom: 0; }
|
40
|
-
.syntaxhighlighter {
|
41
|
-
width: 680px; overflow-x: auto; margin-bottom: 1.8em;
|
42
|
-
-moz-border-radius: 0.5em; -webkit-border-radius: 0.5em; border-radius: 0.5em;
|
43
|
-
}
|
44
|
-
.syntaxhighlighter .line {
|
45
|
-
white-space: pre !important;
|
46
|
-
line-height: 1.2em;
|
47
|
-
}
|
48
|
-
.syntaxhighlighter code {
|
49
|
-
white-space: pre !important;
|
50
|
-
}
|
51
|
-
.syntaxhighlighter.ie {
|
52
|
-
overflow-y: hidden;
|
53
|
-
}
|
54
36
|
|
55
37
|
.center { text-align: center }
|
56
38
|
.left { text-align: left }
|
@@ -70,19 +52,11 @@ code { font-family: Consolas, Monaco, "Courier New", mono-space, monospace !impo
|
|
70
52
|
.inline { display: inline; }
|
71
53
|
.cursor { cursor: pointer }
|
72
54
|
|
73
|
-
|
74
|
-
background-image: url(../img/checkIcon.gif); background-repeat: no-repeat; background-position: 0px 6px;
|
75
|
-
margin-left: -0.5em; padding-left: 2em; line-height: 2em; margin-bottom: 0.3em;
|
76
|
-
}
|
77
|
-
|
78
|
-
li p.smallprint { margin-top: -0.25em; line-height: 1.4em; font-size: 0.9em; margin-bottom: 0.9em; font-style: italic; color: #444; }
|
79
|
-
|
80
|
-
ul.stickerList li { margin: 0.5em 0 0.9em 0; }
|
81
|
-
ul.stickerList li p.smallprint { margin-top: 0.1em; }
|
55
|
+
.small { margin-top: -0.25em; line-height: 1.4em; font-size: 0.8em; margin-bottom: 0.9em; font-style: italic; color: #444; }
|
82
56
|
|
83
57
|
/* Page */
|
84
58
|
#wrapper {margin: 0 auto; width: 982px; }
|
85
|
-
html { background: #d05400
|
59
|
+
html { background: #d05400 no-repeat top center; }
|
86
60
|
body { font-family: arial; font-size: 14px; }
|
87
61
|
.sticker {
|
88
62
|
background-color: white; padding: 1.25em 1.45em 1.25em 1.55em; margin: 0.5em 0 3em 0; -moz-border-radius: 1em; -webkit-border-radius: 1em; border-radius: 1em;
|
@@ -96,14 +70,10 @@ body { font-family: arial; font-size: 14px; }
|
|
96
70
|
border-top-right-radius: 1em; border-top-left-radius: 1em;
|
97
71
|
}
|
98
72
|
|
99
|
-
.engraved {
|
100
|
-
color: #350808;
|
101
|
-
text-shadow: rgba(253, 204, 197, 0.4) 1px 1px 0px;
|
102
|
-
}
|
103
|
-
|
73
|
+
.engraved { color: #350808; text-shadow: rgba(253, 204, 197, 0.4) 1px 1px 0px;}
|
104
74
|
.vspace { height: 2em;}
|
105
75
|
|
106
|
-
.liveExample { padding: 1em; background-color: #EEEEDD; border: 1px solid #CCC; max-width:
|
76
|
+
.liveExample { padding: 1em; background-color: #EEEEDD; border: 1px solid #CCC; max-width: 98%; }
|
107
77
|
.liveExample input { font-family: Arial; }
|
108
78
|
.liveExample b { font-weight: bold; }
|
109
79
|
.liveExample p { margin-top: 0.9em; margin-bottom: 0.9em; }
|
@@ -123,47 +93,20 @@ body { font-family: arial; font-size: 14px; }
|
|
123
93
|
/* Intro */
|
124
94
|
#introBadges li { width: 215px; float:left; text-align: center; padding: 0 0.5em 0 0.5em; color: #555; margin-bottom: 0.5em; }
|
125
95
|
|
126
|
-
/* Homepage */
|
127
|
-
#slogan {
|
128
|
-
float:right; width: 17em;
|
129
|
-
padding-left: 1.4em;
|
130
|
-
background: url(../img/vertical-bar.png) no-repeat;
|
131
|
-
font-size: 20px;
|
132
|
-
margin-bottom: 0.6em;
|
133
|
-
}
|
134
96
|
|
135
|
-
|
136
|
-
|
137
|
-
width: 173px; height: 51px;
|
138
|
-
display: block;
|
139
|
-
margin-top: 0.6em;
|
140
|
-
}
|
141
|
-
.download-button p {
|
142
|
-
color: #005500;
|
143
|
-
font-size: 17px;
|
144
|
-
padding: 0.6em 0 0 9px;
|
145
|
-
}
|
146
|
-
.download-button p.smallprint {
|
147
|
-
font-size: 12px;
|
148
|
-
padding: 0.1em 0 0 11px;
|
149
|
-
}
|
150
|
-
.download-button:hover p { text-decoration: underline; }
|
97
|
+
/* Footer */
|
98
|
+
#page-footer { padding: 15px; text-align: right; }
|
151
99
|
|
100
|
+
.liveExample table, .liveExample td, .liveExample th { padding: 0.2em; border-width: 0; }
|
101
|
+
.liveExample td input { width: 13em; }
|
102
|
+
tr { vertical-align: top; }
|
103
|
+
.liveExample input.error { border: 1px solid red; background-color: #FDC; }
|
104
|
+
.liveExample label.error { display: block; color: Red; font-size: 0.8em; }
|
152
105
|
|
153
|
-
|
154
|
-
.homepageExample .liveExample { padding: 0.5em 1em 0.5em 1.3em; }
|
155
|
-
.homepageExample .liveExample button { padding: 0.2em 0.6em; margin-left: 0.5em; }
|
156
|
-
.homepageExample p { margin: 1.3em 0 0.8em 0; }
|
106
|
+
/* CSS ****************************************************************************/
|
157
107
|
|
158
|
-
|
159
|
-
#page-footer { padding: 15px; text-align: right; }
|
108
|
+
</style>
|
160
109
|
|
161
|
-
.liveExample table, .liveExample td, .liveExample th { padding: 0.2em; border-width: 0; }
|
162
|
-
.liveExample td input { width: 13em; }
|
163
|
-
tr { vertical-align: top; }
|
164
|
-
.liveExample input.error { border: 1px solid red; background-color: #FDC; }
|
165
|
-
.liveExample label.error { display: block; color: Red; font-size: 0.8em; }
|
166
|
-
</style>
|
167
110
|
|
168
111
|
</head><body>
|
169
112
|
<div id="wrapper">
|
@@ -172,14 +115,14 @@ body { font-family: arial; font-size: 14px; }
|
|
172
115
|
<div class="sticker">
|
173
116
|
<div class="content">
|
174
117
|
|
175
|
-
|
176
|
-
|
177
|
-
<form action="/
|
178
|
-
<p>You have asked for <span data-bind="text: parts().length">2</span> part(s)</p>
|
118
|
+
<h2>My Stock - <%= Time.now %> </h2>
|
119
|
+
<div class="liveExample">
|
120
|
+
<form action="/up">
|
179
121
|
<table data-bind="visible: parts().length > 0">
|
180
122
|
<thead>
|
181
123
|
<tr>
|
182
|
-
<th>
|
124
|
+
<th>Qty</th>
|
125
|
+
<th>Name</th>
|
183
126
|
<th>Price</th>
|
184
127
|
<th>
|
185
128
|
</th></tr>
|
@@ -189,27 +132,27 @@ body { font-family: arial; font-size: 14px; }
|
|
189
132
|
|
190
133
|
<button data-bind="click: addPart">Add Part</button>
|
191
134
|
<button data-bind="enable: parts().length > 0" type="submit">Submit</button>
|
135
|
+
<p>We got <span data-bind="text: parts().length">2</span> part(s). $ <%= @sum %> </p>
|
192
136
|
</form>
|
193
137
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
138
|
+
<script type="text/html" id="partRowTemplate">
|
139
|
+
<tr>
|
140
|
+
<td><input class="required number" data-bind="value: qty, uniqueName: true"/></td>
|
141
|
+
<td><input class="required" data-bind="value: name, uniqueName: true"/></td>
|
142
|
+
<td><input class="number" data-bind="value: price, uniqueName: true"/></td>
|
143
|
+
<td><span class="small" data-bind="text: total_price"></span></td>
|
144
|
+
<td><a href="#" data-bind="click: function() { viewModel.removePart($data) }">Delete</a></td>
|
145
|
+
</tr>
|
146
|
+
</script>
|
201
147
|
|
202
148
|
|
203
149
|
<script type="text/javascript">
|
204
150
|
/*<![CDATA[*/
|
205
151
|
var viewModel = {
|
206
|
-
parts: ko.observableArray([
|
207
|
-
<!-- { name: "Tall Hat", price: "39.95" }, -->
|
208
|
-
<!-- { name: "Long Cloak", price: "120.00" } -->
|
209
|
-
]),
|
152
|
+
parts: ko.observableArray([<%= @parts.join(',') %>]),
|
210
153
|
|
211
154
|
addPart: function () {
|
212
|
-
this.parts.push({ name: "", price: "" });
|
155
|
+
this.parts.push({ qty: "", name: "", price: "", total_price: "" });
|
213
156
|
},
|
214
157
|
|
215
158
|
removePart: function (part) {
|
@@ -217,8 +160,8 @@ body { font-family: arial; font-size: 14px; }
|
|
217
160
|
},
|
218
161
|
|
219
162
|
save: function (form) {
|
220
|
-
|
221
|
-
|
163
|
+
//lert("Could now transmit to server: " + ko.utils.stringifyJson(this.parts));
|
164
|
+
ko.utils.postJson($("form")[0], this.parts);
|
222
165
|
}
|
223
166
|
};
|
224
167
|
|
@@ -248,18 +191,7 @@ body { font-family: arial; font-size: 14px; }
|
|
248
191
|
|
249
192
|
|
250
193
|
<br><br><br><br>
|
251
|
-
|
252
|
-
|
253
|
-
%table
|
254
|
-
%tr
|
255
|
-
%th Name
|
256
|
-
%th Qty
|
257
|
-
%th Price
|
258
|
-
- for pr in @parts
|
259
|
-
%tr
|
260
|
-
%td= pr.name
|
261
|
-
%td= pr.qty
|
262
|
-
%td= pr.price
|
194
|
+
|
263
195
|
|
264
196
|
Total:
|
265
197
|
|
@@ -270,13 +202,10 @@ get '/new' do
|
|
270
202
|
haml :new
|
271
203
|
end
|
272
204
|
|
273
|
-
get '/
|
274
|
-
File.open(File.dirname(__FILE__) + "/assets/
|
205
|
+
get '/bundle.js' do
|
206
|
+
File.open(File.dirname(__FILE__) + "/assets/bundle.js")
|
275
207
|
end
|
276
208
|
|
277
|
-
get '/up' do
|
278
|
-
"..."
|
279
|
-
end
|
280
209
|
|
281
210
|
get '/:id' do
|
282
211
|
@part = Part.find(params[:id])
|
@@ -287,6 +216,20 @@ get '/:id' do
|
|
287
216
|
end
|
288
217
|
end
|
289
218
|
|
219
|
+
|
220
|
+
post '/up' do
|
221
|
+
params.each do |id, data|
|
222
|
+
data.gsub!(/\\|\"/, "")
|
223
|
+
# just to not require json and play
|
224
|
+
attrs = [:qty, :name, :price].map { |a| data.match(/\{?\s?(#{a})\s?:\s?(\w*)}?/)[2] }
|
225
|
+
p attrs
|
226
|
+
Part.find_or_create(*attrs)
|
227
|
+
end
|
228
|
+
"..."
|
229
|
+
redirect '/'
|
230
|
+
end
|
231
|
+
|
232
|
+
|
290
233
|
post '/' do
|
291
234
|
@part = Part.new(:distance => params[:distance], :minutes => params[:minutes])
|
292
235
|
if @part.save
|
@@ -296,5 +239,5 @@ post '/' do
|
|
296
239
|
end
|
297
240
|
end
|
298
241
|
|
299
|
-
Sinatra::Application.run!
|
242
|
+
# Sinatra::Application.run!
|
300
243
|
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 0
|
8
7
|
- 1
|
9
|
-
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Marcos Piccinini
|
@@ -31,8 +31,21 @@ dependencies:
|
|
31
31
|
prerelease: false
|
32
32
|
version_requirements: *id001
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: sinatra
|
35
35
|
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
version: "0"
|
43
|
+
type: :development
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
36
49
|
none: false
|
37
50
|
requirements:
|
38
51
|
- - ~>
|
@@ -44,10 +57,10 @@ dependencies:
|
|
44
57
|
version: 2.1.0
|
45
58
|
type: :development
|
46
59
|
prerelease: false
|
47
|
-
version_requirements: *
|
60
|
+
version_requirements: *id003
|
48
61
|
- !ruby/object:Gem::Dependency
|
49
62
|
name: bundler
|
50
|
-
requirement: &
|
63
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
51
64
|
none: false
|
52
65
|
requirements:
|
53
66
|
- - ~>
|
@@ -59,10 +72,10 @@ dependencies:
|
|
59
72
|
version: 1.0.0
|
60
73
|
type: :development
|
61
74
|
prerelease: false
|
62
|
-
version_requirements: *
|
75
|
+
version_requirements: *id004
|
63
76
|
- !ruby/object:Gem::Dependency
|
64
77
|
name: jeweler
|
65
|
-
requirement: &
|
78
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
66
79
|
none: false
|
67
80
|
requirements:
|
68
81
|
- - ~>
|
@@ -74,10 +87,10 @@ dependencies:
|
|
74
87
|
version: 1.5.1
|
75
88
|
type: :development
|
76
89
|
prerelease: false
|
77
|
-
version_requirements: *
|
90
|
+
version_requirements: *id005
|
78
91
|
- !ruby/object:Gem::Dependency
|
79
92
|
name: rcov
|
80
|
-
requirement: &
|
93
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
81
94
|
none: false
|
82
95
|
requirements:
|
83
96
|
- - ">="
|
@@ -87,10 +100,10 @@ dependencies:
|
|
87
100
|
version: "0"
|
88
101
|
type: :development
|
89
102
|
prerelease: false
|
90
|
-
version_requirements: *
|
103
|
+
version_requirements: *id006
|
91
104
|
- !ruby/object:Gem::Dependency
|
92
105
|
name: redis
|
93
|
-
requirement: &
|
106
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
94
107
|
none: false
|
95
108
|
requirements:
|
96
109
|
- - ">="
|
@@ -100,7 +113,7 @@ dependencies:
|
|
100
113
|
version: "0"
|
101
114
|
type: :runtime
|
102
115
|
prerelease: false
|
103
|
-
version_requirements: *
|
116
|
+
version_requirements: *id007
|
104
117
|
description: Help keep track of stuff (good for electronics)
|
105
118
|
email: x@nofxx.com
|
106
119
|
executables:
|
@@ -121,7 +134,7 @@ files:
|
|
121
134
|
- VERSION
|
122
135
|
- bin/stockr
|
123
136
|
- lib/stockr.rb
|
124
|
-
- lib/stockr/assets/
|
137
|
+
- lib/stockr/assets/bundle.js
|
125
138
|
- lib/stockr/export.rb
|
126
139
|
- lib/stockr/part.rb
|
127
140
|
- lib/stockr/store.rb
|
@@ -143,7 +156,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
156
|
requirements:
|
144
157
|
- - ">="
|
145
158
|
- !ruby/object:Gem::Version
|
146
|
-
hash:
|
159
|
+
hash: -709536225112595033
|
147
160
|
segments:
|
148
161
|
- 0
|
149
162
|
version: "0"
|