omghax-einstein 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ desc 'Release the website and new gem version'
2
+ task :deploy => [:check_version, :website, :release] do
3
+ puts "Remember to create SVN tag:"
4
+ puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
5
+ "svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
6
+ puts "Suggested comment:"
7
+ puts "Tagging release #{CHANGES}"
8
+ end
9
+
10
+ desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
11
+ task :local_deploy => [:website_generate, :install_gem]
12
+
13
+ task :check_version do
14
+ unless ENV['VERSION']
15
+ puts 'Must pass a VERSION=x.y.z release version'
16
+ exit
17
+ end
18
+ unless ENV['VERSION'] == VERS
19
+ puts "Please update your version.rb to match the release version, currently #{VERS}"
20
+ exit
21
+ end
22
+ end
23
+
24
+ desc 'Install the package as a gem, without generating documentation(ri/rdoc)'
25
+ task :install_gem_no_doc => [:clean, :package] do
26
+ sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri"
27
+ end
28
+
29
+ namespace :manifest do
30
+ desc 'Recreate Manifest.txt to include ALL files'
31
+ task :refresh do
32
+ `rake check_manifest | patch -p0 > Manifest.txt`
33
+ end
34
+ end
@@ -0,0 +1,7 @@
1
+ task :ruby_env do
2
+ RUBY_APP = if RUBY_PLATFORM =~ /java/
3
+ "jruby"
4
+ else
5
+ "ruby"
6
+ end unless defined? RUBY_APP
7
+ end
@@ -0,0 +1,17 @@
1
+ desc 'Generate website files'
2
+ task :website_generate => :ruby_env do
3
+ (Dir['website/**/*.txt'] - Dir['website/version*.txt']).each do |txt|
4
+ sh %{ #{RUBY_APP} script/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
5
+ end
6
+ end
7
+
8
+ desc 'Upload website files to rubyforge'
9
+ task :website_upload do
10
+ host = "#{rubyforge_username}@rubyforge.org"
11
+ remote_dir = "/var/www/gforge-projects/#{PATH}/"
12
+ local_dir = 'website'
13
+ sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
14
+ end
15
+
16
+ desc 'Generate and upload website files'
17
+ task :website => [:website_generate, :website_upload, :publish_docs]
@@ -0,0 +1,119 @@
1
+ require File.dirname(__FILE__) + "/test_helper"
2
+ require "logger"
3
+
4
+ class TestEvaluate < Test::Unit::TestCase
5
+ def setup
6
+ @parser = Einstein::Parser.new
7
+ @parser.logger = Logger.new(STDERR)
8
+ end
9
+
10
+ def test_parentheses
11
+ assert_equal((5 + 2) * (10 - 5), parse("(5 + 2) * (10 - 5)").evaluate)
12
+ end
13
+
14
+ def test_order_of_operations
15
+ assert_equal(3 + 4 * 2, parse("3 + 4 * 2").evaluate)
16
+ assert_equal(7 + 9 * 2 - 16 / 8, parse("7 + 9 * 2 - 16 / 8").evaluate)
17
+ end
18
+
19
+ def test_resolve_should_raise_on_undefined_variable
20
+ assert_raises(Einstein::ResolveError) { parse("x").evaluate }
21
+ end
22
+
23
+ def test_resolve
24
+ assert_equal(5, parse("x").evaluate(:x => 5))
25
+ assert_equal(5 + 5, parse("x + 5").evaluate(:x => 5))
26
+ assert_equal(4 * 4, parse("x * 4").evaluate(:x => 4))
27
+ end
28
+
29
+ def test_bitwise_or
30
+ assert_equal(0b1100 | 0b1111, parse("0b1100 | 0b1111").evaluate)
31
+ end
32
+
33
+ def test_bitwise_xor
34
+ assert_equal(0b1100 ^ 0b1111, parse("0b1100 ^ 0b1111").evaluate)
35
+ end
36
+
37
+ def test_bitwise_and
38
+ assert_equal(0b1100 & 0b1111, parse("0b1100 & 0b1111").evaluate)
39
+ end
40
+
41
+ def test_subtraction
42
+ assert_equal(10 - 5, parse("10 - 5").evaluate)
43
+ assert_equal(5 - 10, parse("5 - 10").evaluate)
44
+ end
45
+
46
+ def test_addition
47
+ assert_equal(1 + 2, parse("1 + 2").evaluate)
48
+ assert_equal(1.0 + 2.0, parse("1.0 + 2.0").evaluate)
49
+ end
50
+
51
+ def test_modulus
52
+ assert_equal(5 % 10, parse("5 % 10").evaluate)
53
+ assert_equal(10 % 5, parse("10 % 5").evaluate)
54
+ end
55
+
56
+ def test_division_should_raise_on_divide_by_zero
57
+ assert_raises(Einstein::ZeroDivisionError) { parse("1 / 0").evaluate }
58
+ end
59
+
60
+ def test_division
61
+ assert_equal(10 / 5, parse("10 / 5").evaluate)
62
+ assert_equal(10.0 / 5.0, parse("10.0 / 5.0").evaluate)
63
+ end
64
+
65
+ def test_multiplication
66
+ assert_equal(5 * 10, parse("5 * 10").evaluate)
67
+ assert_equal(5.0 * 10.0, parse("5.0 * 10.0").evaluate)
68
+ end
69
+
70
+ def test_exponent
71
+ assert_equal(5 ** 2, parse("5 ** 2").evaluate)
72
+ end
73
+
74
+ def test_float
75
+ assert_equal(1.1, parse("1.1").evaluate)
76
+ end
77
+
78
+ def test_number_base2
79
+ assert_equal(0b0, parse("0b0").evaluate)
80
+ assert_equal(0b1111, parse("0b1111").evaluate)
81
+ assert_equal(0B1010, parse("0B1010").evaluate)
82
+
83
+ assert_equal(+0b1111, parse("+0b1111").evaluate)
84
+ assert_equal(+0B1010, parse("+0B1010").evaluate)
85
+
86
+ assert_equal(-0b1111, parse("-0b1111").evaluate)
87
+ assert_equal(-0B1010, parse("-0B1010").evaluate)
88
+ end
89
+
90
+ def test_number_base16
91
+ assert_equal(0x0, parse("0x0").evaluate)
92
+ assert_equal(0xff, parse("0xff").evaluate)
93
+ assert_equal(0XFF, parse("0XFF").evaluate)
94
+
95
+ assert_equal(+0xff, parse("+0xff").evaluate)
96
+ assert_equal(+0XFF, parse("+0XFF").evaluate)
97
+
98
+ assert_equal(-0xff, parse("-0xff").evaluate)
99
+ assert_equal(-0XFF, parse("-0XFF").evaluate)
100
+ end
101
+
102
+ def test_number_base10
103
+ assert_equal(0, parse("0").evaluate)
104
+ assert_equal(1, parse("01").evaluate)
105
+ assert_equal(10, parse("10").evaluate)
106
+
107
+ assert_equal(+1, parse("+1").evaluate)
108
+ assert_equal(+10, parse("+10").evaluate)
109
+
110
+ assert_equal(-1, parse("-1").evaluate)
111
+ assert_equal(-10, parse("-10").evaluate)
112
+ end
113
+
114
+ private
115
+
116
+ def parse(stmt)
117
+ @parser.parse(stmt)
118
+ end
119
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/einstein'
@@ -0,0 +1,122 @@
1
+ require File.dirname(__FILE__) + "/test_helper"
2
+ require "logger"
3
+
4
+ class TestParser < Test::Unit::TestCase
5
+ def setup
6
+ @parser = Einstein::Parser.new
7
+ @parser.logger = Logger.new(STDERR)
8
+ end
9
+
10
+ def test_parentheses
11
+ assert_equal \
12
+ [:multiply,
13
+ [:add, [:lit, 5], [:lit, 2]],
14
+ [:subtract, [:lit, 10], [:lit, 5]]
15
+ ],
16
+ parse("(5 + 2) * (10 - 5)").to_sexp
17
+ end
18
+
19
+ def test_order_of_operations
20
+ assert_equal \
21
+ [:subtract,
22
+ [:add,
23
+ [:divide, [:lit, 6], [:lit, 3]],
24
+ [:multiply, [:lit, 4], [:lit, 2]]],
25
+ [:lit, 5]
26
+ ],
27
+ parse("6 / 3 + 4 * 2 - 5").to_sexp
28
+ end
29
+
30
+ def test_resolve
31
+ assert_equal [:resolve, "x"], parse("x").to_sexp
32
+ end
33
+
34
+ def test_bitwise_or
35
+ assert_equal [:bitwise_or, [:lit, 5], [:lit, 10]], parse("5 | 10").to_sexp
36
+ end
37
+
38
+ def test_bitwise_xor
39
+ assert_equal [:bitwise_xor, [:lit, 5], [:lit, 10]], parse("5 ^ 10").to_sexp
40
+ end
41
+
42
+ def test_bitwise_and
43
+ assert_equal [:bitwise_and, [:lit, 5], [:lit, 10]], parse("5 & 10").to_sexp
44
+ end
45
+
46
+ def test_rshift
47
+ assert_equal [:rshift, [:lit, 2], [:lit, 3]], parse("2 >> 3").to_sexp
48
+ end
49
+
50
+ def test_lshift
51
+ assert_equal [:lshift, [:lit, 2], [:lit, 3]], parse("2 << 3").to_sexp
52
+ end
53
+
54
+ def test_subtraction
55
+ assert_equal [:subtract, [:lit, 2], [:lit, 1]], parse("2 - 1").to_sexp
56
+ end
57
+
58
+ def test_addition
59
+ assert_equal [:add, [:lit, 1], [:lit, 2]], parse("1 + 2").to_sexp
60
+ end
61
+
62
+ def test_modulus
63
+ assert_equal [:modulus, [:lit, 10], [:lit, 5]], parse("10 % 5").to_sexp
64
+ end
65
+
66
+ def test_division
67
+ assert_equal [:divide, [:lit, 10], [:lit, 5]], parse("10 / 5").to_sexp
68
+ end
69
+
70
+ def test_multiplication
71
+ assert_equal [:multiply, [:lit, 5], [:lit, 10]], parse("5 * 10").to_sexp
72
+ end
73
+
74
+ def test_exponent
75
+ assert_equal [:raise, [:lit, 5], [:lit, 2]], parse("5 ** 2").to_sexp
76
+ assert_equal [:raise, [:lit, 3], [:u_minus, [:lit, 6]]], parse("3 ** -6").to_sexp
77
+ assert_equal [:raise, [:lit, 18], [:bitwise_not, [:lit, 4]]], parse("18 ** ~4").to_sexp
78
+ end
79
+
80
+ def test_unary_bitwise_not
81
+ assert_equal [:bitwise_not, [:lit, 10]], parse("~10").to_sexp
82
+ end
83
+
84
+ def test_unary_minus
85
+ assert_equal [:u_minus, [:lit, 10]], parse("-10").to_sexp
86
+ end
87
+
88
+ def test_unary_plus
89
+ assert_equal [:u_plus, [:lit, 10]], parse("+10").to_sexp
90
+ end
91
+
92
+ def test_float_scientific
93
+ assert_equal [:lit, 10.10e-3], parse("10.10e-3").to_sexp
94
+ end
95
+
96
+ def test_float
97
+ assert_equal [:lit, 10.10], parse("10.10").to_sexp
98
+ end
99
+
100
+ def test_number_base2
101
+ assert_equal [:lit, 0b1010], parse("0b1010").to_sexp
102
+ end
103
+
104
+ def test_number_base8
105
+ assert_equal [:lit, 011], parse("011").to_sexp
106
+ end
107
+
108
+ def test_number_base16
109
+ assert_equal [:lit, 0xdeadbeef], parse("0xdeadbeef").to_sexp
110
+ assert_equal [:lit, 0xCAFEBABE], parse("0xCAFEBABE").to_sexp
111
+ end
112
+
113
+ def test_number_base10
114
+ assert_equal [:lit, 15], parse("15").to_sexp
115
+ end
116
+
117
+ private
118
+
119
+ def parse(stmt)
120
+ @parser.parse(stmt)
121
+ end
122
+ end
@@ -0,0 +1,111 @@
1
+ require File.dirname(__FILE__) + "/test_helper"
2
+ require "logger"
3
+
4
+ class TestPrettyPrint < Test::Unit::TestCase
5
+ def setup
6
+ @parser = Einstein::Parser.new
7
+ @parser.logger = Logger.new(STDERR)
8
+ end
9
+
10
+ def test_parentheses
11
+ assert_equal "((5 + 2) * (10 - 5))", parse("(5 + 2) * (10 - 5)").inspect
12
+ end
13
+
14
+ def test_order_of_operations
15
+ assert_equal "(3 + (4 * 2))", parse("3 + 4 * 2").inspect
16
+ assert_equal "((7 + (9 * 2)) - (16 / 8))", parse("7 + 9 * 2 - 16 / 8").inspect
17
+ end
18
+
19
+ def test_resolve
20
+ assert_equal "x", parse("x").inspect
21
+ assert_equal "(x + 5)", parse("x + 5").inspect
22
+ assert_equal "(x * 4)", parse("x * 4").inspect
23
+ end
24
+
25
+ def test_bitwise_or
26
+ assert_equal "(12 | 15)", parse("0b1100 | 0b1111").inspect
27
+ end
28
+
29
+ def test_bitwise_xor
30
+ assert_equal "(12 ^ 15)", parse("0b1100 ^ 0b1111").inspect
31
+ end
32
+
33
+ def test_bitwise_and
34
+ assert_equal "(12 & 15)", parse("0b1100 & 0b1111").inspect
35
+ end
36
+
37
+ def test_subtraction
38
+ assert_equal "(10 - 5)", parse("10 - 5").inspect
39
+ assert_equal "(5 - 10)", parse("5 - 10").inspect
40
+ end
41
+
42
+ def test_addition
43
+ assert_equal "(1 + 2)", parse("1 + 2").inspect
44
+ assert_equal "(1.0 + 2.0)", parse("1.0 + 2.0").inspect
45
+ end
46
+
47
+ def test_modulus
48
+ assert_equal "(5 % 10)", parse("5 % 10").inspect
49
+ assert_equal "(10 % 5)", parse("10 % 5").inspect
50
+ end
51
+
52
+ def test_division
53
+ assert_equal "(10 / 5)", parse("10 / 5").inspect
54
+ assert_equal "(10.0 / 5.0)", parse("10.0 / 5.0").inspect
55
+ end
56
+
57
+ def test_multiplication
58
+ assert_equal "(5 * 10)", parse("5 * 10").inspect
59
+ assert_equal "(5.0 * 10.0)", parse("5.0 * 10.0").inspect
60
+ end
61
+
62
+ def test_exponent
63
+ assert_equal "(5 ** 2)", parse("5 ** 2").inspect
64
+ end
65
+
66
+ def test_float
67
+ assert_equal "1.1", parse("1.1").inspect
68
+ end
69
+
70
+ def test_number_base2
71
+ assert_equal "0", parse("0b0").inspect
72
+ assert_equal "15", parse("0b1111").inspect
73
+ assert_equal "10", parse("0B1010").inspect
74
+
75
+ assert_equal "+15", parse("+0b1111").inspect
76
+ assert_equal "+10", parse("+0B1010").inspect
77
+
78
+ assert_equal "-15", parse("-0b1111").inspect
79
+ assert_equal "-10", parse("-0B1010").inspect
80
+ end
81
+
82
+ def test_number_base16
83
+ assert_equal "0", parse("0x0").inspect
84
+ assert_equal "255", parse("0xff").inspect
85
+ assert_equal "255", parse("0XFF").inspect
86
+
87
+ assert_equal "+255", parse("+0xff").inspect
88
+ assert_equal "+255", parse("+0XFF").inspect
89
+
90
+ assert_equal "-255", parse("-0xff").inspect
91
+ assert_equal "-255", parse("-0XFF").inspect
92
+ end
93
+
94
+ def test_number_base10
95
+ assert_equal "0", parse("0").inspect
96
+ assert_equal "1", parse("01").inspect
97
+ assert_equal "10", parse("10").inspect
98
+
99
+ assert_equal "+1", parse("+1").inspect
100
+ assert_equal "+10", parse("+10").inspect
101
+
102
+ assert_equal "-1", parse("-1").inspect
103
+ assert_equal "-10", parse("-10").inspect
104
+ end
105
+
106
+ private
107
+
108
+ def parse(stmt)
109
+ @parser.parse(stmt)
110
+ end
111
+ end
@@ -0,0 +1,120 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" />
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7
+ <title>
8
+ einstein
9
+ </title>
10
+ <script src="javascripts/rounded_corners_lite.inc.js" type="text/javascript"></script>
11
+ <style>
12
+
13
+ </style>
14
+ <script type="text/javascript">
15
+ window.onload = function() {
16
+ settings = {
17
+ tl: { radius: 10 },
18
+ tr: { radius: 10 },
19
+ bl: { radius: 10 },
20
+ br: { radius: 10 },
21
+ antiAlias: true,
22
+ autoPad: true,
23
+ validTags: ["div"]
24
+ }
25
+ var versionBox = new curvyCorners(settings, document.getElementById("version"));
26
+ versionBox.applyCornersToAll();
27
+ }
28
+ </script>
29
+ </head>
30
+ <body>
31
+ <div id="main">
32
+
33
+ <h1>einstein</h1>
34
+ <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/einstein"; return false'>
35
+ <p>Get Version</p>
36
+ <a href="http://rubyforge.org/projects/einstein" class="numbers">0.1.0</a>
37
+ </div>
38
+ <h2>What</h2>
39
+
40
+
41
+ <p>The Einstein library provides a simple arithmetic parser for Ruby apps. Sure,
42
+ you could just use eval, but then you&#8217;re opening yourself up to a world of
43
+ hurt when you accept code from untrusted sources (ie. your users). With
44
+ Einstein, you get a safe, &#8220;locked-down&#8221; arithmetic parser and evaluator that
45
+ can&#8217;t run system commands or otherwise hose your server in the event of a
46
+ malicious code snippet.</p>
47
+
48
+
49
+ <p>Einstein is a fun little exercise in building a parser, and I&#8217;m releasing it
50
+ in the hopes that someone will find it useful.</p>
51
+
52
+
53
+ <h2>Installing</h2>
54
+
55
+
56
+ <pre>sudo gem install einstein</pre>
57
+
58
+ <h2>The basics</h2>
59
+
60
+
61
+ <p>If you&#8217;re only interested in evaluating expressions, you can simply call
62
+ Einstein.evaluate:</p>
63
+
64
+
65
+ <p><pre class='syntax'><span class="constant">Einstein</span><span class="punct">.</span><span class="ident">evaluate</span><span class="punct">(&quot;</span><span class="string">1 + 2 + 3</span><span class="punct">&quot;)</span> <span class="comment"># =&gt; 6</span></pre></p>
66
+
67
+
68
+ <p>You can use variables as well, but you must supply their values:</p>
69
+
70
+
71
+ <p><pre class='syntax'>
72
+ <span class="constant">Einstein</span><span class="punct">.</span><span class="ident">evaluate</span><span class="punct">(&quot;</span><span class="string">x * 4</span><span class="punct">&quot;,</span> <span class="symbol">:x</span> <span class="punct">=&gt;</span> <span class="number">5</span><span class="punct">)</span> <span class="comment"># =&gt; 20</span>
73
+ <span class="constant">Einstein</span><span class="punct">.</span><span class="ident">evaluate</span><span class="punct">(&quot;</span><span class="string">x + y + z</span><span class="punct">&quot;,</span> <span class="symbol">:x</span> <span class="punct">=&gt;</span> <span class="number">3</span><span class="punct">,</span> <span class="symbol">:y</span> <span class="punct">=&gt;</span> <span class="number">4</span><span class="punct">,</span> <span class="symbol">:z</span> <span class="punct">=&gt;</span> <span class="number">5</span><span class="punct">)</span> <span class="comment"># =&gt; 12</span>
74
+ </pre></p>
75
+
76
+
77
+ <h2>How to submit patches</h2>
78
+
79
+
80
+ <p>First, if you haven&#8217;t already, read the <a href="http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/">8 steps for fixing other people&#8217;s
81
+ code</a></p>
82
+
83
+
84
+ <p>The best way to submit a patch is to fork the project on
85
+ <a href="http://github.com">GitHub</a> and make your changes. When you&#8217;ve finished, issue
86
+ a pull request to let me know about the changes you&#8217;ve made.</p>
87
+
88
+
89
+ <h2>Repository access</h2>
90
+
91
+
92
+ <p>The repository is <code>git://github.com/omghax/einstein.git</code> for
93
+ anonymous access. <a href="http://github.com/omghax/einstein/tree/master">GitHub</a> also
94
+ offers a .tgz file download for those of you who don&#8217;t have git installed
95
+ (shame on you).</p>
96
+
97
+
98
+ <h2>License</h2>
99
+
100
+
101
+ <p>This code is free to use under the terms of the <span class="caps">MIT</span> license.</p>
102
+
103
+
104
+ <h2>Contact</h2>
105
+
106
+
107
+ <p>Comments are welcome. Send a message to omghax on
108
+ <a href="http://github.com/omghax">GitHub</a>. Better yet, create your own fork of
109
+ Einstein and take it to crazy new places. If I like your changes, I&#8217;ll merge
110
+ them into the master branch.</p>
111
+ <p class="coda">
112
+ <a href="http://github.com/omghax">Dray Lacy</a>, 29th April 2008<br>
113
+ Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
114
+ </p>
115
+ </div>
116
+
117
+ <!-- insert site tracking codes here, like Google Urchin -->
118
+
119
+ </body>
120
+ </html>
data/website/index.txt ADDED
@@ -0,0 +1,58 @@
1
+ h1. einstein
2
+
3
+ h2. What
4
+
5
+ The Einstein library provides a simple arithmetic parser for Ruby apps. Sure,
6
+ you could just use eval, but then you're opening yourself up to a world of
7
+ hurt when you accept code from untrusted sources (ie. your users). With
8
+ Einstein, you get a safe, "locked-down" arithmetic parser and evaluator that
9
+ can't run system commands or otherwise hose your server in the event of a
10
+ malicious code snippet.
11
+
12
+ Einstein is a fun little exercise in building a parser, and I'm releasing it
13
+ in the hopes that someone will find it useful.
14
+
15
+ h2. Installing
16
+
17
+ <pre>sudo gem install einstein</pre>
18
+
19
+ h2. The basics
20
+
21
+ If you're only interested in evaluating expressions, you can simply call
22
+ Einstein.evaluate:
23
+
24
+ <pre syntax="ruby">Einstein.evaluate("1 + 2 + 3") # => 6</pre>
25
+
26
+ You can use variables as well, but you must supply their values:
27
+
28
+ <pre syntax="ruby">
29
+ Einstein.evaluate("x * 4", :x => 5) # => 20
30
+ Einstein.evaluate("x + y + z", :x => 3, :y => 4, :z => 5) # => 12
31
+ </pre>
32
+
33
+ h2. How to submit patches
34
+
35
+ First, if you haven't already, read the "8 steps for fixing other people's
36
+ code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/
37
+
38
+ The best way to submit a patch is to fork the project on
39
+ "GitHub":http://github.com and make your changes. When you've finished, issue
40
+ a pull request to let me know about the changes you've made.
41
+
42
+ h2. Repository access
43
+
44
+ The repository is <code>git://github.com/omghax/einstein.git</code> for
45
+ anonymous access. "GitHub":http://github.com/omghax/einstein/tree/master also
46
+ offers a .tgz file download for those of you who don't have git installed
47
+ (shame on you).
48
+
49
+ h2. License
50
+
51
+ This code is free to use under the terms of the MIT license.
52
+
53
+ h2. Contact
54
+
55
+ Comments are welcome. Send a message to omghax on
56
+ "GitHub":http://github.com/omghax. Better yet, create your own fork of
57
+ Einstein and take it to crazy new places. If I like your changes, I'll merge
58
+ them into the master branch.