stockr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +32 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/bin/stockr +26 -0
- data/lib/stockr.rb +50 -0
- data/lib/stockr/assets/knockout.js +63 -0
- data/lib/stockr/export.rb +72 -0
- data/lib/stockr/part.rb +68 -0
- data/lib/stockr/store.rb +40 -0
- data/lib/stockr/web.rb +300 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/stockr/part_spec.rb +19 -0
- data/spec/stockr_spec.rb +78 -0
- metadata +168 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
gem "redis"
|
6
|
+
|
7
|
+
# Add dependencies to develop your gem here.
|
8
|
+
# Include everything needed to run rake, tests, features, etc.
|
9
|
+
group :development do
|
10
|
+
gem "rspec", "~> 2.1.0"
|
11
|
+
gem "bundler", "~> 1.0.0"
|
12
|
+
gem "jeweler", "~> 1.5.1"
|
13
|
+
gem "rcov", ">= 0"
|
14
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.5.1)
|
7
|
+
bundler (~> 1.0.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
rake (0.8.7)
|
11
|
+
rcov (0.9.9)
|
12
|
+
redis (2.1.1)
|
13
|
+
rspec (2.1.0)
|
14
|
+
rspec-core (~> 2.1.0)
|
15
|
+
rspec-expectations (~> 2.1.0)
|
16
|
+
rspec-mocks (~> 2.1.0)
|
17
|
+
rspec-core (2.1.0)
|
18
|
+
rspec-expectations (2.1.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.1.0)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
bundler (~> 1.0.0)
|
27
|
+
jeweler (~> 1.5.1)
|
28
|
+
rcov
|
29
|
+
redis
|
30
|
+
rspec (~> 2.1.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Marcos Piccinini
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
= stockr
|
2
|
+
|
3
|
+
|
4
|
+
Keep your stock up to date.
|
5
|
+
With easy bash editing, and web view.
|
6
|
+
Exports to a bunch of formats too.
|
7
|
+
|
8
|
+
|
9
|
+
== Use
|
10
|
+
|
11
|
+
|
12
|
+
stockr +5 LM358
|
13
|
+
stockr -2 LM358
|
14
|
+
stockr LM358
|
15
|
+
> 3x LM358
|
16
|
+
|
17
|
+
|
18
|
+
== Contributing to stockr
|
19
|
+
|
20
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
21
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
22
|
+
* Fork the project
|
23
|
+
* Start a feature/bugfix branch
|
24
|
+
* Commit and push until you are happy with your contribution
|
25
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
26
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
27
|
+
|
28
|
+
== Copyright
|
29
|
+
|
30
|
+
Copyright (c) 2010 Marcos Piccinini. See LICENSE.txt for
|
31
|
+
further details.
|
32
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "stockr"
|
16
|
+
gem.homepage = "http://github.com/nofxx/stockr"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Keep track of all your stuff}
|
19
|
+
gem.description = %Q{Help keep track of stuff (good for electronics)}
|
20
|
+
gem.email = "x@nofxx.com"
|
21
|
+
gem.authors = ["Marcos Piccinini"]
|
22
|
+
|
23
|
+
gem.add_runtime_dependency 'redis'
|
24
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
25
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
26
|
+
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
27
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
28
|
+
end
|
29
|
+
Jeweler::RubygemsDotOrgTasks.new
|
30
|
+
|
31
|
+
require 'rspec/core'
|
32
|
+
require 'rspec/core/rake_task'
|
33
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
34
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
35
|
+
end
|
36
|
+
|
37
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
38
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
+
spec.rcov = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => :spec
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "stockr #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/stockr
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'stockr'
|
5
|
+
|
6
|
+
if Time.now.saturday?
|
7
|
+
puts "GEEK!"
|
8
|
+
end
|
9
|
+
|
10
|
+
if ARGV.empty?
|
11
|
+
puts <<EOF
|
12
|
+
|
13
|
+
+[ Stockr ]-
|
14
|
+
|
15
|
+
Use:
|
16
|
+
|
17
|
+
stockr +5 LM358
|
18
|
+
stockr -2 LM358
|
19
|
+
stockr LM358
|
20
|
+
> 3x LM358
|
21
|
+
|
22
|
+
|
23
|
+
EOF
|
24
|
+
else
|
25
|
+
puts Stockr.work(ARGV)
|
26
|
+
end
|
data/lib/stockr.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "redis"
|
3
|
+
|
4
|
+
require "stockr/store"
|
5
|
+
require "stockr/part"
|
6
|
+
require "stockr/export"
|
7
|
+
|
8
|
+
module Stockr
|
9
|
+
|
10
|
+
FORMATS = %w{ txt csv html pdf xml }
|
11
|
+
|
12
|
+
def self.print_parts(res)
|
13
|
+
return "Not found... go shop!" unless res && !res.empty?
|
14
|
+
res.map(&:facts).join("\n")
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def self.work(txt)
|
20
|
+
txt = txt.split(" ") unless txt.is_a? Array
|
21
|
+
parse = txt
|
22
|
+
case parse.join
|
23
|
+
when "all" then puts Export.format
|
24
|
+
when "web" then
|
25
|
+
puts "Starting websever on port."
|
26
|
+
require "stockr/web"
|
27
|
+
when "shop" then print_parts(Part.missing)
|
28
|
+
else
|
29
|
+
if parse.size == 1
|
30
|
+
if parse.join =~ /#{FORMATS.join('|')}/
|
31
|
+
f = Export.send(parse[0].to_sym)
|
32
|
+
"File saved! #{f}"
|
33
|
+
else
|
34
|
+
puts "Searching...#{txt.join}"
|
35
|
+
print_parts Part.search(txt.join.upcase)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
if part = Part.create_or_increment(*parse)
|
39
|
+
return "Done. #{part.facts}"
|
40
|
+
else
|
41
|
+
"Problems creating part..."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
// Knockout JavaScript library v1.1.1
|
2
|
+
// (c) 2010 Steven Sanderson - http://knockoutjs.com/
|
3
|
+
// License: Ms-Pl (http://www.opensource.org/licenses/ms-pl.html)
|
4
|
+
|
5
|
+
function a(f){throw f;}var m=true,o=null,q=false,r=window.ko={};r.b=function(f,b){for(var d=f.split("."),c=window,e=0;e<d.length-1;e++)c=c[d[e]];c[d[d.length-1]]=b};r.g=function(f,b,d){f[b]=d};
|
6
|
+
r.a=new function(){var f=/^(\s|\u00A0)+|(\s|\u00A0)+$/g;return{ca:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],h:function(b,d){for(var c=0,e=b.length;c<e;c++)d(b[c])},i:function(b,d){if(typeof b.indexOf=="function")return b.indexOf(d);for(var c=0,e=b.length;c<e;c++)if(b[c]==d)return c;return-1},wa:function(b,d,c){for(var e=0,h=b.length;e<h;e++)if(d.call(c,b[e]))return b[e];return o},Z:function(b,d){var c=r.a.i(b,d);c>=0&&b.splice(c,1)},Y:function(b){b=b||[];for(var d=[],c=0,e=b.length;c<
|
7
|
+
e;c++)r.a.i(d,b[c])<0&&d.push(b[c]);return d},L:function(b,d){b=b||[];for(var c=[],e=0,h=b.length;e<h;e++)c.push(d(b[e]));return c},K:function(b,d){b=b||[];for(var c=[],e=0,h=b.length;e<h;e++)d(b[e])&&c.push(b[e]);return c},M:function(b,d){for(var c=0,e=d.length;c<e;c++)b.push(d[c])},ba:function(b){for(;b.firstChild;){r.a.e.N(b.firstChild);b.removeChild(b.firstChild)}},Ra:function(b,d){r.a.ba(b);d&&r.a.h(d,function(c){b.appendChild(c)})},ka:function(b,d){var c=b.nodeType?[b]:b;if(c.length>0){for(var e=
|
8
|
+
c[0],h=e.parentNode,g=0,i=d.length;g<i;g++)h.insertBefore(d[g],e);g=0;for(i=c.length;g<i;g++){r.a.e.N(c[g]);h.removeChild(c[g])}}},na:function(b,d){if(navigator.userAgent.indexOf("MSIE 6")>=0)b.setAttribute("selected",d);else b.selected=d},Ha:function(b,d){if(!b||b.nodeType!=1)return[];var c=[];b.getAttribute(d)!==o&&c.push(b);for(var e=b.getElementsByTagName("*"),h=0,g=e.length;h<g;h++)e[h].getAttribute(d)!==o&&c.push(e[h]);return c},k:function(b){return(b||"").replace(f,"")},Ya:function(b,d){for(var c=
|
9
|
+
[],e=(b||"").split(d),h=0,g=e.length;h<g;h++){var i=r.a.k(e[h]);i!==""&&c.push(i)}return c},Sa:function(b,d){b=b||"";if(d.length>b.length)return q;return b.substring(0,d.length)===d},Fa:function(b,d){if(d===undefined)return(new Function("return "+b))();with(d)return eval("("+b+")")},Da:function(b,d){if(d.compareDocumentPosition)return(d.compareDocumentPosition(b)&16)==16;for(;b!=o;){if(b==d)return m;b=b.parentNode}return q},A:function(b){return r.a.Da(b,document)},q:function(b,d,c){if(typeof jQuery!=
|
10
|
+
"undefined")jQuery(b).bind(d,c);else if(typeof b.addEventListener=="function")b.addEventListener(d,c,q);else if(typeof b.attachEvent!="undefined")b.attachEvent("on"+d,function(e){c.call(b,e)});else a(Error("Browser doesn't support addEventListener or attachEvent"))},Va:function(b,d){if(!(b&&b.nodeType))a(Error("element must be a DOM node when calling triggerEvent"));if(typeof document.createEvent=="function")if(typeof b.dispatchEvent=="function"){var c=document.createEvent(d=="click"?"MouseEvents":
|
11
|
+
"HTMLEvents");c.initEvent(d,m,m,window,0,0,0,0,0,q,q,q,q,0,b);b.dispatchEvent(c)}else a(Error("The supplied element doesn't support dispatchEvent"));else if(typeof b.fireEvent!="undefined"){if(d=="click")if(b.tagName=="INPUT"&&(b.type.toLowerCase()=="checkbox"||b.type.toLowerCase()=="radio"))b.checked=b.checked!==m;b.fireEvent("on"+d)}else a(Error("Browser doesn't support triggering events"))},d:function(b){return r.C(b)?b():b},Ca:function(b,d){return r.a.i((b.className||"").split(/\s+/),d)>=0},Ua:function(b,
|
12
|
+
d,c){var e=r.a.Ca(b,d);if(c&&!e)b.className=(b.className||"")+" "+d;else if(e&&!c){c=(b.className||"").split(/\s+/);e="";for(var h=0;h<c.length;h++)if(c[h]!=d)e+=c[h]+" ";b.className=r.a.k(e)}},Oa:function(b,d){b=r.a.d(b);d=r.a.d(d);for(var c=[],e=b;e<=d;e++)c.push(e);return c},ha:function(b){for(var d=[],c=b.length-1;c>=0;c--)d.push(b[c]);return d},P:/MSIE 6/i.test(navigator.userAgent),ea:function(b,d){for(var c=r.a.ha(b.getElementsByTagName("INPUT")).concat(r.a.ha(b.getElementsByTagName("TEXTAREA"))),
|
13
|
+
e=typeof d=="string"?function(i){return i.name===d}:function(i){return d.test(i.name)},h=[],g=c.length-1;g>=0;g--)e(c[g])&&h.push(c[g]);return h},G:function(b){if(typeof b=="string")if(b=r.a.k(b)){if(window.JSON&&window.JSON.parse)return window.JSON.parse(b);return(new Function("return "+b))()}return o},T:function(b){if(typeof JSON=="undefined"||typeof JSON.stringify=="undefined")a(Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js"));
|
14
|
+
return JSON.stringify(r.a.d(b))},Na:function(b,d,c){c=c||{};var e=c.params||{},h=c.includeFields||this.ca,g=b;if(typeof b=="object"&&b.tagName=="FORM"){g=b.action;for(var i=h.length-1;i>=0;i--)for(var j=r.a.ea(b,h[i]),k=j.length-1;k>=0;k--)e[j[k].name]=j[k].value}d=r.a.d(d);var l=document.createElement("FORM");l.style.display="none";l.action=g;l.method="post";for(var n in d){b=document.createElement("INPUT");b.name=n;b.value=r.a.T(r.a.d(d[n]));l.appendChild(b)}for(n in e){b=document.createElement("INPUT");
|
15
|
+
b.name=n;b.value=e[n];l.appendChild(b)}document.body.appendChild(l);c.submitter?c.submitter(l):l.submit();setTimeout(function(){l.parentNode.removeChild(l)},0)},e:{Wa:0,w:"__ko__"+(new Date).getTime(),Xa:{},t:function(b,d){var c=r.a.e.da(b,q);return c===undefined?undefined:c[d]},la:function(b,d,c){r.a.e.da(b,m)[d]=c},da:function(b,d){var c=b[r.a.e.w];if(!c){if(!d)return;c=b[r.a.e.w]="ko"+r.a.e.Wa++;r.a.e[c]={}}return r.a.e[c]},$:function(b){var d=b[r.a.e.w];if(d){delete r.a.e[d];b[r.a.e.w]=o}},N:function(b){if(!(b.nodeType!=
|
16
|
+
1&&b.nodeType!=9)){r.a.e.$(b);b=b.getElementsByTagName("*");for(var d=0,c=b.length;d<c;d++)r.a.e.$(b[d])}}}}};r.b("ko.utils",r.a);r.b("ko.utils.arrayForEach",r.a.h);r.b("ko.utils.arrayFirst",r.a.wa);r.b("ko.utils.arrayFilter",r.a.K);r.b("ko.utils.arrayGetDistinctValues",r.a.Y);r.b("ko.utils.arrayIndexOf",r.a.i);r.b("ko.utils.arrayMap",r.a.L);r.b("ko.utils.arrayPushAll",r.a.M);r.b("ko.utils.arrayRemoveItem",r.a.Z);r.b("ko.utils.fieldsIncludedWithJsonPost",r.a.ca);r.b("ko.utils.getFormFields",r.a.ea);
|
17
|
+
r.b("ko.utils.postJson",r.a.Na);r.b("ko.utils.parseJson",r.a.G);r.b("ko.utils.stringifyJson",r.a.T);r.b("ko.utils.range",r.a.Oa);r.b("ko.utils.triggerEvent",r.a.Va);r.b("ko.utils.unwrapObservable",r.a.d);Function.prototype.bind||(Function.prototype.bind=function(f){var b=this,d=Array.prototype.slice.call(arguments);f=d.shift();return function(){return b.apply(f,d.concat(Array.prototype.slice.call(arguments)))}});
|
18
|
+
r.j=function(){function f(){return((1+Math.random())*4294967296|0).toString(16).substring(1)}function b(c,e){if(c)if(c.nodeType==8){var h=r.j.ia(c.nodeValue);h!=o&&e.push({Ba:c,Ka:h})}else if(c.nodeType==1){h=0;for(var g=c.childNodes,i=g.length;h<i;h++)b(g[h],e)}}var d={};return{R:function(c){if(typeof c!="function")a(Error("You can only pass a function to ko.memoization.memoize()"));var e=f()+f();d[e]=c;return"<!--[ko_memo:"+e+"]--\>"},qa:function(c,e){var h=d[c];if(h===undefined)a(Error("Couldn't find any memo with ID "+
|
19
|
+
c+". Perhaps it's already been unmemoized."));try{h.apply(o,e||[]);return m}finally{delete d[c]}},ra:function(c,e){var h=[];b(c,h);for(var g=0,i=h.length;g<i;g++){var j=h[g].Ba,k=[j];e&&r.a.M(k,e);r.j.qa(h[g].Ka,k);j.nodeValue="";j.parentNode&&j.parentNode.removeChild(j)}},ia:function(c){return(c=c.match(/^\[ko_memo\:(.*?)\]$/))?c[1]:o}}}();r.b("ko.memoization",r.j);r.b("ko.memoization.memoize",r.j.R);r.b("ko.memoization.unmemoize",r.j.qa);r.b("ko.memoization.parseMemoText",r.j.ia);
|
20
|
+
r.b("ko.memoization.unmemoizeDomNodeAndDescendants",r.j.ra);r.Ta=function(f,b){this.ya=f;this.s=b;r.g(this,"dispose",this.s)};r.U=function(){var f=[];this.V=function(b,d){var c=new r.Ta(d?function(){b.call(d)}:b,function(){r.a.Z(f,c)});f.push(c);return c};this.v=function(b){r.a.h(f.slice(0),function(d){d&&d.ya(b)})};this.Ia=function(){return f.length};r.g(this,"subscribe",this.V);r.g(this,"notifySubscribers",this.v);r.g(this,"getSubscriptionsCount",this.Ia)};
|
21
|
+
r.ga=function(f){return typeof f.V=="function"&&typeof f.v=="function"};r.b("ko.subscribable",r.U);r.b("ko.isSubscribable",r.ga);r.z=function(){var f=[];return{xa:function(){f.push([])},end:function(){return f.pop()},ja:function(b){if(!r.ga(b))a("Only subscribable things can act as dependencies");f.length>0&&f[f.length-1].push(b)}}}();
|
22
|
+
r.p=function(f){function b(){if(arguments.length>0){d=arguments[0];b.v(d);return this}else{r.z.ja(b);return d}}var d=f;b.n=r.p;b.I=function(){b.v(d)};r.U.call(b);r.g(b,"valueHasMutated",b.I);return b};r.C=function(f){if(f===o||f===undefined||f.n===undefined)return q;if(f.n===r.p)return m;return r.C(f.n)};r.D=function(f){return typeof f=="function"&&f.n===r.p};r.b("ko.observable",r.p);r.b("ko.isObservable",r.C);r.b("ko.isWriteableObservable",r.D);
|
23
|
+
r.Ma=function(f){var b=new r.p(f);r.a.h(["pop","push","reverse","shift","sort","splice","unshift"],function(d){b[d]=function(){var c=b();c=c[d].apply(c,arguments);b.I();return c}});r.a.h(["slice"],function(d){b[d]=function(){var c=b();return c[d].apply(c,arguments)}});b.remove=function(d){for(var c=b(),e=[],h=[],g=typeof d=="function"?d:function(l){return l===d},i=0,j=c.length;i<j;i++){var k=c[i];g(k)?h.push(k):e.push(k)}b(e);return h};b.Pa=function(d){if(!d)return[];return b.remove(function(c){return r.a.i(d,
|
24
|
+
c)>=0})};b.aa=function(d){for(var c=b(),e=typeof d=="function"?d:function(g){return g===d},h=c.length-1;h>=0;h--)if(e(c[h]))c[h]._destroy=m;b.I()};b.Aa=function(d){if(!d)return[];return b.aa(function(c){return r.a.i(d,c)>=0})};b.indexOf=function(d){var c=b();return r.a.i(c,d)};b.replace=function(d,c){var e=b.indexOf(d);if(e>=0){b()[e]=c;b.I()}};r.g(b,"remove",b.remove);r.g(b,"removeAll",b.Pa);r.g(b,"destroy",b.aa);r.g(b,"destroyAll",b.Aa);r.g(b,"indexOf",b.indexOf);return b};
|
25
|
+
r.b("ko.observableArray",r.Ma);
|
26
|
+
r.m=function(f,b,d){function c(){r.a.h(i,function(l){l.s()});i=[]}function e(l){c();r.a.h(l,function(n){i.push(n.V(h))})}function h(){if(!k&&d&&typeof d.disposeWhen=="function")if(d.disposeWhen()){g.s();return}try{r.z.xa();j=b?f.call(b):f()}finally{var l=r.a.Y(r.z.end());e(l)}g.v(j);k=q}function g(){if(arguments.length>0)a("Cannot write a value to a dependentObservable. Do not pass any parameters to it");r.z.ja(g);return j}if(typeof f!="function")a("Pass a function that returns the value of the dependentObservable");var i=
|
27
|
+
[],j,k=m;g.n=r.m;g.Ga=function(){return i.length};g.s=function(){c()};r.U.call(g);h();r.g(g,"dispose",g.s);r.g(g,"getDependenciesCount",g.Ga);return g};r.m.n=r.p;r.b("ko.dependentObservable",r.m);
|
28
|
+
(function(){function f(c,e,h){h=h||new d;c=e(c);if(!(typeof c=="object"&&c!==o&&c!==undefined))return c;var g=c instanceof Array?[]:{};h.save(c,g);b(c,function(i){var j=e(c[i]);switch(typeof j){case "boolean":case "number":case "string":case "function":g[i]=j;break;case "object":case "undefined":var k=h.t(j);g[i]=k!==undefined?k:f(j,e,h)}});return g}function b(c,e){if(c instanceof Array)for(var h=0;h<c.length;h++)e(h);else for(h in c)e(h)}function d(){var c=[],e=[];this.save=function(h,g){var i=r.a.i(c,
|
29
|
+
h);if(i>=0)e[i]=g;else{c.push(h);e.push(g)}};this.t=function(h){h=r.a.i(c,h);return h>=0?e[h]:undefined}}r.pa=function(c){if(arguments.length==0)a(Error("When calling ko.toJS, pass the object you want to convert."));return f(c,function(e){for(var h=0;r.C(e)&&h<10;h++)e=e();return e})};r.toJSON=function(c){c=r.pa(c);return r.a.T(c)}})();r.b("ko.toJS",r.pa);r.b("ko.toJSON",r.toJSON);
|
30
|
+
r.f={l:function(f){if(f.tagName=="OPTION"){var b=f.getAttribute("value");if(b!==r.c.options.F)return b;return r.a.e.t(f,r.c.options.F)}else return f.tagName=="SELECT"?f.selectedIndex>=0?r.f.l(f.options[f.selectedIndex]):undefined:f.value},J:function(f,b){if(f.tagName=="OPTION"){r.a.e.la(f,r.c.options.F,b);f.value=r.c.options.F}else if(f.tagName=="SELECT")for(var d=f.options.length-1;d>=0;d--){if(r.f.l(f.options[d])==b){f.selectedIndex=d;break}}else f.value=b}};r.b("ko.selectExtensions",r.f);
|
31
|
+
r.b("ko.selectExtensions.readValue",r.f.l);r.b("ko.selectExtensions.writeValue",r.f.J);
|
32
|
+
r.o=function(){function f(e,h){return e.replace(b,function(g,i){return h[i]})}var b=/\[ko_token_(\d+)\]/g,d=/^[\_$a-z][\_$a-z]*(\[.*?\])*(\.[\_$a-z][\_$a-z]*(\[.*?\])*)*$/i,c=["true","false"];return{G:function(e){e=r.a.k(e);if(e.length<3)return{};for(var h=[],g=o,i,j=e.charAt(0)=="{"?1:0;j<e.length;j++){var k=e.charAt(j);if(g===o)switch(k){case '"':case "'":case "/":g=j;i=k;break;case "{":g=j;i="}";break;case "[":g=j;i="]"}else if(k==i){k=e.substring(g,j+1);h.push(k);var l="[ko_token_"+(h.length-
|
33
|
+
1)+"]";e=e.substring(0,g)+l+e.substring(j+1);j-=k.length-l.length;g=o}}g={};e=e.split(",");i=0;for(j=e.length;i<j;i++){l=e[i];var n=l.indexOf(":");if(n>0&&n<l.length-1){k=r.a.k(l.substring(0,n));l=r.a.k(l.substring(n+1));if(k.charAt(0)=="{")k=k.substring(1);if(l.charAt(l.length-1)=="}")l=l.substring(0,l.length-1);k=r.a.k(f(k,h));l=r.a.k(f(l,h));g[k]=l}}return g},O:function(e){var h=r.o.G(e),g=[],i;for(i in h){var j=h[i],k;k=j;k=r.a.i(c,r.a.k(k).toLowerCase())>=0?q:k.match(d)!==o;if(k){g.length>0&&
|
34
|
+
g.push(", ");g.push(i+" : function(__ko_value) { "+j+" = __ko_value; }")}}if(g.length>0)e=e+", '_ko_property_writers' : { "+g.join("")+" } ";return e}}}();r.b("ko.jsonExpressionRewriting",r.o);r.b("ko.jsonExpressionRewriting.parseJson",r.o.G);r.b("ko.jsonExpressionRewriting.insertPropertyAccessorsIntoJson",r.o.O);r.c={};
|
35
|
+
r.X=function(f,b,d){function c(i){return function(){return g[i]}}function e(){return g}var h=m,g;new r.m(function(){var i;if(!(i=typeof b=="function"?b():b)){var j=f.getAttribute("data-bind");try{var k=" { "+r.o.O(j)+" } ";i=r.a.Fa(k,d===o?window:d)}catch(l){a(Error("Unable to parse binding attribute.\nMessage: "+l+";\nAttribute value: "+j))}}g=i;if(h)for(var n in g)r.c[n]&&typeof r.c[n].init=="function"&&(0,r.c[n].init)(f,c(n),e,d);for(n in g)r.c[n]&&typeof r.c[n].update=="function"&&(0,r.c[n].update)(f,
|
36
|
+
c(n),e,d)},o,{disposeWhen:function(){return!r.a.A(f)}});h=q};r.ta=function(f,b){if(b&&b.nodeType==undefined)a(Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node (note: this is a breaking change since KO version 1.05)"));b=b||window.document.body;var d=r.a.Ha(b,"data-bind");r.a.h(d,function(c){r.X(c,o,f)})};r.b("ko.bindingHandlers",r.c);r.b("ko.applyBindings",r.ta);
|
37
|
+
r.c.click={init:function(f,b,d,c){r.a.q(f,"click",function(e){var h,g=b();try{h=g.call(c)}finally{if(h!==m)if(e.preventDefault)e.preventDefault();else e.returnValue=q}})}};r.c.submit={init:function(f,b,d,c){if(typeof b()!="function")a(Error("The value for a submit binding must be a function to invoke on submit"));r.a.q(f,"submit",function(e){var h,g=b();try{h=g.call(c,f)}finally{if(h!==m)if(e.preventDefault)e.preventDefault();else e.returnValue=q}})}};
|
38
|
+
r.c.visible={update:function(f,b){var d=r.a.d(b()),c=f.style.display!="none";if(d&&!c)f.style.display="";else if(!d&&c)f.style.display="none"}};r.c.enable={update:function(f,b){var d=r.a.d(b());if(d&&f.disabled)f.removeAttribute("disabled");else if(!d&&!f.disabled)f.disabled=m}};r.c.disable={update:function(f,b){r.c.enable.update(f,function(){return!r.a.d(b())})}};
|
39
|
+
r.c.value={init:function(f,b,d){var c=d().valueUpdate||"change",e=q;if(r.a.Sa(c,"after")){e=m;c=c.substring(5)}var h=e?function(g){setTimeout(g,0)}:function(g){g()};r.a.q(f,c,function(){h(function(){var g=b(),i=r.f.l(f);if(r.D(g))g(i);else{g=d();g._ko_property_writers&&g._ko_property_writers.value&&g._ko_property_writers.value(i)}})})},update:function(f,b){var d=r.a.d(b());if(d!=r.f.l(f)){var c=function(){r.f.J(f,d)};c();f.tagName=="SELECT"&&setTimeout(c,0)}}};
|
40
|
+
r.c.options={update:function(f,b,d){if(f.tagName!="SELECT")a(Error("options binding applies only to SELECT elements"));var c=r.a.L(r.a.K(f.childNodes,function(j){return j.tagName&&j.tagName=="OPTION"&&j.selected}),function(j){return r.f.l(j)||j.innerText||j.textContent}),e=r.a.d(b());r.a.ba(f);if(e){var h=d();if(typeof e.length!="number")e=[e];if(h.optionsCaption){var g=document.createElement("OPTION");g.innerHTML=h.optionsCaption;r.f.J(g,undefined);f.appendChild(g)}d=0;for(b=e.length;d<b;d++){g=
|
41
|
+
document.createElement("OPTION");var i=typeof h.optionsValue=="string"?e[d][h.optionsValue]:e[d];if(typeof i=="object")r.f.J(g,i);else g.value=i.toString();g.innerHTML=(typeof h.optionsText=="string"?e[d][h.optionsText]:i).toString();f.appendChild(g)}f=f.getElementsByTagName("OPTION");d=e=0;for(b=f.length;d<b;d++)if(r.a.i(c,r.f.l(f[d]))>=0){r.a.na(f[d],m);e++}}}};r.c.options.F="__ko.bindingHandlers.options.optionValueDomData__";
|
42
|
+
r.c.selectedOptions={fa:function(f){var b=[];f=f.childNodes;for(var d=0,c=f.length;d<c;d++){var e=f[d];e.tagName=="OPTION"&&e.selected&&b.push(r.f.l(e))}return b},init:function(f,b,d){r.a.q(f,"change",function(){var c=b();if(r.D(c))c(r.c.selectedOptions.fa(this));else{c=d();c._ko_property_writers&&c._ko_property_writers.value&&c._ko_property_writers.value(r.c.selectedOptions.fa(this))}})},update:function(f,b){if(f.tagName!="SELECT")a(Error("values binding applies only to SELECT elements"));var d=
|
43
|
+
r.a.d(b());if(d&&typeof d.length=="number")for(var c=f.childNodes,e=0,h=c.length;e<h;e++){var g=c[e];g.tagName=="OPTION"&&r.a.na(g,r.a.i(d,r.f.l(g))>=0)}}};r.c.text={update:function(f,b){var d=r.a.d(b());typeof f.innerText=="string"?f.innerText=d:f.textContent=d}};r.c.css={update:function(f,b){var d=r.a.d(b()||{}),c;for(c in d)if(typeof c=="string"){var e=r.a.d(d[c]);r.a.Ua(f,c,e)}}};
|
44
|
+
r.c.style={update:function(f,b){var d=r.a.d(b()||{}),c;for(c in d)if(typeof c=="string"){var e=r.a.d(d[c]);f.style[c]=e||""}}};r.c.uniqueName={init:function(f,b){if(b()){f.name="ko_unique_"+ ++r.c.uniqueName.za;r.a.P&&f.mergeAttributes(document.createElement("<INPUT name='"+f.name+"'/>"),q)}}};r.c.uniqueName.za=0;
|
45
|
+
r.c.checked={init:function(f,b,d){function c(){var e=b();if(r.D(e))if(f.type=="checkbox")e(f.checked);else f.type=="radio"&&f.checked&&e(f.value);else{e=d();if(e._ko_property_writers&&e._ko_property_writers.checked)if(f.type=="checkbox")e._ko_property_writers.checked(f.checked);else f.type=="radio"&&f.checked&&e._ko_property_writers.checked(f.value)}}r.a.q(f,"change",c);r.a.q(f,"click",c);f.type=="radio"&&!f.name&&r.c.uniqueName.init(f,function(){return m})},update:function(f,b){var d=r.a.d(b());
|
46
|
+
if(f.type=="checkbox")(f.checked=d)&&r.a.P&&f.mergeAttributes(document.createElement("<INPUT type='checkbox' checked='checked' />"),q);else if(f.type=="radio"){f.checked=f.value==d;f.value==d&&r.a.P&&f.mergeAttributes(document.createElement("<INPUT type='radio' checked='checked' />"),q)}}};
|
47
|
+
r.W=function(){this.renderTemplate=function(){a("Override renderTemplate in your ko.templateEngine subclass")};this.isTemplateRewritten=function(){a("Override isTemplateRewritten in your ko.templateEngine subclass")};this.rewriteTemplate=function(){a("Override rewriteTemplate in your ko.templateEngine subclass")};this.createJavaScriptEvaluatorBlock=function(){a("Override createJavaScriptEvaluatorBlock in your ko.templateEngine subclass")}};r.b("ko.templateEngine",r.W);
|
48
|
+
r.H=function(){var f=/(<[a-z]+(\s+(?!data-bind=)[a-z0-9]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/g;return{Ea:function(b,d){d.isTemplateRewritten(b)||d.rewriteTemplate(b,function(c){return r.H.La(c,d)})},La:function(b,d){return b.replace(f,function(c,e,h,g,i,j,k){c=k;c=r.o.O(c);return d.createJavaScriptEvaluatorBlock("ko.templateRewriting.applyMemoizedBindingsToNextSibling(function() { return (function() { return { "+c+" } })() })")+e})},ua:function(b){return r.j.R(function(d,
|
49
|
+
c){d.nextSibling&&r.X(d.nextSibling,b,c)})}}}();r.b("ko.templateRewriting",r.H);r.b("ko.templateRewriting.applyMemoizedBindingsToNextSibling",r.H.ua);
|
50
|
+
(function(){function f(d,c,e,h,g){var i=r.a.d(h);g=g||{};var j=g.templateEngine||b;r.H.Ea(e,j);e=j.renderTemplate(e,i,g);if(typeof e.length!="number"||e.length>0&&typeof e[0].nodeType!="number")a("Template engine must return an array of DOM nodes");e&&r.a.h(e,function(k){r.j.ra(k,[h])});switch(c){case "replaceChildren":r.a.Ra(d,e);break;case "replaceNode":r.a.ka(d,e);break;case "ignoreTargetNode":break;default:a(Error("Unknown renderMode: "+c))}return e}var b;r.oa=function(d){if(d!=undefined&&!(d instanceof
|
51
|
+
r.W))a("templateEngine must inherit from ko.templateEngine");b=d};r.S=function(d,c,e,h,g){e=e||{};if((e.templateEngine||b)==undefined)a("Set a template engine before calling renderTemplate");g=g||"replaceChildren";if(h){var i=h.nodeType?h:h.length>0?h[0]:o;return new r.m(function(){var j=f(h,g,d,c,e);if(g=="replaceNode"){h=j;i=h.nodeType?h:h.length>0?h[0]:o}},o,{disposeWhen:function(){return!i||!r.a.A(i)}})}else return r.j.R(function(j){r.S(d,c,e,j,"replaceNode")})};r.Qa=function(d,c,e,h){new r.m(function(){var g=
|
52
|
+
r.a.d(c)||[];if(typeof g.length=="undefined")g=[g];g=r.a.K(g,function(i){return e.includeDestroyed||!i._destroy});r.a.ma(h,g,function(i){return f(o,"ignoreTargetNode",d,i,e)},e)},o,{disposeWhen:function(){return!r.a.A(h)}})};r.c.template={update:function(d,c,e,h){e=r.a.d(c());c=typeof e=="string"?e:e.name;if(typeof e.foreach!="undefined")r.Qa(c,e.foreach||[],{afterAdd:e.afterAdd,beforeRemove:e.beforeRemove,includeDestroyed:e.includeDestroyed},d);else{e=e.data;r.S(c,typeof e=="undefined"?h:e,o,d)}}}})();
|
53
|
+
r.b("ko.setTemplateEngine",r.oa);r.b("ko.renderTemplate",r.S);
|
54
|
+
r.a.r=function(f,b,d){if(d===undefined)return r.a.r(f,b,1)||r.a.r(f,b,10)||r.a.r(f,b,Number.MAX_VALUE);else{f=f||[];b=b||[];for(var c=f,e=b,h=[],g=0;g<=e.length;g++)h[g]=[];g=0;for(var i=Math.min(c.length,d);g<=i;g++)h[0][g]=g;g=1;for(i=Math.min(e.length,d);g<=i;g++)h[g][0]=g;i=c.length;var j,k=e.length;for(g=1;g<=i;g++){var l=Math.min(k,g+d);for(j=Math.max(1,g-d);j<=l;j++)h[j][g]=c[g-1]===e[j-1]?h[j-1][g-1]:Math.min(h[j-1][g]===undefined?Number.MAX_VALUE:h[j-1][g]+1,h[j][g-1]===undefined?Number.MAX_VALUE:
|
55
|
+
h[j][g-1]+1)}f=f;b=b;d=f.length;c=b.length;e=[];g=h[c][d];if(g===undefined)h=o;else{for(;d>0||c>0;){i=h[c][d];j=c>0?h[c-1][d]:g+1;k=d>0?h[c][d-1]:g+1;l=c>0&&d>0?h[c-1][d-1]:g+1;if(j===undefined||j<i-1)j=g+1;if(k===undefined||k<i-1)k=g+1;if(l<i-1)l=g+1;if(j<=k&&j<l){e.push({status:"added",value:b[c-1]});c--}else{if(k<j&&k<l)e.push({status:"deleted",value:f[d-1]});else{e.push({status:"retained",value:f[d-1]});c--}d--}}h=e.reverse()}return h}};r.b("ko.utils.compareArrays",r.a.r);
|
56
|
+
(function(){function f(b,d){var c=[];r.m(function(){var e=b(d)||[];c.length>0&&r.a.ka(c,e);c.splice(0,c.length);r.a.M(c,e)},o,{disposeWhen:function(){return c.length==0||!r.a.A(c[0])}});return c}r.a.ma=function(b,d,c,e){d=d||[];e=e||{};var h=r.a.e.t(b,"setDomNodeChildrenFromArrayMapping_lastMappingResult")===undefined,g=r.a.e.t(b,"setDomNodeChildrenFromArrayMapping_lastMappingResult")||[],i=r.a.L(g,function(s){return s.va}),j=r.a.r(i,d);d=[];var k=0,l=[];i=[];for(var n=o,p=0,w=j.length;p<w;p++)switch(j[p].status){case "retained":var t=
|
57
|
+
g[k];d.push(t);if(t.B.length>0)n=t.B[t.B.length-1];k++;break;case "deleted":r.a.h(g[k].B,function(s){l.push({element:s,index:p,value:j[p].value});n=s});k++;break;case "added":t=f(c,j[p].value);d.push({va:j[p].value,B:t});for(var v=0,x=t.length;v<x;v++){var u=t[v];i.push({element:u,index:p,value:j[p].value});if(n==o)b.firstChild?b.insertBefore(u,b.firstChild):b.appendChild(u);else n.nextSibling?b.insertBefore(u,n.nextSibling):b.appendChild(u);n=u}}r.a.h(l,function(s){r.a.e.N(s.element)});c=q;if(!h){if(e.afterAdd)for(p=
|
58
|
+
0;p<i.length;p++)e.afterAdd(i[p].element,i[p].index,i[p].value);if(e.beforeRemove){for(p=0;p<l.length;p++)e.beforeRemove(l[p].element,l[p].index,l[p].value);c=m}}c||r.a.h(l,function(s){s.element.parentNode&&s.element.parentNode.removeChild(s.element)});r.a.e.la(b,"setDomNodeChildrenFromArrayMapping_lastMappingResult",d)}})();r.b("ko.utils.setDomNodeChildrenFromArrayMapping",r.a.ma);
|
59
|
+
r.Q=function(){function f(d){var c=document.getElementById(d);if(c==o)a(Error("Cannot find template with ID="+d));return c}this.u=function(){if(typeof jQuery=="undefined"||!jQuery.tmpl)return 0;if(jQuery.tmpl.tag)return 2;return 1}();var b=RegExp("__ko_apos__","g");this.renderTemplate=function(d,c){if(this.u==0)a(Error("jquery.tmpl not detected.\nTo use KO's default template engine, reference jQuery and jquery.tmpl. See Knockout installation documentation for more details."));if(this.u==1){var e=
|
60
|
+
'<script type="text/html">'+f(d).text+"<\/script>";e=jQuery.tmpl(e,c)[0].text.replace(b,"'");return jQuery.clean([e],document)}c=[c];e=f(d).text;return jQuery.tmpl(e,c)};this.isTemplateRewritten=function(d){return f(d).Ja===m};this.rewriteTemplate=function(d,c){var e=f(d),h=c(e.text);if(this.u==1){h=r.a.k(h);h=h.replace(/([\s\S]*?)(\${[\s\S]*?}|{{[\=a-z][\s\S]*?}}|$)/g,function(g,i,j){return i.replace(/\'/g,"__ko_apos__")+j})}e.text=h;e.Ja=m};this.createJavaScriptEvaluatorBlock=function(d){if(this.u==
|
61
|
+
1)return"{{= "+d+"}}";return"{{ko_code ((function() { return "+d+" })()) }}"};this.sa=function(d,c){document.write("<script type='text/html' id='"+d+"'>"+c+"<\/script>")};r.g(this,"addTemplate",this.sa);if(this.u>1)jQuery.tmpl.tag.ko_code={open:"_.push($1 || '');"}};r.Q.prototype=new r.W;r.oa(new r.Q);r.b("ko.jqueryTmplTemplateEngine",r.Q);
|
62
|
+
|
63
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
# Export Redis DB to various formats
|
4
|
+
#
|
5
|
+
#
|
6
|
+
module Stockr
|
7
|
+
|
8
|
+
class Export
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def all
|
13
|
+
@all ||= Part.search("") || []
|
14
|
+
end
|
15
|
+
|
16
|
+
def timestamp
|
17
|
+
@ts ||= Time.now.strftime("%y-%m-%d-%H-%M-%S")
|
18
|
+
end
|
19
|
+
|
20
|
+
def format
|
21
|
+
<<TXT
|
22
|
+
Stockr Export
|
23
|
+
|
24
|
+
#{timestamp}
|
25
|
+
|
26
|
+
-------------------------------------------
|
27
|
+
|
28
|
+
#{Part.list.join("\n")}
|
29
|
+
|
30
|
+
-------------------------------------------
|
31
|
+
Total: #{all.size}
|
32
|
+
|
33
|
+
TXT
|
34
|
+
end
|
35
|
+
|
36
|
+
def filename
|
37
|
+
"my_stockr_" + timestamp
|
38
|
+
end
|
39
|
+
|
40
|
+
def txt
|
41
|
+
write(format, "txt")
|
42
|
+
end
|
43
|
+
|
44
|
+
def csv
|
45
|
+
output = "name,qty,price\n"
|
46
|
+
for p in all
|
47
|
+
output << [p.name, p.qty, p.price].join(",")
|
48
|
+
end
|
49
|
+
output << "\n"
|
50
|
+
write(output, "csv")
|
51
|
+
end
|
52
|
+
|
53
|
+
def html
|
54
|
+
end
|
55
|
+
|
56
|
+
def pdf
|
57
|
+
end
|
58
|
+
|
59
|
+
def xml
|
60
|
+
end
|
61
|
+
|
62
|
+
def write(txt, ext)
|
63
|
+
fname = filename + ".#{ext}"
|
64
|
+
File.open(fname, "w") { |f| f << txt }
|
65
|
+
fname
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
data/lib/stockr/part.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stockr
|
4
|
+
|
5
|
+
class Part
|
6
|
+
attr_reader :name, :qty, :price
|
7
|
+
|
8
|
+
def initialize(name, qty = 0, pr = 0)
|
9
|
+
@name = name.upcase
|
10
|
+
@qty = qty.to_i
|
11
|
+
self.price = pr
|
12
|
+
end
|
13
|
+
|
14
|
+
def save
|
15
|
+
Store.write(name, { :qty => qty, :price => price })
|
16
|
+
end
|
17
|
+
|
18
|
+
def facts
|
19
|
+
out = "#{qty}x #{name}"
|
20
|
+
if price && !price.zero?
|
21
|
+
out << ("." * (50 - out.size))
|
22
|
+
out << "$ %.3f" % price
|
23
|
+
out << " ($ %.3f)" % (price * qty) if qty != 1
|
24
|
+
end
|
25
|
+
out
|
26
|
+
end
|
27
|
+
|
28
|
+
def qty=(v)
|
29
|
+
@qty = v
|
30
|
+
end
|
31
|
+
|
32
|
+
def price=(pr)
|
33
|
+
@price = pr.kind_of?(Numeric) ? pr : pr.gsub(",", ".").to_f
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.create_or_increment(q, name, pr=0)
|
37
|
+
part = search(name, true) || new(name)
|
38
|
+
part.qty += q.to_i
|
39
|
+
part.price = pr
|
40
|
+
part.save
|
41
|
+
part
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.search(txt, exact = false)
|
45
|
+
if res = Store.find(exact ? txt : "*#{txt}*")
|
46
|
+
objs = res.map do |k, r|
|
47
|
+
new(k, r["qty"])
|
48
|
+
end
|
49
|
+
exact ? objs[0] : objs # FIXME: better way?
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.all; search(''); end
|
56
|
+
|
57
|
+
def self.list(txt = "*")
|
58
|
+
search(txt).map(&:facts) rescue []
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.missing
|
62
|
+
all.select { |p| p.qty <= 0 }
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
data/lib/stockr/store.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
module Stockr
|
3
|
+
|
4
|
+
|
5
|
+
class Store
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def connect!
|
9
|
+
@db = Redis.new
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def db
|
14
|
+
@@db ||= Redis.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def find(txt)
|
18
|
+
res = db.keys(txt)
|
19
|
+
return nil if res.empty?
|
20
|
+
res.reduce({}) { |h, r| h.merge(r => db.hgetall(r)) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def write(key, values)
|
24
|
+
db.hmset(key, *values.to_a.flatten)
|
25
|
+
end
|
26
|
+
|
27
|
+
def run(coe)
|
28
|
+
db.send(code)
|
29
|
+
end
|
30
|
+
|
31
|
+
def flush
|
32
|
+
db.flushall
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
end
|
data/lib/stockr/web.rb
ADDED
@@ -0,0 +1,300 @@
|
|
1
|
+
require "sinatra"
|
2
|
+
include Stockr
|
3
|
+
# unless const_defined?("Stockr")
|
4
|
+
|
5
|
+
# require "stockr"
|
6
|
+
|
7
|
+
get '/hi' do
|
8
|
+
"Hello World!"
|
9
|
+
end
|
10
|
+
|
11
|
+
get '/' do
|
12
|
+
@parts = Part.all
|
13
|
+
erb <<INDEX
|
14
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
15
|
+
<html class="cufon-active cufon-ready"><head>
|
16
|
+
|
17
|
+
|
18
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
19
|
+
<title>Stockr : My Stock</title>
|
20
|
+
<!--[if IE]><link rel="stylesheet" type="text/css" href="../css/tripoli.simple.ie.css" /><![endif]-->
|
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>
|
24
|
+
|
25
|
+
<style type="text/css">
|
26
|
+
/* Tripoli overrides */
|
27
|
+
.content { font-size: 1.1em !important; }
|
28
|
+
.content p + ul { margin-top: -0.8em; }
|
29
|
+
.sticker .content h1 { font-size: 1.6em; margin: 0.4em 0 0.4em 0; }
|
30
|
+
.sticker .content h2 { font-size: 1.4em ;}
|
31
|
+
.sticker .content h3 { font-size: 1.2em ;}
|
32
|
+
.sticker .content a, .sticker a { color: #A71500; text-decoration: underline; }
|
33
|
+
.sticker .content form { margin: 0; padding: 0; }
|
34
|
+
|
35
|
+
/* Basics */
|
36
|
+
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
|
+
|
55
|
+
.center { text-align: center }
|
56
|
+
.left { text-align: left }
|
57
|
+
.right { text-align: right }
|
58
|
+
.justify { text-align: justify }
|
59
|
+
|
60
|
+
.floatLeft { float: left !important; }
|
61
|
+
.floatRight { float: right !important; }
|
62
|
+
.floatNone { float: none !important; }
|
63
|
+
|
64
|
+
.clear { clear: both}
|
65
|
+
.clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden }
|
66
|
+
|
67
|
+
.relative { position: relative !important }
|
68
|
+
.absolute { position: absolute }
|
69
|
+
|
70
|
+
.inline { display: inline; }
|
71
|
+
.cursor { cursor: pointer }
|
72
|
+
|
73
|
+
ul.tickIcons li { list-style-type: none;
|
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; }
|
82
|
+
|
83
|
+
/* Page */
|
84
|
+
#wrapper {margin: 0 auto; width: 982px; }
|
85
|
+
html { background: #d05400 url(../img/main-background.jpg) no-repeat top center; }
|
86
|
+
body { font-family: arial; font-size: 14px; }
|
87
|
+
.sticker {
|
88
|
+
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;
|
89
|
+
opacity:0.9; -moz-box-shadow: 0 0 1.1em #666; -webkit-box-shadow: 0 0 1.1em #666; box-shadow: 0 0 1.1em #666;
|
90
|
+
}
|
91
|
+
.sticker .heading, .sticker h1 { color: #A71500; margin: 0.25em 0 0.25em 0; font-weight: bold; font-size: 1.2em; }
|
92
|
+
.stickerHeading {
|
93
|
+
background-color: rgb(229, 218, 214); height: 1.3em; margin: -2em -1.45em 1.25em -1.55em; padding: 0.7em 1em 1.1em 1.75em;
|
94
|
+
-moz-border-radius-topright: 1em; -moz-border-radius-topleft: 1em;
|
95
|
+
-webkit-border-top-right-radius: 1em; -webkit-border-top-left-radius: 1em;
|
96
|
+
border-top-right-radius: 1em; border-top-left-radius: 1em;
|
97
|
+
}
|
98
|
+
|
99
|
+
.engraved {
|
100
|
+
color: #350808;
|
101
|
+
text-shadow: rgba(253, 204, 197, 0.4) 1px 1px 0px;
|
102
|
+
}
|
103
|
+
|
104
|
+
.vspace { height: 2em;}
|
105
|
+
|
106
|
+
.liveExample { padding: 1em; background-color: #EEEEDD; border: 1px solid #CCC; max-width: 655px; }
|
107
|
+
.liveExample input { font-family: Arial; }
|
108
|
+
.liveExample b { font-weight: bold; }
|
109
|
+
.liveExample p { margin-top: 0.9em; margin-bottom: 0.9em; }
|
110
|
+
.liveExample select[multiple] { width: 100%; height: 8em; }
|
111
|
+
.liveExample h2 { margin-top: 0.4em; }
|
112
|
+
|
113
|
+
/* Menu */
|
114
|
+
.main-menu { display:table; float:right; margin: 1em 0 0.5em 0; font-size: 14px; font-weight: bold; }
|
115
|
+
.main-menu ul li { display: inline; }
|
116
|
+
.main-menu ul li a {
|
117
|
+
display: inline-block; padding: 0.4em 1.5em; text-decoration: none;
|
118
|
+
-moz-border-radius: 0.5em; -webkit-border-radius: 0.5em; border-radius: 0.5em;
|
119
|
+
}
|
120
|
+
.main-menu ul li a, .main-menu ul li a:visited { color: white; }
|
121
|
+
.main-menu ul li a.active, .main-menu ul li a:hover { background-color: rgb(81, 29, 0); }
|
122
|
+
|
123
|
+
/* Intro */
|
124
|
+
#introBadges li { width: 215px; float:left; text-align: center; padding: 0 0.5em 0 0.5em; color: #555; margin-bottom: 0.5em; }
|
125
|
+
|
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
|
+
|
135
|
+
.download-button {
|
136
|
+
background: url(../img/download-button-bg.png);
|
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; }
|
151
|
+
|
152
|
+
|
153
|
+
.homepageExample h2 { margin: 0.75em 0 0.5em 0;}
|
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; }
|
157
|
+
|
158
|
+
/* Footer */
|
159
|
+
#page-footer { padding: 15px; text-align: right; }
|
160
|
+
|
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
|
+
|
168
|
+
</head><body>
|
169
|
+
<div id="wrapper">
|
170
|
+
<div>
|
171
|
+
<div class="full">
|
172
|
+
<div class="sticker">
|
173
|
+
<div class="content">
|
174
|
+
|
175
|
+
<script src="gridEditor_files/jquery_002.js" type="text/javascript"> </script><h2>My Stock</h2><div class="liveExample">
|
176
|
+
|
177
|
+
<form action="/someServerSideHandler">
|
178
|
+
<p>You have asked for <span data-bind="text: parts().length">2</span> part(s)</p>
|
179
|
+
<table data-bind="visible: parts().length > 0">
|
180
|
+
<thead>
|
181
|
+
<tr>
|
182
|
+
<th>Part name</th>
|
183
|
+
<th>Price</th>
|
184
|
+
<th>
|
185
|
+
</th></tr>
|
186
|
+
</thead>
|
187
|
+
<tbody data-bind="template: { name: "partRowTemplate", foreach: parts }">
|
188
|
+
</tbody></table>
|
189
|
+
|
190
|
+
<button data-bind="click: addPart">Add Part</button>
|
191
|
+
<button data-bind="enable: parts().length > 0" type="submit">Submit</button>
|
192
|
+
</form>
|
193
|
+
|
194
|
+
<script id="partRowTemplate" type="text/html">
|
195
|
+
<tr>
|
196
|
+
<td>{{ko_code ((function() { return ko.templateRewriting.applyMemoizedBindingsToNextSibling(function() { return (function() { return { value: name, uniqueName: true, '_ko_property_writers' : { value : function(__ko_value) { name = __ko_value; } } } })() }) })()) }}<input class='required' /></td>
|
197
|
+
<td>{{ko_code ((function() { return ko.templateRewriting.applyMemoizedBindingsToNextSibling(function() { return (function() { return { value: price, uniqueName: true, '_ko_property_writers' : { value : function(__ko_value) { price = __ko_value; } } } })() }) })()) }}<input class='required number' /></td>
|
198
|
+
<td>{{ko_code ((function() { return ko.templateRewriting.applyMemoizedBindingsToNextSibling(function() { return (function() { return { click: function() { viewModel.removePart($data) } } })() }) })()) }}<a href='#' >Delete</a></td>
|
199
|
+
</tr>
|
200
|
+
</script>
|
201
|
+
|
202
|
+
|
203
|
+
<script type="text/javascript">
|
204
|
+
/*<![CDATA[*/
|
205
|
+
var viewModel = {
|
206
|
+
parts: ko.observableArray([
|
207
|
+
<!-- { name: "Tall Hat", price: "39.95" }, -->
|
208
|
+
<!-- { name: "Long Cloak", price: "120.00" } -->
|
209
|
+
]),
|
210
|
+
|
211
|
+
addPart: function () {
|
212
|
+
this.parts.push({ name: "", price: "" });
|
213
|
+
},
|
214
|
+
|
215
|
+
removePart: function (part) {
|
216
|
+
this.parts.remove(part);
|
217
|
+
},
|
218
|
+
|
219
|
+
save: function (form) {
|
220
|
+
alert("Could now transmit to server: " + ko.utils.stringifyJson(this.parts));
|
221
|
+
// To transmit to server, write this: ko.utils.postJson($("form")[0], this.parts);
|
222
|
+
}
|
223
|
+
};
|
224
|
+
|
225
|
+
ko.applyBindings(viewModel);
|
226
|
+
|
227
|
+
$("form").validate({ submitHandler: function () { viewModel.save() } });
|
228
|
+
/*]]>*/
|
229
|
+
</script>
|
230
|
+
</div>
|
231
|
+
|
232
|
+
|
233
|
+
|
234
|
+
<div class="clear"></div>
|
235
|
+
</div>
|
236
|
+
</div>
|
237
|
+
</div>
|
238
|
+
<div class="clear"></div>
|
239
|
+
<div class="main-menu">
|
240
|
+
</div>
|
241
|
+
<div class="clear"></div>
|
242
|
+
</div>
|
243
|
+
|
244
|
+
<div id="page-footer">stockr</div>
|
245
|
+
|
246
|
+
|
247
|
+
</body></html>
|
248
|
+
|
249
|
+
|
250
|
+
<br><br><br><br>
|
251
|
+
%h1 Stockr List
|
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
|
263
|
+
|
264
|
+
Total:
|
265
|
+
|
266
|
+
INDEX
|
267
|
+
end
|
268
|
+
|
269
|
+
get '/new' do
|
270
|
+
haml :new
|
271
|
+
end
|
272
|
+
|
273
|
+
get '/knockout.js' do
|
274
|
+
File.open(File.dirname(__FILE__) + "/assets/knockout.js")
|
275
|
+
end
|
276
|
+
|
277
|
+
get '/up' do
|
278
|
+
"..."
|
279
|
+
end
|
280
|
+
|
281
|
+
get '/:id' do
|
282
|
+
@part = Part.find(params[:id])
|
283
|
+
if (@part)
|
284
|
+
haml :show
|
285
|
+
else
|
286
|
+
redirect '/'
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
post '/' do
|
291
|
+
@part = Part.new(:distance => params[:distance], :minutes => params[:minutes])
|
292
|
+
if @part.save
|
293
|
+
redirect "/#{@part.id}"
|
294
|
+
else
|
295
|
+
redirect '/'
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
Sinatra::Application.run!
|
300
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'stockr'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
|
13
|
+
config.before(:each) do
|
14
|
+
Redis.new.flushall
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Part" do
|
4
|
+
include Stockr
|
5
|
+
|
6
|
+
it "should have name" do
|
7
|
+
Part.new("IRF640").name.should eql("IRF640")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have qty" do
|
11
|
+
Part.new("IRF640", 10).qty.should eql(10)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have price" do
|
15
|
+
Part.new("IRF640", 10, "0,30").price.should be_within(0.1).of(0.3)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
end
|
data/spec/stockr_spec.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Stockr" do
|
4
|
+
|
5
|
+
def run(x)
|
6
|
+
Stockr.work(x)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should add parts" do
|
10
|
+
run("2 LM358").should eql("Done. 2x LM358")
|
11
|
+
run("LM358").should eql("2x LM358")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should add parts" do
|
15
|
+
run("100 1K").should eql("Done. 100x 1K")
|
16
|
+
run("1K").should eql("100x 1K")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should increment existing parts" do
|
20
|
+
run("2 ATMEGA32").should eql("Done. 2x ATMEGA32")
|
21
|
+
run("2 ATMEGA32").should eql("Done. 4x ATMEGA32")
|
22
|
+
run("ATMEGA32").should eql("4x ATMEGA32")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should decrement existing parts" do
|
26
|
+
run("4 LM447").should eql("Done. 4x LM447")
|
27
|
+
run("-2 LM447").should eql("Done. 2x LM447")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should add missing parts" do
|
31
|
+
run("-2 LM447").should eql("Done. -2x LM447")
|
32
|
+
run("2 LM447").should eql("Done. 0x LM447")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should list missing parts" do
|
36
|
+
run("-2 LM447").should eql("Done. -2x LM447")
|
37
|
+
run("2 LM448").should eql("Done. 2x LM448")
|
38
|
+
run("shop").should eql("-2x LM447")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should add prices to parts" do
|
42
|
+
run("1 LM447 0.50").should eql("Done. 1x LM447..........................................$ 0.500")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should add prices to multiple parts (sum)" do
|
46
|
+
run("2 LM447 0.50").should eql("Done. 2x LM447..........................................$ 0.500 ($ 1.000)")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should upcase the part name" do
|
50
|
+
run("8 enc28j60").should eql("Done. 8x ENC28J60")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should be case insensitive" do
|
54
|
+
run("2 LM447").should eql("Done. 2x LM447")
|
55
|
+
run("lm447").should eql("2x LM447")
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should search pattern" do
|
59
|
+
run("4 LM447").should eql("Done. 4x LM447")
|
60
|
+
run("4 LM448").should eql("Done. 4x LM448")
|
61
|
+
|
62
|
+
run("LM4").should eql("4x LM447\n4x LM448")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not fault when not found" do
|
66
|
+
run("DUNNO").should eql("Not found... go shop!")
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should export txt file" do
|
70
|
+
Time.should_receive(:now).and_return(Time.at(1291879432))
|
71
|
+
run("4 LM448").should eql("Done. 4x LM448")
|
72
|
+
run("txt").should eql("File saved! my_stockr_10-12-09-05-23-52.txt")
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should export to html file" do
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stockr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Marcos Piccinini
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-09 00:00:00 -02:00
|
18
|
+
default_executable: stockr
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: redis
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
prerelease: false
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rspec
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 2
|
42
|
+
- 1
|
43
|
+
- 0
|
44
|
+
version: 2.1.0
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: bundler
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 1
|
57
|
+
- 0
|
58
|
+
- 0
|
59
|
+
version: 1.0.0
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: jeweler
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ~>
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 1
|
72
|
+
- 5
|
73
|
+
- 1
|
74
|
+
version: 1.5.1
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: *id004
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rcov
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *id005
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: redis
|
93
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
version: "0"
|
101
|
+
type: :runtime
|
102
|
+
prerelease: false
|
103
|
+
version_requirements: *id006
|
104
|
+
description: Help keep track of stuff (good for electronics)
|
105
|
+
email: x@nofxx.com
|
106
|
+
executables:
|
107
|
+
- stockr
|
108
|
+
extensions: []
|
109
|
+
|
110
|
+
extra_rdoc_files:
|
111
|
+
- LICENSE.txt
|
112
|
+
- README.rdoc
|
113
|
+
files:
|
114
|
+
- .document
|
115
|
+
- .rspec
|
116
|
+
- Gemfile
|
117
|
+
- Gemfile.lock
|
118
|
+
- LICENSE.txt
|
119
|
+
- README.rdoc
|
120
|
+
- Rakefile
|
121
|
+
- VERSION
|
122
|
+
- bin/stockr
|
123
|
+
- lib/stockr.rb
|
124
|
+
- lib/stockr/assets/knockout.js
|
125
|
+
- lib/stockr/export.rb
|
126
|
+
- lib/stockr/part.rb
|
127
|
+
- lib/stockr/store.rb
|
128
|
+
- lib/stockr/web.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
- spec/stockr/part_spec.rb
|
131
|
+
- spec/stockr_spec.rb
|
132
|
+
has_rdoc: true
|
133
|
+
homepage: http://github.com/nofxx/stockr
|
134
|
+
licenses:
|
135
|
+
- MIT
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
|
139
|
+
require_paths:
|
140
|
+
- lib
|
141
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
hash: 176274908678948305
|
147
|
+
segments:
|
148
|
+
- 0
|
149
|
+
version: "0"
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
none: false
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
segments:
|
156
|
+
- 0
|
157
|
+
version: "0"
|
158
|
+
requirements: []
|
159
|
+
|
160
|
+
rubyforge_project:
|
161
|
+
rubygems_version: 1.3.7
|
162
|
+
signing_key:
|
163
|
+
specification_version: 3
|
164
|
+
summary: Keep track of all your stuff
|
165
|
+
test_files:
|
166
|
+
- spec/spec_helper.rb
|
167
|
+
- spec/stockr/part_spec.rb
|
168
|
+
- spec/stockr_spec.rb
|