ichiban 1.0.13 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bed8e1c7721e2dde1e29fe7ee6e749ef444c4da6
4
- data.tar.gz: cc2e0054c59fe95cb59637c42427656fa95568eb
3
+ metadata.gz: df9160a6af9ff5f9c21a2e6a5ca27f7488de6055
4
+ data.tar.gz: 9091f8368c5e34f12471910c537ab2e45ea1317d
5
5
  SHA512:
6
- metadata.gz: e2db860d451e68cc3f83c1d4e77e8a64bddaa266a9daad0fc2d252299e5bb812566d8d7041692ad2c8886ab4bdd86dec46e28f7439b1f15f0b5dee399fba8a10
7
- data.tar.gz: 0549dde9e9d8db5c32edff2c1ef16f83fac511e07fab45c5157a101467581d48dad913e3e86efa74a08f6ab61d5deb21c503335929914d33d995951d4991004e
6
+ metadata.gz: 2faa7ec8f821e61c0d39dad65ee70354ddcee0bf5c338a355c6ef901c0ffe5784717b99a0501e97eaca4e3c8bef096dd1d5e97609e5f14d957ce781f140c5b81
7
+ data.tar.gz: de957b1070e3f0f85f03065135c10660bf3e049749bcbadc3db593e6c26ce8fbfdeef7e21483d7822f21d7e694c2ed29b791d5f7cb148970bbc3263569b867e2
data/bin/ichiban CHANGED
@@ -1,9 +1,21 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # for dev purposes:
4
- #$:.unshift File.expand_path(File.join(File.dirname(__FILE__), '../lib'))
3
+ # When developing Ichiban, run this script like so:
4
+ # DEV=1 ~/gems/ichiban/bin/ichiban watch
5
+ # This will prevent your project's Gemfile from being used! There's really no way to avoid
6
+ # that, because using the Gemfile would cause the installed version of Ichiban to be used.
5
7
 
6
8
  require 'rubygems'
9
+ if ENV['DEV'] == '1'
10
+ puts "\n"
11
+ puts '====================================================='
12
+ puts 'Ichiban running in dev mode. Gemfile will be ignored.'
13
+ puts '====================================================='
14
+ puts "\n"
15
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '../lib'))
16
+ else
17
+ require 'bundler/setup'
18
+ end
7
19
  require 'ichiban'
8
20
 
9
21
  Ichiban::Command.new(ARGV).run
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'ichiban'
@@ -1,3 +1,34 @@
1
- Ichiban.config do |config|
2
- config.relative_url_root = '/'
1
+ Ichiban.config do |cfg|
2
+ # If your site lives under a sub-path specify that here. For example, if your site lives
3
+ # under http://example.com/my-site, then the relative_url_root should be '/my-site'.
4
+ cfg.relative_url_root = '/'
5
+
6
+ # Specifies what must be recompiled when files change. For example, the following rule:
7
+ #
8
+ # 'layouts/default.html' => ['html/**/*', 'scripts/**/*']
9
+ #
10
+ # means that when layouts/default.html changes, everything in the html and scripts
11
+ # folders must be recompiled.
12
+ cfg.dependencies = {
13
+ 'layouts/default.html' => ['html/**/*', 'scripts/**/*']
14
+ }
15
+
16
+ # Configures JavaScript compilation. Each hash key is the name of a concatenated,
17
+ # minified JS file. Each hash value is an array of source files in the assets/js
18
+ # folder. For example, the following rule:
19
+ #
20
+ # 'site.js' => ['home.js', 'popups.js']
21
+ #
22
+ # would generate compiled/js/site.js and compiled/js/site.js.map, using the source files
23
+ # assets/js/home.js and assets/js/popups.js.
24
+ cfg.js_manifests = {
25
+ 'site.js' => ['site.js']
26
+ }
27
+
28
+ # Identify the SCSS files in assets/css that should be compiled into final CSS files.
29
+ # Often, many of your SCSS files are simply imported into other SCSS files--in
30
+ # other words, they don't map to their own, distinct CSS files. Make sure that
31
+ # cfg.dependencies covers any SCSS imports. Otherwise, saving the imported file won't
32
+ # update the root file.
33
+ cfg.scss_root_files = ['screen.scss']
3
34
  end
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <title><%= @page_title %> - Site Name Here</title>
5
5
  <%= stylesheet_link_tag 'screen.css' %>
6
- <%= javascript_include_tag 'interaction.js' %>
6
+ <%= javascript_include_tag 'site.js' %>
7
7
  </head>
8
8
 
9
9
  <body>
@@ -11,8 +11,8 @@ module Ichiban
11
11
  end
12
12
  case @file
13
13
  when Ichiban::SCSSFile
14
- Sass.compile_file @file.abs, @file.dest
15
- else
14
+ Sass.compile_file @file.abs, @file.dest, load_paths: [File.join(Ichiban.project_root, 'assets/css')]
15
+ else
16
16
  FileUtils.cp @file.abs, @file.dest
17
17
  end
18
18
  Ichiban.logger.compilation(@file.abs, @file.dest)
@@ -7,10 +7,12 @@ module Ichiban
7
7
 
8
8
  def print_usage
