stockr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
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
@@ -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)
@@ -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.
@@ -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
+
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 &gt; 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: &quot;partRowTemplate&quot;, foreach: parts }">
188
+ </tbody></table>
189
+
190
+ <button data-bind="click: addPart">Add Part</button>
191
+ <button data-bind="enable: parts().length &gt; 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
+
@@ -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
@@ -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