stalk 0.1.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
@@ -0,0 +1,3 @@
1
+ [submodule "vendor/stalker"]
2
+ path = vendor/stalker
3
+ url = git@github.com:moviepilot/stalker.git
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.4"
12
+ gem "rcov", ">= 0"
13
+ end
@@ -0,0 +1,20 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.6.4)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rake (0.9.2)
10
+ rcov (0.9.10)
11
+ shoulda (2.11.3)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ bundler (~> 1.0.0)
18
+ jeweler (~> 1.6.4)
19
+ rcov
20
+ shoulda
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Jannis Hermanns
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,19 @@
1
+ = stalker-gem
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to stalker-gem
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * 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.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 Jannis Hermanns. See LICENSE.txt for
18
+ further details.
19
+
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "stalk"
18
+ gem.homepage = "http://github.com/jayniz/stalk-gem"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Installs stalker.js for you and adds a convenient stalk command}
21
+ gem.description = %Q{Takes the javascript of stalker and provides the stalker command that uses node to run stalker.js}
22
+ gem.email = "jannis@gmail.com"
23
+ gem.authors = ["Jannis Hermanns"]
24
+ gem.executables = %w( stalk )
25
+ gem.files.include 'vendor/**/*.js'
26
+ # dependencies defined in Gemfile
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |test|
39
+ test.libs << 'test'
40
+ test.pattern = 'test/**/test_*.rb'
41
+ test.verbose = true
42
+ test.rcov_opts << '--exclude "gems/*"'
43
+ end
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "stalker-gem #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Help us die gracefully
4
+ def die(code = 0)
5
+ puts "\nTest your API using stalker.js.\n\nUsage:\n stalk my_endpoints.txt http://host1:80\n\n"
6
+ exit code
7
+ end
8
+
9
+ # Less than two args can't be good
10
+ if ARGV.length != 2
11
+ die
12
+ end
13
+
14
+ # We use node.js to execute things
15
+ node = `which node`.chomp
16
+ unless File.executable?(node)
17
+ puts "Error: Can't execute node, make sure it's in your path (I tried #{node})"
18
+ end
19
+
20
+ # First argument is the definition of the tests, the
21
+ # rest is hosts to test
22
+ tests = ARGV.first
23
+ api = ARGV.last
24
+
25
+ # We can't read test definition, we fail!
26
+ unless File.file?(tests) and File.readable?(tests)
27
+ puts "Error: Can't open test definition file #{tests}"
28
+ die
29
+ end
30
+
31
+ # Where is stalker?
32
+ stalker = File.expand_path File.join(__FILE__, '..', '..', 'vendor', 'stalker', 'stalk.js')
33
+
34
+ # Replace our process with node
35
+ cmd = "#{node} #{stalker} #{api} #{tests}"
36
+ puts "Running: #{cmd}"
37
+ exec cmd
File without changes
@@ -0,0 +1,72 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{stalker}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jannis Hermanns"]
12
+ s.date = %q{2011-10-10}
13
+ s.default_executable = %q{stalk}
14
+ s.description = %q{Takes the javascript of stalker and provides the stalker command that uses node to run stalker.js}
15
+ s.email = %q{jannis@gmail.com}
16
+ s.executables = ["stalk"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitmodules",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE.txt",
27
+ "README.rdoc",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "bin/stalk",
31
+ "lib/stalker-gem.rb",
32
+ "stalker.gemspec",
33
+ "test/helper.rb",
34
+ "test/test_stalker-gem.rb",
35
+ "vendor/stalker/lib/example_parser.js",
36
+ "vendor/stalker/lib/object_compare.js",
37
+ "vendor/stalker/lib/shout.js",
38
+ "vendor/stalker/lib/stalker.js",
39
+ "vendor/stalker/lib/var_parser.js",
40
+ "vendor/stalker/stalk.js",
41
+ "vendor/stalker/vendor/multipartform.js",
42
+ "vendor/stalker/vendor/restler.js",
43
+ "vendor/stalker/vendor/underscore-min.js"
44
+ ]
45
+ s.homepage = %q{http://github.com/jayniz/stalker-gem}
46
+ s.licenses = ["MIT"]
47
+ s.require_paths = ["lib"]
48
+ s.rubygems_version = %q{1.5.2}
49
+ s.summary = %q{Installs stalker for you and adds a convenient stalker command}
50
+
51
+ if s.respond_to? :specification_version then
52
+ s.specification_version = 3
53
+
54
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
56
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
57
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
58
+ s.add_development_dependency(%q<rcov>, [">= 0"])
59
+ else
60
+ s.add_dependency(%q<shoulda>, [">= 0"])
61
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
62
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
63
+ s.add_dependency(%q<rcov>, [">= 0"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<shoulda>, [">= 0"])
67
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
68
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
69
+ s.add_dependency(%q<rcov>, [">= 0"])
70
+ end
71
+ end
72
+
@@ -0,0 +1,18 @@
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 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'stalker-gem'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestStalkerGem < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,72 @@
1
+ var ExampleParser, VarParser, fs, _;
2
+ fs = require('fs');
3
+ _ = require('../vendor/underscore-min');
4
+ VarParser = require('./var_parser').VarParser;
5
+ ExampleParser = (function() {
6
+ function ExampleParser() {}
7
+ ExampleParser.prototype.parse = function(lines) {
8
+ var parsed;
9
+ this.lines = lines;
10
+ this.i = 0;
11
+ this.data = {
12
+ request: {},
13
+ response: {},
14
+ description: ""
15
+ };
16
+ this.current = null;
17
+ parsed = this.parseLine();
18
+ return parsed;
19
+ };
20
+ ExampleParser.prototype.parseLine = function() {
21
+ var match;
22
+ if (match = this.lines[this.i].match(/^##?[ ]*(.*)/)) {
23
+ this.data.description += match[1] + "\n";
24
+ } else if (match = this.lines[this.i].match(/^([A-Z]{3,6})([:]?[ ]+)(.*)$/)) {
25
+ this.parseMethod(match);
26
+ } else if (match = this.lines[this.i].match(/^Response:[ ]?(.*)$/)) {
27
+ this.switchMode(match);
28
+ } else {
29
+ if (this.current) {
30
+ this.parseVars();
31
+ }
32
+ }
33
+ return this.advance();
34
+ };
35
+ ExampleParser.prototype.parseMethod = function(match) {
36
+ this.data.method = match[1];
37
+ this.data.uri = match[3];
38
+ return this.current = "request";
39
+ };
40
+ ExampleParser.prototype.switchMode = function(match) {
41
+ this.current = "response";
42
+ return this.data["response_code"] = match[1];
43
+ };
44
+ ExampleParser.prototype.parseVars = function() {
45
+ var stop, vars;
46
+ stop = (this.current === "request" ? "Response" : null);
47
+ vars = VarParser.parse(this.lines, this.i, stop);
48
+ this.i = vars[0]++;
49
+ return this.data[this.current] = vars[1];
50
+ };
51
+ ExampleParser.prototype.advance = function() {
52
+ this.i++;
53
+ if (this.lines.length > this.i) {
54
+ return this.parseLine();
55
+ } else {
56
+ return this.result();
57
+ }
58
+ };
59
+ ExampleParser.prototype.result = function() {
60
+ return this.data;
61
+ };
62
+ return ExampleParser;
63
+ })();
64
+ ExampleParser.parseFile = function(fileName) {
65
+ var data, p;
66
+ data = fs.readFileSync(fileName, 'utf8');
67
+ p = new ExampleParser();
68
+ return _.map(data.split("\n##"), function(example) {
69
+ return p.parse(example.split("\n"));
70
+ });
71
+ };
72
+ exports.ExampleParser = ExampleParser;
@@ -0,0 +1,67 @@
1
+ var ObjectCompare, _;
2
+ _ = require('../vendor/underscore-min');
3
+ ObjectCompare = {
4
+ diff: function(needle, haystack) {
5
+ var diff, diffs;
6
+ diff = ObjectCompare.contained_in(needle, haystack);
7
+ diffs = {};
8
+ _.each(diff, function(d) {
9
+ var joined_key;
10
+ joined_key = _.union(d.path, [d.key]).join('][');
11
+ return diffs["[" + joined_key + "]"] = {
12
+ expected: d.expected,
13
+ actual: d.actual
14
+ };
15
+ });
16
+ return diffs;
17
+ },
18
+ contained_in: function(needle, haystack, history) {
19
+ var diffs;
20
+ if (history == null) {
21
+ history = [];
22
+ }
23
+ diffs = [];
24
+ _.each(needle, function(val, key) {
25
+ var expected, found, new_hist;
26
+ expected = haystack != null ? haystack[key] : void 0;
27
+ if (val === expected) {
28
+ return;
29
+ }
30
+ if (haystack instanceof Array && needle instanceof Array) {
31
+ found = false;
32
+ _.each(haystack, function(he) {
33
+ var arr_diff, h, v;
34
+ v = val instanceof Object ? val : {
35
+ test: val
36
+ };
37
+ h = he instanceof Object ? he : {
38
+ test: he
39
+ };
40
+ arr_diff = ObjectCompare.contained_in(v, h);
41
+ if (arr_diff.length === 0) {
42
+ return found = true;
43
+ }
44
+ });
45
+ if (found) {
46
+ return;
47
+ }
48
+ return diffs.push(ObjectCompare.diff_obj(history, key, val, expected));
49
+ } else if (val instanceof Object) {
50
+ new_hist = _.union(history, [key]);
51
+ return diffs.push(ObjectCompare.contained_in(val, expected, new_hist));
52
+ } else {
53
+ return diffs.push(ObjectCompare.diff_obj(history, key, val, expected));
54
+ }
55
+ });
56
+ return _.flatten(diffs);
57
+ },
58
+ diff_obj: function(history, key, expected, actual) {
59
+ return {
60
+ path: history,
61
+ key: key,
62
+ expected: expected,
63
+ actual: actual || null
64
+ };
65
+ }
66
+ };
67
+ exports.ObjectCompare = ObjectCompare;
@@ -0,0 +1,19 @@
1
+ var Shout, exec, sys;
2
+ sys = require('sys');
3
+ exec = require('child_process').exec;
4
+ Shout = {
5
+ green: function(name) {
6
+ return exec("shout -d '&nbsp;' --group stalker --expires-in 300 green '" + name + "' '" + name + "'");
7
+ },
8
+ red: function(name, msg) {
9
+ return exec("shout -d '&nbsp;' --group stalker red '" + name + "' '" + name + ": " + (JSON.stringify(msg)) + "'");
10
+ },
11
+ report: function(host, summary) {
12
+ if (summary.success) {
13
+ return Shout.green("" + host + "_" + summary.test);
14
+ } else {
15
+ return Shout.red("" + host + "_" + summary.test, summary.errors);
16
+ }
17
+ }
18
+ };
19
+ exports.Shout = Shout;
@@ -0,0 +1,109 @@
1
+ var ObjectCompare, Stalker, rest, _;
2
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
3
+ _ = require('../vendor/underscore-min');
4
+ rest = require('../vendor/restler');
5
+ ObjectCompare = require('./object_compare').ObjectCompare;
6
+ Stalker = (function() {
7
+ function Stalker(host, tests, cb) {
8
+ this.host = host;
9
+ this.tests = tests;
10
+ this.cb = cb;
11
+ this.current = 0;
12
+ this.errors = false;
13
+ }
14
+ Stalker.prototype.run = function() {
15
+ return this.next();
16
+ };
17
+ Stalker.prototype.next = function(summary) {
18
+ var t;
19
+ if (summary == null) {
20
+ summary = false;
21
+ }
22
+ if (summary) {
23
+ this.cb(summary);
24
+ }
25
+ if ((summary != null) && summary.success === false) {
26
+ this.errors = true;
27
+ }
28
+ if (!(t = this.tests[this.current])) {
29
+ return this.exit();
30
+ }
31
+ this.current++;
32
+ return this.probe(t);
33
+ };
34
+ Stalker.prototype.exit = function() {
35
+ var status;
36
+ status = this.errors ? 1 : 0;
37
+ return process.exit(status);
38
+ };
39
+ Stalker.prototype.probe = function(def, cb) {
40
+ var options, uri;
41
+ if (cb == null) {
42
+ cb = false;
43
+ }
44
+ uri = this.host + def.uri;
45
+ options = {
46
+ method: def.method,
47
+ data: JSON.stringify(def.request)
48
+ };
49
+ return rest.request(uri, options).on('complete', __bind(function(data, response) {
50
+ return this.check_response(data, response, def, cb);
51
+ }, this)).on('error', __bind(function(data, response) {
52
+ return this.check_response(data, response, def, cb);
53
+ }, this));
54
+ };
55
+ Stalker.prototype.check_response = function(data, response, def, cb) {
56
+ var summary;
57
+ summary = {
58
+ success: true,
59
+ test: def.uri,
60
+ errors: {}
61
+ };
62
+ this.check_status(summary, response, def);
63
+ this.check_object(summary, data, def);
64
+ if (cb) {
65
+ return cb(summary);
66
+ } else {
67
+ return this.next(summary);
68
+ }
69
+ };
70
+ Stalker.prototype.check_object = function(summary, data, def) {
71
+ var actual, diffs, expected;
72
+ if (def.response.body == null) {
73
+ return true;
74
+ }
75
+ actual = JSON.parse(def.response.body);
76
+ try {
77
+ expected = JSON.parse(data);
78
+ diffs = ObjectCompare.diff(actual, expected);
79
+ if (_.isEmpty(diffs)) {
80
+ return;
81
+ }
82
+ summary.success = false;
83
+ summary.errors.body = diffs;
84
+ return false;
85
+ } catch (e) {
86
+ summary.success = false;
87
+ summary.errors.body = {
88
+ "invalid_syntax": e
89
+ };
90
+ return false;
91
+ }
92
+ };
93
+ Stalker.prototype.check_status = function(summary, response, def) {
94
+ var actual, expected;
95
+ expected = parseInt(def.response_code);
96
+ actual = response.statusCode;
97
+ if (expected === actual) {
98
+ return true;
99
+ }
100
+ summary.errors.status = {
101
+ expected: expected,
102
+ actual: actual || null
103
+ };
104
+ summary.success = false;
105
+ return false;
106
+ };
107
+ return Stalker;
108
+ })();
109
+ exports.Stalker = Stalker;
@@ -0,0 +1,51 @@
1
+ var VarParser;
2
+ VarParser = {
3
+ parse: function(lines, start, stopword) {
4
+ var ret;
5
+ this.lines = lines;
6
+ this.current = null;
7
+ this.data = {};
8
+ this.i = start;
9
+ this.stop = stopword;
10
+ ret = this.parseLine();
11
+ return ret;
12
+ },
13
+ parseLine: function() {
14
+ var match;
15
+ if (this.stop && this.lines[this.i].substr(0, this.stop.length).toLowerCase() === this.stop.toLowerCase()) {
16
+ return [this.i - 1, this.data];
17
+ } else if (match = this.lines[this.i].match(/^([\w]+)\: *(.*)$/)) {
18
+ this.parseSectionHeader(match);
19
+ } else if (match = this.lines[this.i].match(/^ (.*)$/)) {
20
+ this.parseSample(match);
21
+ } else if (match = this.lines[this.i].match(/^[ ]+([\w-]+)\: *([^ ]?.*)$/)) {
22
+ this.parseValue(match);
23
+ } else {
24
+
25
+ }
26
+ return this.advance();
27
+ },
28
+ parseSectionHeader: function(match) {
29
+ return this.current = match[1].toLowerCase();
30
+ },
31
+ parseValue: function(match) {
32
+ this.data[this.current] = this.data[this.current] || {};
33
+ return this.data[this.current][match[1]] = match[2];
34
+ },
35
+ parseSample: function(match) {
36
+ this.data[this.current] = this.data[this.current] || "";
37
+ return this.data[this.current] += match[1] + "\n";
38
+ },
39
+ advance: function() {
40
+ this.i++;
41
+ if (this.lines.length > this.i) {
42
+ return this.parseLine();
43
+ } else {
44
+ return this.result();
45
+ }
46
+ },
47
+ result: function() {
48
+ return [this.i, this.data];
49
+ }
50
+ };
51
+ exports.VarParser = VarParser;
@@ -0,0 +1,32 @@
1
+ var ExampleParser, Shout, Stalker, filename, host, report, stalker, success, tests, verbose, _;
2
+ _ = require('./vendor/underscore-min');
3
+ Shout = require('./lib/shout').Shout;
4
+ Stalker = require('./lib/stalker').Stalker;
5
+ ExampleParser = require('./lib/example_parser').ExampleParser;
6
+ if (process.argv.length < 3) {
7
+ console.log("Usage: node stalk.js [-v] hostname definition.txt");
8
+ console.log(" e.g. node stalk.js -v http://production.host search_queries.txt");
9
+ process.exit(1);
10
+ }
11
+ filename = _.last(process.argv);
12
+ host = _.first(_.rest(process.argv, -2));
13
+ verbose = _.include(process.argv, "-v");
14
+ tests = ExampleParser.parseFile(filename);
15
+ success = true;
16
+ report = function(summary) {
17
+ var errors, result;
18
+ result = summary.success === true ? "✔" : "✗";
19
+ errors = summary.success === true ? "" : " errors " + JSON.stringify(summary.errors);
20
+ console.log("" + result + " " + summary.test + errors);
21
+ success = success && summary.success;
22
+ Shout.report(host, summary);
23
+ return true;
24
+ };
25
+ stalker = new Stalker(host, tests, report);
26
+ stalker.run();
27
+ if (verbose) {
28
+ _.map(tests, function(d) {
29
+ console.log(d);
30
+ return console.log("-----------------------------------------------\n");
31
+ });
32
+ }
@@ -0,0 +1,202 @@
1
+ var fs = require('fs');
2
+ var sys = require("sys")
3
+ exports.defaultBoundary = '48940923NODERESLTER3890457293';
4
+
5
+
6
+ // This little object allows us hijack the write method via duck-typing
7
+ // and write to strings or regular streams that support the write method.
8
+ function Stream(stream) {
9
+ //If the user pases a string for stream,we initalize one to write to
10
+ if (this._isString(stream)) {
11
+ this.string = "";
12
+ }
13
+ this.stream = stream;
14
+
15
+ }
16
+
17
+ Stream.prototype = {
18
+ //write to an internal String or to the Stream
19
+ write: function(data) {
20
+ if (this.string != undefined) {
21
+ this.string += data;
22
+ } else {
23
+ this.stream.write(data, "binary");
24
+ }
25
+ },
26
+
27
+ //stolen from underscore.js
28
+ _isString: function(obj) {
29
+ return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
30
+ }
31
+ }
32
+
33
+ function File(path, filename, fileSize, encoding, contentType) {
34
+ this.path = path;
35
+ this.filename = filename || this._basename(path);
36
+ this.fileSize = fileSize;
37
+ this.encoding = encoding || "binary";
38
+ this.contentType = contentType || 'application/octet-stream';
39
+ }
40
+
41
+ File.prototype = {
42
+ _basename: function(path) {
43
+ var parts = path.split(/\/|\\/);
44
+ return parts[parts.length - 1];
45
+ }
46
+ };
47
+
48
+ function Data(filename, contentType, data) {
49
+ this.filename = filename;
50
+ this.contentType = contentType || 'application/octet-stream';
51
+ this.data = data;
52
+ }
53
+
54
+ function Part(name, value, boundary) {
55
+ this.name = name;
56
+ this.value = value;
57
+ this.boundary = boundary;
58
+ }
59
+
60
+
61
+ Part.prototype = {
62
+
63
+ //returns the Content-Disposition header
64
+ header: function() {
65
+ var header;
66
+
67
+ if (this.value.data) {
68
+ header = "Content-Disposition: form-data; name=\"" + this.name +
69
+ "\"; filename=\"" + this.value.filename + "\"\r\n" +
70
+ "Content-Type: " + this.value.contentType;
71
+ } if (this.value instanceof File) {
72
+ header = "Content-Disposition: form-data; name=\"" + this.name +
73
+ "\"; filename=\"" + this.value.filename + "\"\r\n" +
74
+ "Content-Length: " + this.value.fileSize + "\r\n" +
75
+ "Content-Type: " + this.value.contentType;
76
+ } else {
77
+ header = "Content-Disposition: form-data; name=\"" + this.name + "\"";
78
+ }
79
+
80
+ return "--" + this.boundary + "\r\n" + header + "\r\n\r\n";
81
+ },
82
+
83
+ //calculates the size of the Part
84
+ sizeOf: function() {
85
+ var valueSize;
86
+ if (this.value instanceof File) {
87
+ valueSize = this.value.fileSize;
88
+ } else if (this.value.data) {
89
+ valueSize = this.value.data.length;
90
+ } else {
91
+ valueSize = this.value.length;
92
+ }
93
+ return valueSize + this.header().length + 2;
94
+ },
95
+
96
+ // Writes the Part out to a writable stream that supports the write(data) method
97
+ // You can also pass in a String and a String will be returned to the callback
98
+ // with the whole Part
99
+ // Calls the callback when complete
100
+ write: function(stream, callback) {
101
+
102
+ var self = this;
103
+
104
+ //first write the Content-Disposition
105
+ stream.write(this.header());
106
+
107
+ //Now write out the body of the Part
108
+ if (this.value instanceof File) {
109
+ fs.open(this.value.path, "r", 0666, function (err, fd) {
110
+ if (err) throw err;
111
+
112
+ position = 0;
113
+
114
+ (function reader () {
115
+ fs.read(fd, 1024 * 4, position, "binary", function (er, chunk) {
116
+ if (er) callback(err);
117
+ stream.write(chunk);
118
+ position += 1024 * 4;
119
+ if (chunk) reader();
120
+ else {
121
+ stream.write("\r\n")
122
+ callback();
123
+ fs.close(fd);
124
+ }
125
+ });
126
+ })(); // reader()
127
+ });
128
+ } else {
129
+ stream.write(this.value + "\r\n");
130
+ callback();
131
+ }
132
+ }
133
+ }
134
+
135
+ //Renamed to MultiPartRequest from Request
136
+ function MultiPartRequest(data, boundary) {
137
+ this.encoding = 'binary';
138
+ this.boundary = boundary || exports.defaultBoundary;
139
+ this.data = data;
140
+ this.partNames = this._partNames();
141
+ }
142
+
143
+ MultiPartRequest.prototype = {
144
+ _partNames: function() {
145
+ partNames = []
146
+ for (var name in this.data) {
147
+ partNames.push(name)
148
+ }
149
+ return partNames;
150
+ },
151
+ write: function(stream, callback) {
152
+ var partCount = 0, self = this;
153
+
154
+ // wrap the stream in our own Stream object
155
+ // See the Stream function above for the benefits of this
156
+ var stream = new Stream(stream);
157
+
158
+ // Let each part write itself out to the stream
159
+ (function writePart() {
160
+ partName = this.partNames[partCount];
161
+ part = new Part(partName, self.data[partName], self.boundary);
162
+ part.write(stream, function (err) {
163
+ if (err) {
164
+ callback(err);
165
+ return;
166
+ }
167
+ partCount += 1;
168
+ if (partCount < self.partNames.length)
169
+ writePart();
170
+ else {
171
+ stream.write('--' + self.boundary + '--' + "\r\n");
172
+
173
+ if (callback) callback(stream.string || "");
174
+ }
175
+ });
176
+ })();
177
+ }
178
+ }
179
+
180
+ var exportMethods = {
181
+ file: function(path, filename, fileSize, encoding, contentType) {
182
+ return new File(path, filename, fileSize, encoding, contentType)
183
+ },
184
+ data: function(filename, contentType, data) {
185
+ return new Data(filename, contentType, data);
186
+ },
187
+ sizeOf: function(parts, boundary) {
188
+ var totalSize = 0;
189
+ boundary = boundary || exports.defaultBoundary;
190
+ for (var name in parts) totalSize += new Part(name, parts[name], boundary).sizeOf();
191
+ return totalSize + boundary.length + 6;
192
+ },
193
+ write: function(stream, data, callback, boundary) {
194
+ var r = new MultiPartRequest(data, boundary);
195
+ r.write(stream, callback);
196
+ return r;
197
+ }
198
+ }
199
+
200
+ Object.keys(exportMethods).forEach(function(exportMethod) {
201
+ exports[exportMethod] = exportMethods[exportMethod]
202
+ })
@@ -0,0 +1,300 @@
1
+ var sys = require('sys'),
2
+ http = require('http'),
3
+ https = require('https'),
4
+ url = require('url'),
5
+ qs = require('querystring'),
6
+ multipart = require('./multipartform');
7
+
8
+ function mixin(target, source) {
9
+ Object.keys(source).forEach(function(key) {
10
+ target[key] = source[key];
11
+ });
12
+
13
+ return target;
14
+ }
15
+
16
+ function Request(uri, options) {
17
+ this.url = url.parse(uri);
18
+ this.options = options;
19
+ this.headers = {
20
+ 'Accept': '*/*',
21
+ 'User-Agent': 'Restler for node.js',
22
+ 'Host': this.url.host
23
+ }
24
+
25
+ mixin(this.headers, options.headers || {});
26
+
27
+ // set port and method defaults
28
+ if (!this.url.port) this.url.port = (this.url.protocol == 'https:') ? '443' : '80';
29
+ if (!this.options.method) this.options.method = (this.options.data) ? 'POST' : 'GET';
30
+ if (typeof this.options.followRedirects == 'undefined') this.options.followRedirects = true;
31
+
32
+ // stringify query given in options of not given in URL
33
+ if (this.options.query && !this.url.query) {
34
+ if (typeof this.options.query == 'object')
35
+ this.url.query = qs.stringify(this.options.query);
36
+ else this.url.query = this.options.query;
37
+ }
38
+
39
+ this._applyBasicAuth();
40
+
41
+ if (this.options.multipart) {
42
+ this.headers['Content-Type'] = 'multipart/form-data; boundary=' + multipart.defaultBoundary;
43
+ } else {
44
+ if (typeof this.options.data == 'object') {
45
+ this.options.data = qs.stringify(this.options.data);
46
+ this.headers['Content-Type'] = 'application/x-www-form-urlencoded';
47
+ this.headers['Content-Length'] = this.options.data.length;
48
+ }
49
+ }
50
+
51
+ var proto = (this.url.protocol == 'https:') ? https : http;
52
+
53
+ this.request = proto.request({
54
+ host: this.url.hostname,
55
+ port: this.url.port,
56
+ path: this._fullPath(),
57
+ method: this.options.method,
58
+ headers: this.headers
59
+ });
60
+
61
+ this._makeRequest();
62
+ }
63
+
64
+ Request.prototype = new process.EventEmitter();
65
+ mixin(Request.prototype, {
66
+ _isRedirect: function(response) {
67
+ return ([301, 302].indexOf(response.statusCode) >= 0);
68
+ },
69
+ _fullPath: function() {
70
+ var path = this.url.pathname || '/';
71
+ if (this.url.hash) path += this.url.hash;
72
+ if (this.url.query) path += '?' + this.url.query;
73
+ return path;
74
+ },
75
+ _applyBasicAuth: function() {
76
+ var authParts;
77
+
78
+ if (this.url.auth) {
79
+ authParts = this.url.auth.split(':');
80
+ this.options.username = authParts[0];
81
+ this.options.password = authParts[1];
82
+ }
83
+
84
+ if (this.options.username && this.options.password) {
85
+ var b = new Buffer([this.options.username, this.options.password].join(':'));
86
+ this.headers['Authorization'] = "Basic " + b.toString('base64');
87
+ }
88
+ },
89
+ _responseHandler: function(response) {
90
+ var self = this;
91
+
92
+ if (this._isRedirect(response) && this.options.followRedirects == true) {
93
+ try {
94
+ var location = url.resolve(this.url, response.headers['location']);
95
+ this.options.originalRequest = this;
96
+
97
+ request(location, this.options);
98
+ } catch(e) {
99
+ self._respond('error', '', 'Failed to follow redirect');
100
+ }
101
+ } else {
102
+ var body = '';
103
+
104
+ // TODO Handle different encodings
105
+ response.setEncoding('utf8');
106
+
107
+ response.on('data', function(chunk) {
108
+ body += chunk;
109
+ });
110
+
111
+ response.on('end', function() {
112
+ if (self.options.parser) {
113
+ self.options.parser.call(response, body, function(parsedData) {
114
+
115
+ self._fireEvents(parsedData, response);
116
+ });
117
+ } else {
118
+ self._fireEvents(body, response);
119
+ }
120
+ });
121
+ }
122
+ },
123
+ _respond: function(type, data, response) {
124
+ if (this.options.originalRequest) {
125
+ this.options.originalRequest.emit(type, data, response);
126
+ } else {
127
+ this.emit(type, data, response);
128
+ }
129
+ },
130
+ _fireEvents: function(body, response) {
131
+ if (parseInt(response.statusCode) >= 400) this._respond('error', body, response);
132
+ else this._respond('success', body, response);
133
+
134
+ this._respond(response.statusCode.toString().replace(/\d{2}$/, 'XX'), body, response);
135
+ this._respond(response.statusCode.toString(), body, response);
136
+ this._respond('complete', body, response);
137
+ },
138
+ _makeRequest: function() {
139
+ var self = this;
140
+
141
+ this.request.on('response', function(response) {
142
+ self._responseHandler(response);
143
+ }).on('error', function(err) {
144
+ self._respond('error', null, err);
145
+ });
146
+ },
147
+ run: function() {
148
+ var self = this;
149
+
150
+ if (this.options.multipart) {
151
+ multipart.write(this.request, this.options.data, function() {
152
+ self.request.end();
153
+ });
154
+ } else {
155
+ if (this.options.data) {
156
+ this.request.write(this.options.data.toString(), this.options.encoding || 'utf8');
157
+ }
158
+ this.request.end();
159
+ }
160
+
161
+ return this;
162
+ }
163
+ });
164
+
165
+ function shortcutOptions(options, method) {
166
+ options = options || {};
167
+ options.method = method;
168
+ options.parser = (typeof options.parser !== "undefined") ? options.parser : parsers.auto;
169
+ return options;
170
+ }
171
+
172
+ function request(url, options) {
173
+ return (new Request(url, options)).run();
174
+ }
175
+
176
+ function get(url, options) {
177
+ return request(url, shortcutOptions(options, 'GET'));
178
+ }
179
+
180
+ function post(url, options) {
181
+ return request(url, shortcutOptions(options, 'POST'));
182
+ }
183
+
184
+ function put(url, options) {
185
+ return request(url, shortcutOptions(options, 'PUT'));
186
+ }
187
+
188
+ function del(url, options) {
189
+ return request(url, shortcutOptions(options, 'DELETE'));
190
+ }
191
+
192
+ var parsers = {
193
+ auto: function(data, callback) {
194
+ var contentType = this.headers['content-type'];
195
+
196
+ if (contentType) {
197
+ for (var matcher in parsers.auto.matchers) {
198
+
199
+ if (contentType.indexOf(matcher) == 0) {
200
+ return parsers.auto.matchers[matcher].call(this, data, callback);
201
+ }
202
+ }
203
+ }
204
+
205
+ callback(data);
206
+ },
207
+ json: function(data, callback) {
208
+ callback(data && JSON.parse(data));
209
+ }
210
+ }
211
+
212
+ parsers.auto.matchers = {
213
+ 'application/json': parsers.json
214
+ };
215
+
216
+ try {
217
+ var yaml = require('yaml');
218
+
219
+ parsers.yaml = function(data, callback) {
220
+ return callback(data && yaml.eval(data));
221
+ };
222
+
223
+ parsers.auto.matchers['application/yaml'] = parsers.yaml;
224
+ } catch(e) {}
225
+
226
+ try {
227
+ var xml2js = require('xml2js');
228
+
229
+ parsers.xml = function(data, callback) {
230
+ if (data) {
231
+ var parser = new xml2js.Parser();
232
+
233
+ parser.on('end', function(result) {
234
+ callback(result);
235
+ });
236
+
237
+ parser.parseString(data);
238
+ } else {
239
+ callback();
240
+ }
241
+ };
242
+
243
+ parsers.auto.matchers['application/xml'] = parsers.xml;
244
+ } catch(e) {}
245
+
246
+
247
+ function Service(defaults) {
248
+ if (defaults.baseURL) {
249
+ this.baseURL = defaults.baseURL;
250
+ delete defaults.baseURL;
251
+ }
252
+
253
+ this.defaults = defaults;
254
+ }
255
+
256
+ mixin(Service.prototype, {
257
+ request: function(path, options) {
258
+ return request(this._url(path), this._withDefaults(options));
259
+ },
260
+ get: function(path, options) {
261
+ return get(this._url(path), this._withDefaults(options));
262
+ },
263
+ put: function(path, options) {
264
+ return put(this._url(path), this._withDefaults(options));
265
+ },
266
+ post: function(path, options) {
267
+ return post(this._url(path), this._withDefaults(options));
268
+ },
269
+ del: function(path, options) {
270
+ return del(this._url(path), this._withDefaults(options));
271
+ },
272
+ _url: function(path) {
273
+ if (this.baseURL) return url.resolve(this.baseURL, path);
274
+ else return path;
275
+ },
276
+ _withDefaults: function(options) {
277
+ var o = mixin({}, this.defaults);
278
+ return mixin(o, options);
279
+ }
280
+ });
281
+
282
+ function service(constructor, defaults, methods) {
283
+ constructor.prototype = new Service(defaults || {});
284
+ mixin(constructor.prototype, methods);
285
+ return constructor;
286
+ }
287
+
288
+ mixin(exports, {
289
+ Request: Request,
290
+ Service: Service,
291
+ request: request,
292
+ service: service,
293
+ get: get,
294
+ post: post,
295
+ put: put,
296
+ del: del,
297
+ parsers: parsers,
298
+ file: multipart.file,
299
+ data: multipart.data
300
+ });
@@ -0,0 +1,27 @@
1
+ // Underscore.js 1.1.7
2
+ // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
3
+ // Underscore is freely distributable under the MIT license.
4
+ // Portions of Underscore are inspired or borrowed from Prototype,
5
+ // Oliver Steele's Functional, and John Resig's Micro-Templating.
6
+ // For all details and documentation:
7
+ // http://documentcloud.github.com/underscore
8
+ (function(){var p=this,C=p._,m={},i=Array.prototype,n=Object.prototype,f=i.slice,D=i.unshift,E=n.toString,l=n.hasOwnProperty,s=i.forEach,t=i.map,u=i.reduce,v=i.reduceRight,w=i.filter,x=i.every,y=i.some,o=i.indexOf,z=i.lastIndexOf;n=Array.isArray;var F=Object.keys,q=Function.prototype.bind,b=function(a){return new j(a)};typeof module!=="undefined"&&module.exports?(module.exports=b,b._=b):p._=b;b.VERSION="1.1.7";var h=b.each=b.forEach=function(a,c,b){if(a!=null)if(s&&a.forEach===s)a.forEach(c,b);else if(a.length===
9
+ +a.length)for(var e=0,k=a.length;e<k;e++){if(e in a&&c.call(b,a[e],e,a)===m)break}else for(e in a)if(l.call(a,e)&&c.call(b,a[e],e,a)===m)break};b.map=function(a,c,b){var e=[];if(a==null)return e;if(t&&a.map===t)return a.map(c,b);h(a,function(a,g,G){e[e.length]=c.call(b,a,g,G)});return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var k=d!==void 0;a==null&&(a=[]);if(u&&a.reduce===u)return e&&(c=b.bind(c,e)),k?a.reduce(c,d):a.reduce(c);h(a,function(a,b,f){k?d=c.call(e,d,a,b,f):(d=a,k=!0)});if(!k)throw new TypeError("Reduce of empty array with no initial value");
10
+ return d};b.reduceRight=b.foldr=function(a,c,d,e){a==null&&(a=[]);if(v&&a.reduceRight===v)return e&&(c=b.bind(c,e)),d!==void 0?a.reduceRight(c,d):a.reduceRight(c);a=(b.isArray(a)?a.slice():b.toArray(a)).reverse();return b.reduce(a,c,d,e)};b.find=b.detect=function(a,c,b){var e;A(a,function(a,g,f){if(c.call(b,a,g,f))return e=a,!0});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(w&&a.filter===w)return a.filter(c,b);h(a,function(a,g,f){c.call(b,a,g,f)&&(e[e.length]=a)});return e};
11
+ b.reject=function(a,c,b){var e=[];if(a==null)return e;h(a,function(a,g,f){c.call(b,a,g,f)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=!0;if(a==null)return e;if(x&&a.every===x)return a.every(c,b);h(a,function(a,g,f){if(!(e=e&&c.call(b,a,g,f)))return m});return e};var A=b.some=b.any=function(a,c,d){c=c||b.identity;var e=!1;if(a==null)return e;if(y&&a.some===y)return a.some(c,d);h(a,function(a,b,f){if(e|=c.call(d,a,b,f))return m});return!!e};b.include=b.contains=function(a,c){var b=
12
+ !1;if(a==null)return b;if(o&&a.indexOf===o)return a.indexOf(c)!=-1;A(a,function(a){if(b=a===c)return!0});return b};b.invoke=function(a,c){var d=f.call(arguments,2);return b.map(a,function(a){return(c.call?c||a:a[c]).apply(a,d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};h(a,function(a,b,f){b=c?c.call(d,a,b,f):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,
13
+ c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};h(a,function(a,b,f){b=c?c.call(d,a,b,f):a;b<e.computed&&(e={value:a,computed:b})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,f){return{value:a,criteria:c.call(d,a,b,f)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,b){var d={};h(a,function(a,f){var g=b(a,f);(d[g]||(d[g]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||
14
+ (d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return f.call(a);if(b.isArguments(a))return f.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?f.call(a,0,b):a[0]};b.rest=b.tail=function(a,b,d){return f.call(a,b==null||d?1:b)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.filter(a,
15
+ function(a){return!!a})};b.flatten=function(a){return b.reduce(a,function(a,d){if(b.isArray(d))return a.concat(b.flatten(d));a[a.length]=d;return a},[])};b.without=function(a){return b.difference(a,f.call(arguments,1))};b.uniq=b.unique=function(a,c){return b.reduce(a,function(a,e,f){if(0==f||(c===!0?b.last(a)!=e:!b.include(a,e)))a[a.length]=e;return a},[])};b.union=function(){return b.uniq(b.flatten(arguments))};b.intersection=b.intersect=function(a){var c=f.call(arguments,1);return b.filter(b.uniq(a),
16
+ function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a,c){return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=f.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(o&&a.indexOf===o)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,
17
+ b){if(a==null)return-1;if(z&&a.lastIndexOf===z)return a.lastIndexOf(b);for(var d=a.length;d--;)if(a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);d=arguments[2]||1;for(var e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};b.bind=function(a,b){if(a.bind===q&&q)return q.apply(a,f.call(arguments,1));var d=f.call(arguments,2);return function(){return a.apply(b,d.concat(f.call(arguments)))}};b.bindAll=function(a){var c=f.call(arguments,1);
18
+ c.length==0&&(c=b.functions(a));h(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var b=c.apply(this,arguments);return l.call(d,b)?d[b]:d[b]=a.apply(this,arguments)}};b.delay=function(a,b){var d=f.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(f.call(arguments,1)))};var B=function(a,b,d){var e;return function(){var f=this,g=arguments,h=function(){e=null;
19
+ a.apply(f,g)};d&&clearTimeout(e);if(d||!e)e=setTimeout(h,b)}};b.throttle=function(a,b){return B(a,b,!1)};b.debounce=function(a,b){return B(a,b,!0)};b.once=function(a){var b=!1,d;return function(){if(b)return d;b=!0;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(f.call(arguments));return b.apply(this,d)}};b.compose=function(){var a=f.call(arguments);return function(){for(var b=f.call(arguments),d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=
20
+ function(a,b){return function(){if(--a<1)return b.apply(this,arguments)}};b.keys=F||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)l.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){h(f.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){h(f.call(arguments,
21
+ 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,c){if(a===c)return!0;var d=typeof a;if(d!=typeof c)return!1;if(a==c)return!0;if(!a&&c||a&&!c)return!1;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual)return a.isEqual(c);if(c.isEqual)return c.isEqual(a);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return!1;
22
+ if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return!1;if(a.length&&a.length!==c.length)return!1;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return!1;for(var f in a)if(!(f in c)||!b.isEqual(a[f],c[f]))return!1;return!0};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(l.call(a,c))return!1;return!0};b.isElement=function(a){return!!(a&&a.nodeType==
23
+ 1)};b.isArray=n||function(a){return E.call(a)==="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return!(!a||!l.call(a,"callee"))};b.isFunction=function(a){return!(!a||!a.constructor||!a.call||!a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===!0||a===!1};b.isDate=function(a){return!(!a||!a.getTimezoneOffset||
24
+ !a.setUTCFullYear)};b.isRegExp=function(a){return!(!a||!a.test||!a.exec||!(a.ignoreCase||a.ignoreCase===!1))};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){p._=C;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.mixin=function(a){h(b.functions(a),function(c){H(c,b[c]=a[c])})};var I=0;b.uniqueId=function(a){var b=I++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g};
25
+ b.template=function(a,c){var d=b.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return c?d(c):d};
26
+ var j=function(a){this._wrapped=a};b.prototype=j.prototype;var r=function(a,c){return c?b(a).chain():a},H=function(a,c){j.prototype[a]=function(){var a=f.call(arguments);D.call(a,this._wrapped);return r(c.apply(b,a),this._chain)}};b.mixin(b);h(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=i[a];j.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});h(["concat","join","slice"],function(a){var b=i[a];j.prototype[a]=function(){return r(b.apply(this._wrapped,
27
+ arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return this};j.prototype.value=function(){return this._wrapped}})();
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stalk
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.1
6
+ platform: ruby
7
+ authors:
8
+ - Jannis Hermanns
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-10-10 00:00:00 +02:00
14
+ default_executable: stalk
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: shoulda
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 1.0.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: jeweler
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.6.4
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rcov
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ description: Takes the javascript of stalker and provides the stalker command that uses node to run stalker.js
61
+ email: jannis@gmail.com
62
+ executables:
63
+ - stalk
64
+ extensions: []
65
+
66
+ extra_rdoc_files:
67
+ - LICENSE.txt
68
+ - README.rdoc
69
+ files:
70
+ - .document
71
+ - .gitmodules
72
+ - Gemfile
73
+ - Gemfile.lock
74
+ - LICENSE.txt
75
+ - README.rdoc
76
+ - Rakefile
77
+ - VERSION
78
+ - bin/stalk
79
+ - lib/stalker-gem.rb
80
+ - stalker.gemspec
81
+ - test/helper.rb
82
+ - test/test_stalker-gem.rb
83
+ - vendor/stalker/lib/example_parser.js
84
+ - vendor/stalker/lib/object_compare.js
85
+ - vendor/stalker/lib/shout.js
86
+ - vendor/stalker/lib/stalker.js
87
+ - vendor/stalker/lib/var_parser.js
88
+ - vendor/stalker/stalk.js
89
+ - vendor/stalker/vendor/multipartform.js
90
+ - vendor/stalker/vendor/restler.js
91
+ - vendor/stalker/vendor/underscore-min.js
92
+ has_rdoc: true
93
+ homepage: http://github.com/jayniz/stalk-gem
94
+ licenses:
95
+ - MIT
96
+ post_install_message:
97
+ rdoc_options: []
98
+
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 776588002380951086
107
+ segments:
108
+ - 0
109
+ version: "0"
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: "0"
116
+ requirements: []
117
+
118
+ rubyforge_project:
119
+ rubygems_version: 1.5.2
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: Installs stalker.js for you and adds a convenient stalk command
123
+ test_files: []
124
+