9
9
  puts(
10
- "Usage: ichiban [command]\n" +
10
+ "\nUsage: ichiban [command]\n" +
11
11
  "Available commands: \n" +
12
12
  " watch\n" +
13
- " new [path]"
13
+ " new [path]\n" +
14
+ " help\n\n" +
15
+ "https://github.com/jarrett/ichiban\n\n"
14
16
  )
15
17
  end
16
18
 
@@ -24,7 +26,9 @@ module Ichiban
24
26
  Ichiban::ProjectGenerator.new(
25
27
  File.expand_path(@args[0])
26
28
  ).generate
27
- puts "Initialized Ichiban project in #{@args[0]}"
29
+ Ichiban.logger.out "Initialized Ichiban project in #{@args[0]}"
30
+ when 'version', '-v', '--version'
31
+ puts "Ichiban version #{::Ichiban::VERSION}"
28
32
  else
29
33
  print_usage
30
34
  end
@@ -6,16 +6,34 @@ module Ichiban
6
6
  end
7
7
 
8
8
  class Config
9
- attr_writer :relative_url_root
10
-
11
9
  def self.load_file
12
10
  config_file = File.join(Ichiban.project_root, 'config.rb')
13
11
  raise "#{config_file} must exist" unless File.exists?(config_file)
14
12
  load config_file
15
13
  end
16
14
 
15
+ attr_writer :dependencies
16
+
17
+ def dependencies
18
+ @dependencies || raise("Ichiban.config.dependencies not set. Set inside block in config.rb like this: cfg.dependencies = {...}")
19
+ end
20
+
21
+ attr_writer :js_manifests
22
+
23
+ def js_manifests
24
+ @js_manifests || raise("Ichiban.config.js_manifests not set. Set inside block in config.rb like this: cfg.js_manifests = {...}")
25
+ end
26
+
27
+ attr_writer :relative_url_root
28
+
17
29
  def relative_url_root
18
30
  @relative_url_root || raise("Ichiban.config.relative_url_root not set. Set inside block in config.rb like this: cfg.relative_url_root = '/'")
19
31
  end
32
+
33
+ attr_writer :scss_root_files
34
+
35
+ def scss_root_files
36
+ @scss_root_files || raise("Ichiban.config.scss_root_files not set. Set inside block in config.rb like this: cfg.scss_root_files = ['screen.scss']")
37
+ end
20
38
  end
21
39
  end
@@ -1,54 +1,58 @@
1
1
  module Ichiban
2
- module Dependencies
3
- @graphs = {}
4
-
5
- # Does not delete the files. Just clears the graphs from memory. This gets called whenever
6
- # Ichiban.project_root is changed.
7
- def self.clear_graphs
8
- @graphs = {}
9
- end
10
-
11
- def self.delete_dep(graph_file_path, dep)
12
- ensure_graph_initialized(graph_file_path)
13
- graph = @graphs[graph_file_path]
14
- graph.each do |ind, deps|
15
- deps.delete dep
16
- end
17
- save_graph_file(graph_file_path, graph)
18
- end
19
-
20
- # graph_file_path is a relative path
21
- def self.graph(graph_file_path)
22
- ensure_graph_initialized(graph_file_path)
23
- @graphs[graph_file_path]
24
- end
25
-
26
- # graph_file_path is a relative path
27
- def self.ensure_graph_initialized(graph_file_path)
28
- unless @graphs[graph_file_path]
29
- abs = File.join(Ichiban.project_root, graph_file_path)
30
- if File.exists?(abs)
31
- @graphs[graph_file_path] = JSON.parse(File.read(abs))
2
+ module Dependencies
3
+ def self.files_depending_on(abs_path)
4
+ rel = abs_path.slice((Ichiban.project_root.length + 1)..-1)
5
+ if deps = Ichiban.config.dependencies[rel]
6
+ case deps
7
+ when String
8
+ paths = Dir.glob(File.join(Ichiban.project_root, deps))
9
+ when Array
10
+ paths = deps.map do |dep|
11
+ case dep
12
+ when String
13
+ Dir.glob(File.join(Ichiban.project_root, dep))
14
+ when Proc
15
+ files_from_proc(dep)
16
+ else
17
+ raise(TypeError, "Expected String or Proc, but was: #{files.inspect}")
18
+ end
19
+ end.flatten
20
+ when Proc
21
+ paths = [files_from_proc(deps)].flatten
32
22
  else
33
- @graphs[graph_file_path] = {}
23
+ raise(TypeError, "Expected String, Array, or Proc, but was: #{files.inspect}")
34
24
  end
25
+ paths.map do |path|
26
+ project_file = Ichiban::ProjectFile.from_abs(path)
27
+ end.compact
28
+ else
29
+ []
35
30
  end
36
31
  end
37
32
 
38
- def self.save_graph_file(graph_file_path, graph)
39
- File.open(File.join(Ichiban.project_root, graph_file_path), 'w') do |f|
40
- f << JSON.generate(graph)
33
+ def self.files_from_proc(proc)
34
+ files = proc.call
35
+ # Validate return value is String or Array.
36
+ if !files.is_a?(String) and !files.is_a?(Array)
37
+ raise(TypeError, "Expected Proc to return String or Array, but was: #{files.inspect}")
38
+ end
39
+ # If return value is Array, validate all members are Strings.
40
+ if files.is_a?(Array) and !files.all? { |f| f.is_a?(String) }
41
+ raise(TypeError, "Proc returned Array, but not all elements were Strings: #{files.inspect}")
42
+ end
43
+ # If return value is String, wrap it in an Array.
44
+ if files.is_a?(String)
45
+ files = [files]
46
+ end
47
+ files.map do |file|
48
+ File.join(Ichiban.project_root,file)
41
49
  end
