guard-templates 0.0.1

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,13 @@
1
+ require 'execjs'
2
+
3
+ module Guard
4
+ class Templates < Guard
5
+ module Compilers
6
+ def self.compile_jade(str)
7
+ jade = File.read(File.join(Pathname.new(__FILE__).dirname.to_s, '../engines/jade.js'))
8
+ ExecJS.compile("window = {}; #{jade}").eval("window.jade.compile(#{str.dump}, {client:true, compileDebug:false}).toString()")
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # Example guardfile for guard-templates
2
+ #
3
+ # by default, output to a single .js file in public/javascript/templates.js
4
+ # if you'd like to compile each template to an individual .js file,
5
+ # change :output to a directory path instead
6
+ #
7
+ # see https://github.com/thegreatape/guard-templates for more options
8
+ guard 'templates', :output => 'public/javascript/templates.js', :namespace => 'MyApp' do
9
+ # change this regex to match your source files
10
+ watch(/app\/javascripts\/templates\/(.*)\.jade$/)
11
+ end
@@ -0,0 +1,115 @@
1
+ require "guard"
2
+ require "guard/guard"
3
+ require 'guard/compilers'
4
+
5
+ module Guard
6
+ class Templates < Guard
7
+
8
+ DEFAULTS = {
9
+ :namespace => 'this'
10
+ }
11
+
12
+ # Initialize a Guard.
13
+ # @param [Array<Guard::Watcher>] watchers the Guard file watchers
14
+ # @param [Hash] options the custom Guard options
15
+ def initialize(watchers = [], options = {})
16
+ @watchers = watchers
17
+ @options = DEFAULTS.clone.merge(options)
18
+ @single_file_mode = @options[:output].match(/\.js$/)
19
+ super(watchers, @options)
20
+ end
21
+
22
+ # Call once when Guard starts. Please override initialize method to init stuff.
23
+ # @raise [:task_has_failed] when start has failed
24
+ def start
25
+ run_all
26
+ end
27
+
28
+ # Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits).
29
+ # @raise [:task_has_failed] when stop has failed
30
+ def stop
31
+ end
32
+
33
+ # Called when `reload|r|z + enter` is pressed.
34
+ # This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
35
+ # @raise [:task_has_failed] when reload has failed
36
+ def reload
37
+ end
38
+
39
+ # Called when just `enter` is pressed
40
+ # This method should be principally used for long action like running all specs/tests/...
41
+ # @raise [:task_has_failed] when run_all has failed
42
+ def run_all
43
+ run_on_change(Watcher.match_files(self, Dir.glob('**/*')))
44
+ end
45
+
46
+ # Called on file(s) modifications that the Guard watches.
47
+ # @param [Array<String>] paths the changes files or paths
48
+ # @raise [:task_has_failed] when run_on_change has failed
49
+ def run_on_change(paths)
50
+ templates = {}
51
+ paths.each do |path|
52
+ @watchers.each do |watcher|
53
+ if target = get_target(path, watcher)
54
+ contents = File.read(path)
55
+ if @single_file_mode
56
+ templates[target] = contents
57
+ else
58
+ dir = Pathname.new(target[:path]).dirname.to_s
59
+ FileUtils.mkdir_p(dir) if !File.exist?(dir)
60
+ File.open(target[:path], 'w') do |f|
61
+ f.write("#{@options[:namespace]}['#{target[:name]}'] = #{compile(contents, target[:type])}")
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ if @single_file_mode
68
+ File.open(@options[:output], 'w') do |f|
69
+ js = templates.map{|target,content| "#{target[:name].dump}: #{compile(content, target[:type])}" }.join(",\n")
70
+ f.write "#{@options[:namespace]}.templates = {#{js}}"
71
+ end
72
+ end
73
+ end
74
+
75
+ # Called on file(s) deletions that the Guard watches.
76
+ # @param [Array<String>] paths the deleted files or paths
77
+ # @raise [:task_has_failed] when run_on_change has failed
78
+ def run_on_deletion(paths)
79
+ matched = false
80
+ paths.each do |path|
81
+ @watchers.each do |watcher|
82
+ if target = get_target(path, watcher)
83
+ matched = true
84
+ FileUtils.rm target[:path] unless @single_file_mode
85
+ end
86
+ end
87
+ end
88
+
89
+ # this is slow, but works. would be better to do a eval + series of deletes,
90
+ # but how to re-serialize the js object?
91
+ run_all if @single_file_mode && matched
92
+ end
93
+
94
+ private
95
+ def get_target(path, watcher)
96
+ if match = path.match(watcher.pattern)
97
+ subpath = match[1]
98
+ jspath = File.join(@options[:output], "#{subpath}.js")
99
+
100
+ {:name => subpath,
101
+ :type => File.extname(path).gsub(/^\./,''),
102
+ :path => jspath}
103
+ end
104
+ end
105
+
106
+ def compile(str, type)
107
+ if Compilers.methods.include?("compile_#{type}")
108
+ Compilers.send("compile_#{type}", str)
109
+ else
110
+ str.dump
111
+ end
112
+ end
113
+
114
+ end
115
+ end
@@ -0,0 +1,202 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require 'execjs'
4
+ require 'fakefs/safe'
5
+
6
+ require 'guard/templates'
7
+ require 'guard/watcher'
8
+
9
+ # ExecJS needs real filesystem access to find the js engine
10
+ def eval_js(str, context='')
11
+ FakeFS.deactivate!
12
+ result = ExecJS.compile(context).eval(str)
13
+ FakeFS.activate!
14
+ result
15
+ end
16
+
17
+ module ExecJS
18
+ class << self
19
+ alias :old_compile :compile
20
+ def compile(source, *args)
21
+ FakeFS.deactivate!
22
+ result = self.old_compile(source, *args)
23
+ FakeFS.activate!
24
+ result
25
+ end
26
+ end
27
+ end
28
+
29
+ ExecJS.runtime.class.const_get('Context').class_eval do
30
+ alias :old_eval :eval
31
+ def eval(source, options={})
32
+ FakeFS.deactivate!
33
+ result = old_eval(source, options)
34
+ FakeFS.activate!
35
+ result
36
+ end
37
+ end
38
+
39
+
40
+ describe Guard::Templates do
41
+ before(:each) do
42
+ # have to write js libs to FakeFS post-fake-activation
43
+ js_src = File.join(Pathname.new(__FILE__).dirname.to_s, '../lib/engines/')
44
+ libs = Dir.glob(File.join(js_src, "*.js")).map do |path|
45
+ [File.basename(path), File.read(path)]
46
+ end
47
+ FakeFS.activate!
48
+ FileUtils.mkdir_p 'lib/engines'
49
+ FileUtils.mkdir_p 'javascripts/templates'
50
+ FileUtils.mkdir_p 'public/templates'
51
+ libs.each do |name, content|
52
+ File.open("lib/engines/#{name}", 'w') {|f| f.write(content)}
53
+ end
54
+ end
55
+
56
+ after(:each) do
57
+ FileUtils.rm_rf 'javascripts'
58
+ FileUtils.rm_rf 'public'
59
+ FakeFS.deactivate!
60
+ end
61
+
62
+ describe "#run_on_change" do
63
+ before(:each) do
64
+ @content = "<div>\n {{foo}}\n</div>"
65
+ @template = 'javascripts/templates/home.handlebars'
66
+ File.open(@template, 'w') {|f| f.write(@content)}
67
+ end
68
+
69
+ it "should create a new file with the stringifed template" do
70
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
71
+ :output => 'public/templates')
72
+ @guard_templates.run_on_change [@template]
73
+ File.exists?('public/templates/home.js').should == true
74
+ File.read('public/templates/home.js').should == "this['home'] = #{@content.dump}"
75
+ end
76
+
77
+ it "should write the template with a namespace" do
78
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
79
+ :output => 'public/templates',
80
+ :namespace => 'App.Jawesome')
81
+ @guard_templates.run_on_change [@template]
82
+ File.read('public/templates/home.js').should == "App.Jawesome['home'] = #{@content.dump}"
83
+ end
84
+
85
+ it "should write the templates to a single file when output has a .js extension" do
86
+ wombat_template = 'javascripts/templates/wombat.handlebars'
87
+ wombat_content = '<span>{{wombat}}</span>'
88
+ File.open(wombat_template, 'w') {|f| f.write(wombat_content)}
89
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
90
+ :output => 'public/templates/templates.js')
91
+ @guard_templates.run_on_change [@template, wombat_template]
92
+ File.exists?('public/templates/home.js').should == false
93
+ File.exists?('public/templates/wombat.js').should == false
94
+ File.exists?('public/templates/templates.js').should == true
95
+ templates = eval_js File.read('public/templates/templates.js')
96
+ templates.keys.should =~ ['home', 'wombat']
97
+ templates['wombat'].should == wombat_content
98
+ templates['home'].should == @content
99
+ end
100
+
101
+ it "should compile jade to native functions" do
102
+ jade_template = 'javascripts/templates/jade-test.jade'
103
+ jade_content = '.greetings O HAI'
104
+ File.open(jade_template, 'w') {|f| f.write(jade_content)}
105
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.jade/)],
106
+ :namespace => 'app',
107
+ :output => 'public/templates')
108
+ @guard_templates.run_on_change [jade_template]
109
+ File.exists?('public/templates/jade-test.js').should == true
110
+ compiled = File.read('public/templates/jade-test.js')
111
+ jade = File.read('lib/engines/jade-runtime.js')
112
+ context = %{var window = {}, app = {};
113
+ #{jade}
114
+ #{compiled} }
115
+ fn = "app['jade-test']()"
116
+ eval_js(fn, context).should == '<div class="greetings">O HAI</div>'
117
+ end
118
+
119
+ it "should compile jade to native functions in single-file mode" do
120
+ jade_template = 'javascripts/templates/jade-test.jade'
121
+ jade_content = '.greetings O HAI'
122
+ File.open(jade_template, 'w') {|f| f.write(jade_content)}
123
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.jade/)],
124
+ :namespace => 'app',
125
+ :output => 'public/templates/all.js')
126
+ @guard_templates.run_on_change [jade_template]
127
+ File.exists?('public/templates/all.js').should == true
128
+ File.exists?('public/templates/jade-test.js').should == false
129
+ compiled = File.read('public/templates/all.js')
130
+ jade = File.read('lib/engines/jade-runtime.js')
131
+ context = %{var window = {}, app = {};
132
+ #{jade}
133
+ #{compiled} }
134
+ fn = "app.templates['jade-test']()"
135
+ eval_js(fn, context).should == '<div class="greetings">O HAI</div>'
136
+ end
137
+ end
138
+
139
+ describe "#run_on_deletion" do
140
+ it "should delete output files with source files are deleted" do
141
+ src_path = 'javascripts/templates/home.handlebars'
142
+ dst_path = 'public/templates/home.js'
143
+ File.open(src_path, 'w') {|f| f.write('<div>{foo}</div>')}
144
+ File.open(dst_path, 'w') {|f| f.write('var home = "<div>{foo}</div>"')}
145
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
146
+ :output => 'public/templates')
147
+ FileUtils.rm(src_path)
148
+ @guard_templates.run_on_deletion(src_path)
149
+ File.exists?(dst_path).should == false
150
+ end
151
+
152
+ it "should delete multiple files correctly" do
153
+ srcs = ['foo', 'bar', 'goats']
154
+ srcs.each do |s|
155
+ File.open("javascripts/templates/#{s}.handlebars", 'w') {|f| f.write('<div>{foo}</div>')}
156
+ File.open("public/templates/#{s}.js", 'w') {|f| f.write("this['#{s}'] = '<div>{foo}</div>'")}
157
+ end
158
+ File.open("javascripts/templates/dont_delete_me_bro.handlebars", 'w') {|f| f.write('<div>{foo}</div>')}
159
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
160
+ :output => 'public/templates')
161
+
162
+ srcs.each{|s| FileUtils.rm "javascripts/templates/#{s}.handlebars"}
163
+ @guard_templates.run_on_deletion(srcs.map{|s| "javascripts/templates/#{s}.handlebars"})
164
+ srcs.each{|s| File.exists?("public/templates/#{s}.js").should == false}
165
+ end
166
+
167
+ it "should delete entries in single file mode" do
168
+ src_path = 'javascripts/templates/home.handlebars'
169
+ File.open(src_path, 'w') {|f| f.write("<div>{foo}</div>")}
170
+ File.open('javascripts/templates/foo.handlebars', 'w') {|f| f.write('foo')}
171
+
172
+ dst_path = 'public/templates.js'
173
+ File.open(dst_path, 'w') {|f| f.write("this['templates'] = {home: 'home', foo: 'bar'}")}
174
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
175
+ :output => 'public/templates.js')
176
+ eval_js(File.read(dst_path)).keys.should =~ ['home', 'foo']
177
+
178
+ FileUtils.rm(src_path)
179
+ @guard_templates.run_on_deletion([src_path])
180
+ templates = eval_js(File.read(dst_path))
181
+ templates.keys.should == ['foo']
182
+ end
183
+ end
184
+
185
+ describe '#run_all' do
186
+ it "should compile everything" do
187
+ paths = ['foo', 'bar/baz', 'duck/duck/go']
188
+ paths.each do |p|
189
+ target = "javascripts/templates/#{p}.handlebars"
190
+ FileUtils.mkdir_p Pathname.new(target).dirname.to_s
191
+ File.open(target, 'w') {|f| f.write(p)}
192
+ end
193
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
194
+ :output => 'public/templates/')
195
+
196
+ @guard_templates.run_all
197
+ paths.each do |p|
198
+ File.exists?("public/templates/#{p}.js").should == true
199
+ end
200
+ end
201
+ end
202
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: guard-templates
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Thomas Mayfield
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-01 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: guard
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: execjs
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: json
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ type: :development
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: rspec
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ type: :development
89
+ version_requirements: *id005
90
+ - !ruby/object:Gem::Dependency
91
+ name: fakefs
92
+ prerelease: false
93
+ requirement: &id006 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ type: :development
103
+ version_requirements: *id006
104
+ description: Guard plugin for smart, automatic compilation of your Javascript template files into usable Javascript
105
+ email:
106
+ - Thomas.Mayfield@gmail.com
107
+ executables: []
108
+
109
+ extensions: []
110
+
111
+ extra_rdoc_files: []
112
+
113
+ files:
114
+ - .gitignore
115
+ - Gemfile
116
+ - README.md
117
+ - Rakefile
118
+ - guard-templates.gemspec
119
+ - lib/engines/jade-runtime.js
120
+ - lib/engines/jade.js
121
+ - lib/guard/compilers.rb
122
+ - lib/guard/templates.rb
123
+ - lib/guard/templates/templates/Guardfile
124
+ - spec/guard-templates-spec.rb
125
+ homepage: ""
126
+ licenses: []
127
+
128
+ post_install_message:
129
+ rdoc_options: []
130
+
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ hash: 3
139
+ segments:
140
+ - 0
141
+ version: "0"
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ hash: 3
148
+ segments:
149
+ - 0
150
+ version: "0"
151
+ requirements: []
152
+
153
+ rubyforge_project: guard-templates
154
+ rubygems_version: 1.8.17
155
+ signing_key:
156
+ specification_version: 3
157
+ summary: Javascript template compilation via Guard
158
+ test_files:
159
+ - spec/guard-templates-spec.rb