oreilly-snippets 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6c1f282b603c87774de48d0528a5a5c502b7f781
4
+ data.tar.gz: 1107766b02889b4bd04852cc6010594ffd347d1c
5
+ SHA512:
6
+ metadata.gz: 0cfb0a42084e9391dfcbf575c327a48e3cab0fbf18214b4d7227f3c4d008dae3c65a3692d335faff429ac8ff81016b94c5e37efda3c7a0efdbf16eb521858f85
7
+ data.tar.gz: f8ea02d98b973a31bc8dcbedf52a471b40e0673646e16c12a7be2b3fbefbe489f240a03bbbe99e4039a3a8ba3f638973c2d58f70aa569b61e45fe0cc36bf294c
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in oreilly-snippets.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Chris Dawson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # Oreilly::Snippets
2
+
3
+ Write O'Reilly-style code snippets inside your Asciidoc or markdown files.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'oreilly-snippets'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install oreilly-snippets
18
+
19
+ ## Usage
20
+
21
+ ### Snippets Usage
22
+
23
+ Check out (Code Snippets)[http://chimera.labs.oreilly.com/books/1230000000065/ch04.html#code_explanation]
24
+ from O'Reilly.
25
+
26
+ A snippet looks like this inside your markup.
27
+
28
+ ```
29
+ [filename="coffeetech.js", language="js" identifier="IDENTIFIER"]
30
+ snippet~~~~
31
+ Put any descriptive text you want here. It will be replaced with the
32
+ specified code snippet when you build ebook outputs
33
+ snippet~~~~
34
+ ```
35
+
36
+ Then, inside your `coffeetech.js` file you have something like this:
37
+
38
+ ```
39
+ // BEGIN IDENTIFIER
40
+ var mod = angular.module( 'coffeetech', [] );
41
+
42
+ mod.factory( 'Github', function() {
43
+ return new Github({});
44
+ });
45
+ // END IDENTIFIER
46
+
47
+ mod.factory( 'Geo', [ '$window', function( $window ) {
48
+ return $window.navigator.geolocation;
49
+ } ] );
50
+
51
+ ```
52
+
53
+ Putting that snippet into your markdown makes the markdown eventually
54
+ end up like this:
55
+
56
+ ```
57
+ var mod = angular.module( 'coffeetech', [] );
58
+
59
+ mod.factory( 'Github', function() {
60
+ return new Github({});
61
+ });
62
+ ```
63
+
64
+ ### Special additions to snippets
65
+
66
+ You can also use syntax like this to pull from a specific commit in
67
+ your git repository:
68
+
69
+ ```
70
+ [filename="../../github.js.test", language="js", sha="8e05a916fe0b1a9d3e:coffeetech.js"]
71
+ ```
72
+
73
+ This will look in the git repository in the `../../github.js.test`
74
+ directory, then grab the file at the specific SHA hash. This means you
75
+ can write code inside a repository, and add a snippet pointing to that
76
+ exact revision in the repository.
77
+
78
+ NB: This format of snippets is not currently compatible with Atlas
79
+ from O'Reilly. However, you can always process the snippet and write
80
+ out a normal Asciidoc file, a file which will be compatible with
81
+ Atlas. See below for an example using Guard.
82
+
83
+ ### Using with Guard and live-reload
84
+
85
+ One nice workflow is to edit files in your editor, then have guard
86
+ running to process those files, and open them in the browser so that
87
+ when you make changes the live-reload plugin will automatically reload
88
+ the processed HTML.
89
+
90
+ The file structure I use for this:
91
+
92
+ * Store my pre-processed files in the /pre directory. These are the
93
+ files which have the snippets. I need to have a special directory
94
+ because this means Atlas will ignore them, and guard will process
95
+ the files into the Asciidoc files used by Atlas.
96
+ * In the directory root I have a bunch of .asciidoc files which are
97
+ the post-processed files, the files which have the snippets resolved
98
+ into real code.
99
+ * Guard watches both directories. When I make a change to the files in
100
+ /pre, Guard regenerates the asciidoc files at the top level. When
101
+ Guard sees one of those files has been changed, it processes them
102
+ through Asciidoctor and then builds the HTML.
103
+ * I serve the HTML file using a combination of pow (pow.cx) and
104
+ config.ru.
105
+ * Use the live-reload plugin for Chrome:
106
+ https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei
107
+
108
+ ```
109
+ require 'asciidoctor'
110
+ require 'oreilly/snippets'
111
+
112
+ # Add a bit of JS to each file before writing to HTML
113
+ init_script = '<script type="text/javascript" src="init.js"></script>';
114
+
115
+ guard 'shell' do
116
+ watch( /^pre\/[^\/]*\.asciidoc$/) {|m|
117
+ contents = File.read( m[0] )
118
+ snippetized = Oreilly::Snippets.process( contents )
119
+ snippet_out = m[0].gsub( "pre/", "" )
120
+ File.open( snippet_out, "w+" ) do |f|
121
+ f.write snippetized
122
+ puts "Wrote new snippet: #{snippet_out}"
123
+ end
124
+ }
125
+ end
126
+
127
+ guard 'shell' do
128
+ watch( /^[^\/]*\.asciidoc$/ ) { |m|
129
+ puts "File: #{m.inspect}"
130
+ asciidoc = File.read( m[0] )
131
+ out = Asciidoctor.render( asciidoc,
132
+ :header_footer => true,
133
+ :safe => Asciidoctor::SafeMode::SAFE,
134
+ :attributes => {'linkcss!' => ''})
135
+
136
+ File.open( m[0]+ ".html", "w+" ) do |f|
137
+ out.gsub!( '</body>', "</body>\n#{init_script}\n" )
138
+ f.write out
139
+ puts "Wrote: #{m[0]+'.html'}"
140
+ end
141
+ }
142
+ end
143
+
144
+ guard 'livereload' do
145
+ watch(%r{^.+\.(css|js|html)$})
146
+ end
147
+ ```
148
+
149
+ My `config.ru`
150
+
151
+ ```
152
+ app = proc do |env|
153
+ Rack::Directory.new('.').call(env)
154
+ end
155
+
156
+ run app
157
+ ```
158
+
159
+
160
+ ## Running the tests
161
+
162
+ Install the `rspec` gem.
163
+
164
+ ```
165
+ $ rspec
166
+ ```
167
+
168
+ ## Contributing
169
+
170
+ 1. Fork it
171
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
172
+ 2. Write tests (see the tests in the spec/ directory)
173
+ 2. Commit your changes (`git commit -am 'Add some feature'`)
174
+ 3. Push to the branch (`git push origin my-new-feature`)
175
+ 4. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ module Oreilly
2
+ module Snippets
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,87 @@
1
+ require "oreilly/snippets/version"
2
+
3
+ COMMENTS = {
4
+ :js => "\/\/",
5
+ :ruby => "#"
6
+ }
7
+
8
+ module Oreilly
9
+ module Snippets
10
+
11
+ def self.get_content_from_file( spec, identifier, language, sha=nil )
12
+ contents = nil
13
+ if sha
14
+ # Use the filename to change into the directory and use git-show
15
+ cwd = Dir.pwd
16
+ Dir.chdir spec
17
+ contents = `git show #{sha}`
18
+ Dir.chdir cwd
19
+ else
20
+ contents = File.read( spec )
21
+ end
22
+
23
+ rv = nil
24
+ if identifier
25
+ comments = COMMENTS[language.to_sym]
26
+ re = /#{comments} BEGIN #{identifier}\n(.*)\n#{comments} END #{identifier}\n/m
27
+ m = contents.match( re )
28
+ rv = m[1]
29
+ else
30
+ rv = contents
31
+ end
32
+
33
+ # rv = scrub_other_identifiers( contents, comments )
34
+ rv
35
+ end
36
+
37
+ def self.scrub_other_identifiers( s, comments )
38
+ puts s
39
+ re = /#{comments} BEGIN \S+\n(.*)\n#{comments} END \S+\n/m
40
+ s.gsub!( re, $1 )
41
+ s
42
+ end
43
+
44
+ def self.process( input )
45
+ snippets = parse( input )
46
+ rv = input
47
+ if snippets and snippets.length > 0
48
+ snippets.each do |s|
49
+ content = get_content_from_file( s[:filename], s[:identifier], s[:language], s[:sha] )
50
+ rv = rv.gsub( s[:full], content )
51
+ end
52
+ end
53
+ rv
54
+ end
55
+
56
+ def self.parse( input )
57
+ output = []
58
+ input.scan( /(\[[^\]]*\])(\s+)(snippet[^\s]*)(.*?)\3/m ) do |m|
59
+ # Add it all up, and include the snippet piece (second to last captured)
60
+ full = m.join( "" ) + m[m.length-2]
61
+ match = {}
62
+ m[0].scan( /([^=\[,\s]*)="([^"]*)"/ ) do |kv|
63
+ match[kv[0].to_sym] = kv[1]
64
+ end
65
+ match[:full] = full.strip
66
+ output << match
67
+ end
68
+ output
69
+ end
70
+ end
71
+ end
72
+ # To include this snippet in an AsciiDoc file, add the following block:
73
+
74
+ # [filename="factorial.js", language="js", identifier="FACTORIAL_FUNC"]
75
+ # snippet~~~~
76
+ # Put any descriptive text you want here. It will be replaced with the
77
+ # specified code snippet when you build ebook outputs
78
+ # snippet~~~~
79
+ # When the ebook is generated, the following output will be present in place of the snippet block:
80
+
81
+ # function factorial(number) {
82
+ # if (number == 0) {
83
+ # return 1
84
+ # } else {
85
+ # return factorial(number - 1) * number
86
+ # }
87
+ # }
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'oreilly/snippets/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "oreilly-snippets"
8
+ spec.version = Oreilly::Snippets::VERSION
9
+ spec.authors = ["Chris Dawson"]
10
+ spec.email = ["xrdawson@gmail.com"]
11
+ spec.description = %q{Use O'Reilly style snippets inside your markup (asciidoc or markdown) files}
12
+ spec.summary = %q{See the README}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,25 @@
1
+ // BEGIN MODULE_DEFINITION
2
+ var mod = angular.module( 'coffeetech', [] );
3
+
4
+ mod.factory( 'Github', function() {
5
+ return new Github({});
6
+ });
7
+ // END MODULE_DEFINITION
8
+
9
+ mod.factory( 'Geo', [ '$window', function( $window ) {
10
+ return $window.navigator.geolocation;
11
+ } ] );
12
+
13
+ mod.controller( 'GithubCtrl', [ '$scope', 'Github', 'Geo', function( $scope, ghs, Geo ) {
14
+ $scope.messages = []
15
+
16
+ $scope.init = function() {
17
+ $scope.getCurrentLocation( function( position ) {
18
+ $scope.latitude = position.coords.latitude;
19
+ $scope.longitude = position.coords.longitude;
20
+
21
+ // BEGIN FOOBAR
22
+
23
+ alert( "Something" );
24
+
25
+ // END FOOBAR
@@ -0,0 +1,12 @@
1
+ alert("This code has a snippet below!");
2
+ factorial(3);
3
+
4
+ // BEGIN FACTORIAL_FUNC
5
+ function factorial(number) {
6
+ if (number == 0) {
7
+ return 1
8
+ } else {
9
+ return factorial(number - 1) * number
10
+ }
11
+ }
12
+ // END FACTORIAL_FUNC
@@ -0,0 +1,134 @@
1
+ require 'spec_helper'
2
+
3
+ WITH_SHA = <<END
4
+ [filename="../../github.js.test", language="js", sha="8e05a916fe0b1a9d3e:coffeetech.js"]
5
+ snippet~~~~
6
+ Put any descriptive text you want here. It will be replaced with the
7
+ snippet~~~~
8
+ END
9
+
10
+ ORIGINAL_CONTENTS = <<END
11
+ var mod = angular.module( 'coffeetech', [] )
12
+ mod.controller( 'ShopsCtrl', function( $scope ) {
13
+ var github = new Github({} );
14
+ var repo = github.getRepo( "xrd", "spa.coffeete.ch" );
15
+ repo.contents( "gh-pages", "portland.json", function(err, data) {
16
+ $scope.shops = JSON.parse( data );
17
+ $scope.$digest();
18
+ }, false );
19
+ })
20
+ END
21
+
22
+ LOTS_OF_IDENTIFIERS = <<END
23
+
24
+ [filename="spec/fixtures/coffeetech.js", language="js"]
25
+ snippet~~~~
26
+ Put any descriptive text you want here. It will be replaced with the
27
+ specified code snippet when you build ebook outputs
28
+ snippet~~~~
29
+
30
+ END
31
+
32
+
33
+ FULL = <<END
34
+ [filename="spec/fixtures/factorial.js", language="js", identifier="FACTORIAL_FUNC"]
35
+ snippet~~~~
36
+ Put any descriptive text you want here. It will be replaced with the
37
+ specified code snippet when you build ebook outputs
38
+ snippet~~~~
39
+ END
40
+
41
+ TEMPLATE = <<END
42
+
43
+ ABC
44
+
45
+ #{FULL}
46
+
47
+ DEF
48
+
49
+ END
50
+
51
+ WRAPPED_BY_SOURCE = <<END
52
+
53
+ [source,javascript]
54
+ -----
55
+ [filename="spec/fixtures/coffeetech.js", language="js", identifier="MODULE_DEFINITION"]
56
+ snippet~~~~~
57
+ var mod = angular.module( 'coffeetech', [] )
58
+ mod.controller( 'GithubCtrl', function( $scope ) {
59
+ var github = new Github({} );
60
+ var repo = github.getRepo( "gollum", "gollum" );
61
+ repo.show( function(err, repo) {
62
+ $scope.repo = repo;
63
+ $scope.$apply();
64
+ });
65
+ })
66
+ snippet~~~~~
67
+ -----
68
+
69
+ END
70
+
71
+ describe "#parse" do
72
+
73
+ it "should parse wrapped items" do
74
+ outputs = Oreilly::Snippets.parse( WRAPPED_BY_SOURCE )
75
+ output = outputs[0]
76
+ output[:filename].should == "spec/fixtures/coffeetech.js"
77
+ output[:language].should == "js"
78
+ output[:identifier].should == "MODULE_DEFINITION"
79
+ end
80
+
81
+ it "should parse the file and extract the correct things" do
82
+ outputs = Oreilly::Snippets.parse( TEMPLATE )
83
+ output = outputs[0]
84
+ output[:filename].should == "spec/fixtures/factorial.js"
85
+ output[:language].should == "js"
86
+ output[:identifier].should == "FACTORIAL_FUNC"
87
+ output[:full].strip.should == FULL.strip
88
+ end
89
+
90
+ it "should get the SHA when specified" do
91
+ outputs = Oreilly::Snippets.parse( WITH_SHA )
92
+ output = outputs[0]
93
+ output[:sha].should == "8e05a916fe0b1a9d3e:coffeetech.js"
94
+ end
95
+ end
96
+
97
+ # describe "#scrub_other_identifiers" do
98
+ # it "should scrub everything that looks like an identifier" do
99
+ # out = Oreilly::Snippets.scrub_other_identifiers( File.read( "spec/fixtures/coffeetech.js" ), "//" )
100
+ # out.should_not match( /FOOBAR/ )
101
+ # end
102
+ # end
103
+
104
+ describe "#process" do
105
+
106
+ it "should process a complex file" do
107
+ output = Oreilly::Snippets.process( WRAPPED_BY_SOURCE )
108
+ output.should_not match( /MODULE_DEFINITION/ )
109
+ output.should_not match( /\/\/$/ )
110
+ end
111
+
112
+ it "should process a simple file" do
113
+ output = Oreilly::Snippets.process( TEMPLATE )
114
+ output.should match( /ABC/ )
115
+ output.should match( /DEF/ )
116
+ output.should match( /function factorial\(number\)/ )
117
+ output.should_not match( /BEGIN FACTORIAL_FUNC/ )
118
+ output.should_not match( /END FACTORIAL_FUNC/ )
119
+ end
120
+
121
+ # NYI
122
+ # it "should remove all identifiers when processing" do
123
+ # output = Oreilly::Snippets.process( LOTS_OF_IDENTIFIERS )
124
+ # output.should_not match( /BEGIN/ )
125
+ # end
126
+
127
+ describe "#git" do
128
+ it "should retrieve by SHA if specified" do
129
+ output = Oreilly::Snippets.process( WITH_SHA )
130
+ ORIGINAL_CONTENTS.strip.should == output.strip
131
+ end
132
+ end
133
+
134
+ end
@@ -0,0 +1,20 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'oreilly/snippets'
9
+
10
+ RSpec.configure do |config|
11
+ config.treat_symbols_as_metadata_keys_with_true_values = true
12
+ config.run_all_when_everything_filtered = true
13
+ config.filter_run :focus
14
+
15
+ # Run specs in random order to surface order dependencies. If you find an
16
+ # order dependency and want to debug it, you can fix the order by providing
17
+ # the seed, which is printed after each run.
18
+ # --seed 1234
19
+ config.order = 'random'
20
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oreilly-snippets
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Chris Dawson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Use O'Reilly style snippets inside your markup (asciidoc or markdown)
56
+ files
57
+ email:
58
+ - xrdawson@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - .gitignore
64
+ - .rspec
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - lib/oreilly/snippets.rb
70
+ - lib/oreilly/snippets/version.rb
71
+ - oreilly-snippets.gemspec
72
+ - spec/fixtures/coffeetech.js
73
+ - spec/fixtures/factorial.js
74
+ - spec/process_spec.rb
75
+ - spec/spec_helper.rb
76
+ homepage: ''
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.0.2
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: See the README
100
+ test_files:
101
+ - spec/fixtures/coffeetech.js
102
+ - spec/fixtures/factorial.js
103
+ - spec/process_spec.rb
104
+ - spec/spec_helper.rb