42
50
  end
43
51
 
44
- # Loads the graph from disk if it's not already in memory. Updates the graph. Writes the new
45
- # graph to disk. graph_file_path is a relative path.
46
- def self.update(graph_file_path, ind, dep)
47
- ensure_graph_initialized(graph_file_path)
48
- graph = @graphs[graph_file_path]
49
- graph[ind] ||= []
50
- graph[ind] << dep unless graph[ind].include?(dep)
51
- save_graph_file(graph_file_path, graph)
52
+ def self.propagate(path)
53
+ files_depending_on(path).each do |project_file|
54
+ project_file.update
55
+ end
52
56
  end
53
57
  end
54
58
  end
@@ -26,6 +26,8 @@ module Ichiban
26
26
  @_current_path
27
27
  end
28
28
 
29
+ # h is provided by ERB::Util.
30
+
29
31
  def javascript_include_tag(js_file)
30
32
  js_file = js_file + '.js' unless js_file.end_with?('.js')
31
33
  path = normalize_path(File.join('/js', js_file))
@@ -79,14 +81,14 @@ module Ichiban
79
81
  path
80
82
  end
81
83
 
82
- def partial(path)
84
+ def partial(path, options = {})
85
+ options = {ivars: {}}.merge(options)
83
86
  file = Ichiban::PartialHTMLFile.new(
84
87
  File.join('html', path)
85
88
  )
86
- # Record the dependency like this: 'folder/partial-name' => 'folder/included-file.html'
87
- Ichiban::Dependencies.update('.partial_dependencies.json', file.partial_name, @_template_path)
88
89
  compiler = Ichiban::HTMLCompiler.new(file)
89
- compiler.ivars = to_hash # to_hash is inherited from Erubis::Context. It's a hash of the instance variables.
90
+ # to_hash is inherited from Erubis::Context. It's a hash of the instance variables.
91
+ compiler.ivars = to_hash.merge(options[:ivars])
90
92
  compiler.compile_to_str
91
93
  end
92
94
 
@@ -60,7 +60,6 @@ module Ichiban
60
60
  :filename => layout_path
61
61
  )
62
62
  html = eruby.evaluate(ctx) { html }
63
- Ichiban::Dependencies.update('.layout_dependencies.json', layout_name, @html_file.rel)
64
63
  html
65
64
  end
66
65
  end
@@ -0,0 +1,89 @@
1
+ module Ichiban
2
+ # This class uses source_map and uglifier to concatenate, minify, and source-map JS files.
3
+ class JSCompiler
4
+ def compile
5
+ rel = @file.rel_to 'assets/js'
6
+ Ichiban.config.js_manifests.each do |dest, sources|
7
+ if sources.include? rel
8
+ # Make the source paths absolute.
9
+ sources = sources.map do |source|
10
+ abs = File.join Ichiban.project_root, 'assets/js', source
11
+ unless File.exists? abs
12
+ raise "Something's wrong with Ichiban.config.js_manifests. JS source file does not exist: #{abs.inspect}"
13
+ end
14
+ abs
15
+ end
16
+
17
+ # Make two code strings. The first contains the minified JS. The second
18
+ # contains the source map.
19
+ js, map = compile_paths(
20
+ sources,
21
+ File.join(Ichiban.config.relative_url_root, 'js'),
22
+ dest,
23
+ dest + '.map'
24
+ )
25
+
26
+ # Make sure the JS folder exists.
27
+ FileUtils.mkdir_p File.join(Ichiban.project_root, 'compiled/js')
28
+
29
+ # Write the minified JS.
30
+ compiled_js_path = File.join(Ichiban.project_root, 'compiled/js', dest)
31
+ File.open(compiled_js_path, 'w') do |f|
32
+ f << js
33
+ end
34
+
35
+ # Write the source map.
36
+ File.open(File.join(Ichiban.project_root, 'compiled/js', dest + '.map'), 'w') do |f|
37
+ f << map
38
+ end
39
+
40
+ # Log the compilation of the JS file, but don't log the compilation of the map.
41
+ Ichiban.logger.compilation(@file.abs, compiled_js_path)
42
+ end
43
+ end
44
+ end
45
+
46
+ def initialize(file)
47
+ @file = file
48
+ end
49
+
50
+ private
51
+
52
+ # Pass in absolute source_paths.
53
+ def compile_paths(source_paths, source_root, js_filename, map_filename)
54
+ js, map = concat(
55
+ source_paths.map { |p| [File.read(p), File.basename(p)] },
56
+ source_root
57
+ )
58
+
59
+ js, map = uglify js, map, js_filename
60
+
61
+ map_url = File.join Ichiban.config.relative_url_root, 'js', map_filename
62
+ js << "\n//# sourceMappingURL=#{map_url}"
63
+
64
+ [js, map]
65
+ end
66
+
67
+ # Sources should be an array of form: [['alert("foo");', 'alert.js']] Returns a tuple. The
68
+ # first element is the concatenated JS. The second element is the map string.
69
+ def concat(sources, source_root)
70
+ js = StringIO.new
71
+ map = SourceMap.new(
72
+ generated_output: js,
73
+ source_root: source_root
74
+ )
75
+ sources.each do |source_js, source_filename|
76
+ map.add_generated source_js, source: source_filename
77
+ end
78
+ js.rewind
79
+ [js.read, map.to_s]
80
+ end
81
+
82
+ def uglify(js, map, js_filename)
83
+ Uglifier.new(
84
+ input_source_map: map,
85
+ output_filename: js_filename
86
+ ).compile_with_map(js)
87
+ end
88
+ end
89
+ end
@@ -20,7 +20,9 @@ module Ichiban
20
20
  # Calls Object.remove_const on all tracked modules. Also clears the compiler's list of user-defined helpers.
21
21
  def delete_all
22
22
  @loaded_constants.each do |const_name|
23
- Object.send(:remove_const, const_name)
23
+ if Object.const_defined?(const_name)
24
+ Object.send(:remove_const, const_name)
25
+ end
24
26
  end
25
27
  Ichiban::HTMLCompiler::Context.clear_user_defined_helpers
26
28
  end
@@ -82,10 +82,9 @@ module Ichiban
82
82
  end
83
83
  end
84
84
 
85
- def script_run(ind_path, dep_path)
86
- ind_path = ind_path.slice(Ichiban.project_root.length + 1..-1)
87
- dep_path = dep_path.slice(Ichiban.project_root.length + 1..-1)
88
- msg = "#{dep_path} running due to change in #{ind_path}"
85
+ def script_run(path)
86
+ path = path.slice(Ichiban.project_root.length + 1..-1)
87
+ msg = "#{path} running"
89
88
  if ansi?
90
89
  msg = ANSI.color(msg, :magenta)
91
90
  end
@@ -103,7 +102,7 @@ module Ichiban
103
102
  require 'ansi'
104
103
  @ansi = true
105
104
  rescue LoadError
106
- Ichiban.logger.out("Try `gem install ansi` for colorized output. If you're using a Gemfile, add it there too.")
105
+ Ichiban.logger.out("Add `gem ansi` to your Gemfile for colorized output.")
107
106
  end
108
107
  end
109
108
  end
@@ -122,7 +122,7 @@ module Ichiban
122
122
  li_inner_html << @ctx.content_tag('span', text, 'class' => 'selected')
123
123
  else
124
124
  if current_path_starts_with?(path)
125
- a_attrs = {'class' => 'ancestor_of_selected'}
125
+ a_attrs = {'class' => 'above-selected'}
126
126
  else
127
127
  a_attrs = {}
128
128
  end
@@ -12,6 +12,7 @@ module Ichiban
12
12
  # Returns a new instance based on an absolute path. Will automatically pick the right subclass.
13
13
  # Return nil if the file is not recognized.
14
14
  def self.from_abs(abs)
15
+ return nil if File.directory?(abs)
15
16
  rel = abs.slice(Ichiban.project_root.length..-1) # Relative to project root
16
17
  rel.sub!(/^\//, '') # Remove leading slash
17
18
  handler = @types.detect do |_, proc|
@@ -45,6 +46,18 @@ module Ichiban
45
46
 
46
47
  attr_reader :rel
47
48
 
49
+ # Pass in a path relative to Ichiban.projet_root. Returns the path relative to the
50
+ # one you passed in. E.g. if the file is assets/js/example.js, then
51
+ # rel_to('assets/js') returns 'example.js'.
52
+ def rel_to(path)
53
+ path = path + '/' unless path.end_with?('/')
54
+ if rel.start_with?(path)
55
+ rel.sub(path, '')
56
+ else
57
+ raise "#{@abs} is not in #{path}"
58
+ end
59
+ end
60
+
48
61
  # Returns a new path where the old extension is replaced with new_ext
49
62
  def replace_ext(path, new_ext)
50
63
  path.sub(/\..+$/, '.' + new_ext)
@@ -85,24 +98,7 @@ module Ichiban
85
98
  end
86
99
 
87
100
  def update
88
- # Normal HTML files that depend on this partial
89
- if deps = Ichiban::Dependencies.graph('.partial_dependencies.json')[partial_name]
90
- deps.each do |dep|
91
- # dep will be a path relative to the html directory. It looks like this: 'folder/file.html'
92
- Ichiban::HTMLFile.new(File.join('html', dep)).update
93
- end
94
- end
95
-
96
- # Scripts that depend on this partial
97
- dep_key = "html/#{partial_name}.html"
98
- if deps = Ichiban::Dependencies.graph('.script_dependencies.json')[dep_key]
99
- deps.each do |dep|
100
- # dep will be a path relative to the html directory. It looks like this: 'folder/file.html'
101
- script_path = File.join(Ichiban.project_root, dep)
102
- Ichiban.logger.script_run(@abs, script_path)
103
- script = Ichiban::Script.new(script_path).run
104
- end
105
- end
101
+ # no-op
106
102
  end
107
103
  end
108
104
 
@@ -112,30 +108,13 @@ module Ichiban
112
108
  end
113
109
 
114
110
  def update
115
- Ichiban.logger.layout(@abs)
116
- if deps = Ichiban::Dependencies.graph('.layout_dependencies.json')[layout_name]
117
- deps.each do |dep|
118
- # dep is a path relative to the project root
119
- if File.exists?(File.join(Ichiban.project_root, dep))
120
- # Ignore partial templates
121
- unless File.basename(dep).start_with?('_')
122
- Ichiban::HTMLFile.new(dep).update
123
- end
124
- else
125
- Dependencies.delete_dep('.layout_dependencies.json', dep)
126
- end
127
- end
128
- end
111
+ # no-op
129
112
  end
130
113
  end
131
114
 
132
- class JSFile < ProjectFile
133
- def dest_rel_to_compiled
134
- File.join('js', @rel.slice('assets/js/'.length..-1))
135
- end
136
-
115
+ class JSFile < ProjectFile
137
116
  def update
138
- Ichiban::AssetCompiler.new(self).compile
117
+ Ichiban::JSCompiler.new(self).compile
139
118
  end
140
119
  end
141
120
 
@@ -168,7 +147,9 @@ module Ichiban
168
147
  end
169
148
 
170
149
  def update
171
- Ichiban::AssetCompiler.new(self).compile
150
+ if Ichiban.config.scss_root_files.include?(rel_to('assets/css'))
151
+ Ichiban::AssetCompiler.new(self).compile
152
+ end
172
153
  end
173
154
  end
174
155
 
@@ -218,7 +199,7 @@ module Ichiban
218
199
 
219
200
  class DataFile < ProjectFile
220
201
  def update
221
- Ichiban.script_runner.data_file_changed(@abs)
202
+ #no-op
222
203
  end
223
204
  end
224
205
 
@@ -241,16 +222,16 @@ module Ichiban
241
222
  !File.basename(rel).start_with?('_')
242
223
  end
243
224
  register_type(Ichiban::LayoutFile) { |rel| rel.start_with?('layouts') and rel.end_with?('.html') }
244
- register_type(Ichiban::JSFile) { |rel| rel.start_with?('assets/js') }
225
+ register_type(Ichiban::JSFile) { |rel| rel.start_with?('assets/js') and rel.end_with?('.js') }
245
226
  register_type(Ichiban::EJSFile) { |rel| rel.start_with?('assets/ejs') and rel.end_with?('.ejs') }
246
227
  register_type(Ichiban::CSSFile) { |rel| rel.start_with?('assets/css') and rel.end_with?('.css') }
247
228
  register_type(Ichiban::SCSSFile) { |rel| rel.start_with?('assets/css') and rel.end_with?('.scss') }
248
229
  register_type(Ichiban::ImageFile) { |rel| rel.start_with?('assets/img') }
249
230
  register_type(Ichiban::MiscAssetFile) { |rel| rel.start_with?('assets/misc') }
250
- register_type(Ichiban::ModelFile) { |rel| rel.start_with?('models') }
231
+ register_type(Ichiban::ModelFile) { |rel| rel.start_with?('models') and rel.end_with?('.rb') }
251
232
  register_type(Ichiban::DataFile) { |rel| rel.start_with?('data') }
252
- register_type(Ichiban::ScriptFile) { |rel| rel.start_with?('scripts') }
253
- register_type(Ichiban::HelperFile) { |rel| rel.start_with?('helpers') }
233
+ register_type(Ichiban::ScriptFile) { |rel| rel.start_with?('scripts') and rel.end_with?('.rb') }
234
+ register_type(Ichiban::HelperFile) { |rel| rel.start_with?('helpers') and rel.end_with?('.rb') }
254
235
  register_type(Ichiban::HtaccessFile) { |rel| rel == 'webserver/htaccess.txt' }
255
236
  end
256
237
  end
@@ -3,57 +3,15 @@ module Ichiban
3
3
  @script_runner ||= Ichiban::ScriptRunner.new
4
4
  end
5
5
 
6
- class ScriptRunner
7
- # Takes an absolute path. Consults the dependencies graph.
8
- def data_file_changed(path)
9
- # Add one to the length to remove the leading slash
10
- rel_to_root = path.slice(Ichiban.project_root.length + 1..-1)
11
- dep_graph = Ichiban::Dependencies.graph(self.class.dep_graph_path)
12
- if deps = dep_graph[rel_to_root]
13
- deps.each do |dep|
14
- Ichiban.logger.script_run(path, File.join(Ichiban.project_root, dep))
15
- Ichiban::Script.new(
16
- File.join(Ichiban.project_root, dep)
17
- ).run
18
- end
19
- end
20
- end
21
-
22
- def self.dep_graph_path
23
- '.script_dependencies.json'
24
- end
25
-
6
+ class ScriptRunner
26
7
  # Takes an absolute path
27
8
  def script_file_changed(path)
28
- Ichiban.logger.script_run(path, path)
9
+ Ichiban.logger.script_run(path)
29
10
  script = Ichiban::Script.new(path).run
30
11
  end
31
12
  end
32
13
 
33
14
  class Script
34
- # Every file that the script depends on (e.g. a data file) should be declared with this method.
35
- # This is how Ichiban knows to re-run the script when one of the files changes. Pass in a path
36
- # relative to the project root.
37
- #
38
- # However, you don't need to declare dependencies for the files in the html directory that the
39
- # script uses. Those will automatically be tracked.
40
- def depends_on(ind_path)
41
- if ind_path.start_with?('/')
42
- raise(ArgumentError, 'depends_on must be passed a path relative to the project root, e.g. "data/employees.xml"')
43
- end
44
- # Format in dependency graph: 'data/employees.json' => 'scripts/generate_employees.rb'
45
- Ichiban::Dependencies.update(
46
- Ichiban::ScriptRunner.dep_graph_path,
47
-
48
- # Path to independent file, relative to project root.
49
- ind_path,
50
-
51
- # Path to dependent file (i.e. this script), relative to project root.
52
- # Add one to the length to remove the leading slash.
53
- @path.slice(Ichiban.project_root.length + 1..-1)
54
- )
55
- end
56
-
57
15
  # Automatically appends .html to dest_path
58
16
  def generate(template_path, dest_path, ivars)
59
17
  dest_path += '.html'
@@ -1,46 +1,62 @@
1
1
  module Ichiban
2
- class Watcher
2
+ class Watcher
3
3
  def initialize(options = {})
4
4
  @options = {
5
5
  :latency => 0.5
6
6
  }.merge(options)
7
+ @listen_event_log = []
8
+ @loader = Ichiban::Loader.new
7
9
  end
8
10
 
9
- def start(blocking = true)
10
- @loader = Ichiban::Loader.new
11
+ attr_reader :listener
12
+
13
+ attr_reader :listen_event_log
14
+
15
+ # The test suite calls this method directly
16
+ # to bypass the Listen gem for certain tests.
17
+ def on_change(modified, added, deleted)
18
+ # Modifications and additions are treated the same.
19
+ (modified + added).uniq.each do |path|
20
+ if file = Ichiban::ProjectFile.from_abs(path)
21
+ begin
22
+ @loader.change(file) # Tell the Loader that this file has changed
23
+ file.update
24
+ rescue Exception => exc
25
+ Ichiban.logger.exception(exc)
26
+ end
27
+ end
28
+ end
11
29
 
30
+ # Deletions are handled specially.
31
+ deleted.each do |path|
32
+ Ichiban::Deleter.new.delete_dest(path)
33
+ end
34
+
35
+ # Finally, propagate this change to any dependent files.
36
+ (modified + added + deleted).uniq.each do |path|
37
+ begin
38
+ Ichiban::Dependencies.propagate(path)
39
+ rescue => exc
40
+ Ichiban.logger.exception(exc)
41
+ end
42
+ end
43
+ end
44
+
45
+ def start
12
46
  Ichiban.logger.out 'Starting watcher'
13
47
  begin
14
48
  @listener = Listen.to(
15
- File.join(Ichiban.project_root, 'html'),
16
- File.join(Ichiban.project_root, 'layouts'),
17
- File.join(Ichiban.project_root, 'assets'),
18
- File.join(Ichiban.project_root, 'models'),
19
- File.join(Ichiban.project_root, 'helpers'),
20
- File.join(Ichiban.project_root, 'scripts'),
21
- File.join(Ichiban.project_root, 'data'),
22
- File.join(Ichiban.project_root, 'webserver')
23
- )
24
- .ignore(/.listen_test$/)
25
- .latency(@options[:latency])
26
- .change do |modified, added, deleted|
27
- (modified + added).uniq.each do |path|
28
- if file = Ichiban::ProjectFile.from_abs(path)
29
- @loader.change(file) # Tell the Loader that this file has changed
30
- begin
31
- file.update
32
- rescue => exc
33
- Ichiban.logger.exception(exc)
34
- end
35
- end
36
- end
37
- deleted.each do |path|
38
- Ichiban::Deleter.new.delete_dest(path)
39
- end
49
+ Ichiban.project_root,
50
+ ignore: /.listen_test$/,
51
+ latency: @options[:latency]
52
+ ) do |modified, added, deleted|
53
+ @listen_event_log << [modified, added, deleted]
54
+ on_change modified, added, deleted
40
55
  end
41
- @listener.start(blocking)
56
+ @listener.start
57
+ sleep
42
58
  rescue Interrupt
43
- Ichiban.logger.out "Stopping watcher"
59
+ stop
44
60
  exit 0
45
61
  end
46
62
  end
data/lib/ichiban.rb CHANGED
@@ -2,19 +2,20 @@
2
2
  require 'fileutils'
3
3
  require 'json'
4
4
  require 'erb' # Just for the helpers
5
+ require 'stringio'
5
6
 
6
7
  # Gems
7
- #require 'active_support/core_ext/class/attribute'
8
- #require 'active_support/core_ext/object/blank'
9
8
  require 'active_support/core_ext/array/extract_options'
10
9
  require 'active_support/inflector'
11
10
  require 'sass'
12
11
  require 'erubis'
13
12
  require 'rake'
14
13
  require 'bundler'
15
- gem 'listen', '= 0.7.3'
16
14
  require 'listen'
17
15
  require 'ejs'
16
+ require 'uglifier'
17
+ require 'therubyracer'
18
+ require 'source_map'
18
19
 
19
20
  # Ichiban files. Order matters!
20
21
  require 'ichiban/bundle'
@@ -26,23 +27,21 @@ require 'ichiban/dependencies'
26
27
  require 'ichiban/loader'
27
28
  require 'ichiban/watcher'
28
29
  require 'ichiban/deleter'
29
- require 'ichiban/file'
30
+ require 'ichiban/project_file'
30
31
  require 'ichiban/helpers'
31
32
  require 'ichiban/nav_helper'
32
33
  require 'ichiban/html_compiler'
33
34
  require 'ichiban/asset_compiler'
35
+ require 'ichiban/js_compiler'
34
36
  require 'ichiban/ejs_compiler'
35
37
  require 'ichiban/markdown'
36
38
  require 'ichiban/scripts'
37
39
 
38
40
  module Ichiban
41
+ VERSION = '1.1.0'
42
+
39
43
  # In addition to setting the variable, this loads the config file
40
44
  def self.project_root=(path)
41
- unless @project_root == path
42
- # If we're changing the project root, then we need to clear all dependency graphs from memory.
43
- # This doesn't delete any files.
44
- Ichiban::Dependencies.clear_graphs
45
- end
46
45
  @project_root = path
47
46
  if path # It's valid to set project_root to nil, though this would likely only happen in tests
48
47
  Ichiban::Config.load_file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ichiban
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.13
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jarrett Colby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2012-11-29 00:00:00.000000000 Z
11
+ date: 2014-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ejs
@@ -16,75 +16,148 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 1.1.1
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '1'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '0'
29
+ version: 1.1.1
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: erubis
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - ">="
32
38
  - !ruby/object:Gem::Version
33
- version: '0'
39
+ version: 2.7.0
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '2'
34
43
  type: :runtime
35
44
  prerelease: false
36
45
  version_requirements: !ruby/object:Gem::Requirement
37
46
  requirements:
38
47
  - - ">="
39
48
  - !ruby/object:Gem::Version
40
- version: '0'
49
+ version: 2.7.0
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '2'
41
53
  - !ruby/object:Gem::Dependency
42
54
  name: sass
43
55
  requirement: !ruby/object:Gem::Requirement
44
56
  requirements:
45
57
  - - ">="
46
58
  - !ruby/object:Gem::Version
47
- version: '0'
59
+ version: 3.3.14
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '3'
48
63
  type: :runtime
49
64
  prerelease: false
50
65
  version_requirements: !ruby/object:Gem::Requirement
51
66
  requirements:
52
67
  - - ">="
53
68
  - !ruby/object:Gem::Version
54
- version: '0'
69
+ version: 3.3.14
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: '3'
55
73
  - !ruby/object:Gem::Dependency
56
74
  name: listen
57
75
  requirement: !ruby/object:Gem::Requirement
58
76
  requirements:
59
- - - '='
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 2.7.9
80
+ - - "~>"
60
81
  - !ruby/object:Gem::Version
61
- version: 0.7.3
82
+ version: '2'
62
83
  type: :runtime
63
84
  prerelease: false
64
85
  version_requirements: !ruby/object:Gem::Requirement
65
86
  requirements:
66
- - - '='
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 2.7.9
90
+ - - "~>"
67
91
  - !ruby/object:Gem::Version
68
- version: 0.7.3
92
+ version: '2'
69
93
  - !ruby/object:Gem::Dependency
70
94
  name: activesupport
71
95
  requirement: !ruby/object:Gem::Requirement
72
96
  requirements:
73
97
  - - ">="
74
98
  - !ruby/object:Gem::Version
75
- version: '0'
99
+ version: 4.1.5
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '4'
76
103
  type: :runtime
77
104
  prerelease: false
78
105
  version_requirements: !ruby/object:Gem::Requirement
79
106
  requirements:
80
107
  - - ">="
81
108
  - !ruby/object:Gem::Version
82
- version: '0'
109
+ version: 4.1.5
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '4'
83
113
  - !ruby/object:Gem::Dependency
84
114
  name: bundler
85
115
  requirement: !ruby/object:Gem::Requirement
86
116
  requirements:
87
117
  - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: 1.5.1
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '1'
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: 1.5.1
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '1'
133
+ - !ruby/object:Gem::Dependency
134
+ name: uglifier
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 2.5.3
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '2'
143
+ type: :runtime
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: 2.5.3
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '2'
153
+ - !ruby/object:Gem::Dependency
154
+ name: therubyracer
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: 0.12.1
160
+ - - "~>"
88
161
  - !ruby/object:Gem::Version
89
162
  version: '0'
90
163
  type: :runtime
@@ -92,41 +165,113 @@ dependencies:
92
165
  version_requirements: !ruby/object:Gem::Requirement
93
166
  requirements:
94
167
  - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: 0.12.1
170
+ - - "~>"
95
171
  - !ruby/object:Gem::Version
96
172
  version: '0'
97
173
  - !ruby/object:Gem::Dependency
98
- name: mocha
174
+ name: source_map
99
175
  requirement: !ruby/object:Gem::Requirement
100
176
  requirements:
101
177
  - - ">="
102
178
  - !ruby/object:Gem::Version
103
- version: '0'
179
+ version: 3.0.1
180
+ type: :runtime
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: 3.0.1
187
+ - !ruby/object:Gem::Dependency
188
+ name: rake
189
+ requirement: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: 10.3.2
194
+ - - "~>"
195
+ - !ruby/object:Gem::Version
196
+ version: '10'
104
197
  type: :development
105
198
  prerelease: false
106
199
  version_requirements: !ruby/object:Gem::Requirement
107
200
  requirements:
108
201
  - - ">="
109
202
  - !ruby/object:Gem::Version
110
- version: '0'
203
+ version: 10.3.2
204
+ - - "~>"
205
+ - !ruby/object:Gem::Version
206
+ version: '10'
111
207
  - !ruby/object:Gem::Dependency
112
- name: turn
208
+ name: minitest
113
209
  requirement: !ruby/object:Gem::Requirement
114
210
  requirements:
115
211
  - - ">="
116
212
  - !ruby/object:Gem::Version
117
- version: '0'
213
+ version: 5.4.1
214
+ - - "~>"
215
+ - !ruby/object:Gem::Version
216
+ version: '5'
118
217
  type: :development
119
218
  prerelease: false
120
219
  version_requirements: !ruby/object:Gem::Requirement
121
220
  requirements:
122
221
  - - ">="
123
222
  - !ruby/object:Gem::Version
124
- version: '0'
223
+ version: 5.4.1
224
+ - - "~>"
225
+ - !ruby/object:Gem::Version
226
+ version: '5'
227
+ - !ruby/object:Gem::Dependency
228
+ name: minitest-reporters
229
+ requirement: !ruby/object:Gem::Requirement
230
+ requirements:
231
+ - - ">="
232
+ - !ruby/object:Gem::Version
233
+ version: 1.0.5
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: '1'
237
+ type: :development
238
+ prerelease: false
239
+ version_requirements: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: 1.0.5
244
+ - - "~>"
245
+ - !ruby/object:Gem::Version
246
+ version: '1'
247
+ - !ruby/object:Gem::Dependency
248
+ name: mocha
249
+ requirement: !ruby/object:Gem::Requirement
250
+ requirements:
251
+ - - ">="
252
+ - !ruby/object:Gem::Version
253
+ version: 1.1.0
254
+ - - "~>"
255
+ - !ruby/object:Gem::Version
256
+ version: '1'
257
+ type: :development
258
+ prerelease: false
259
+ version_requirements: !ruby/object:Gem::Requirement
260
+ requirements:
261
+ - - ">="
262
+ - !ruby/object:Gem::Version
263
+ version: 1.1.0
264
+ - - "~>"
265
+ - !ruby/object:Gem::Version
266
+ version: '1'
125
267
  - !ruby/object:Gem::Dependency
126
268
  name: lorax
127
269
  requirement: !ruby/object:Gem::Requirement
128
270
  requirements:
129
271
  - - ">="
272
+ - !ruby/object:Gem::Version
273
+ version: 0.2.0
274
+ - - "~>"
130
275
  - !ruby/object:Gem::Version
131
276
  version: '0'
132
277
  type: :development
@@ -134,9 +279,32 @@ dependencies:
134
279
  version_requirements: !ruby/object:Gem::Requirement
135
280
  requirements:
136
281
  - - ">="
282
+ - !ruby/object:Gem::Version
283
+ version: 0.2.0
284
+ - - "~>"
137
285
  - !ruby/object:Gem::Version
138
286
  version: '0'
139
- description: Static website compiler with advanced feature, including watcher script.
287
+ - !ruby/object:Gem::Dependency
288
+ name: rdiscount
289
+ requirement: !ruby/object:Gem::Requirement
290
+ requirements:
291
+ - - ">="
292
+ - !ruby/object:Gem::Version
293
+ version: 2.1.7
294
+ - - "~>"
295
+ - !ruby/object:Gem::Version
296
+ version: '2'
297
+ type: :development
298
+ prerelease: false
299
+ version_requirements: !ruby/object:Gem::Requirement
300
+ requirements:
301
+ - - ">="
302
+ - !ruby/object:Gem::Version
303
+ version: 2.1.7
304
+ - - "~>"
305
+ - !ruby/object:Gem::Version
306
+ version: '2'
307
+ description: Static website compiler with advanced features, including watcher script.
140
308
  email: jarrett@madebyhq.com
141
309
  executables:
142
310
  - ichiban
@@ -144,6 +312,7 @@ extensions: []
144
312
  extra_rdoc_files: []
145
313
  files:
146
314
  - bin/ichiban
315
+ - empty_project/Gemfile
147
316
  - empty_project/assets/css/screen.scss
148
317
  - empty_project/assets/js/interaction.js
149
318
  - empty_project/assets/misc/readme.txt
@@ -168,13 +337,14 @@ files:
168
337
  - lib/ichiban/deleter.rb
169
338
  - lib/ichiban/dependencies.rb
170
339
  - lib/ichiban/ejs_compiler.rb
171
- - lib/ichiban/file.rb
172
340
  - lib/ichiban/helpers.rb
173
341
  - lib/ichiban/html_compiler.rb
342
+ - lib/ichiban/js_compiler.rb
174
343
  - lib/ichiban/loader.rb
175
344
  - lib/ichiban/logger.rb
176
345
  - lib/ichiban/markdown.rb
177
346
  - lib/ichiban/nav_helper.rb
347
+ - lib/ichiban/project_file.rb
178
348
  - lib/ichiban/project_generator.rb
179
349
  - lib/ichiban/scripts.rb
180
350
  - lib/ichiban/watcher.rb