ichiban 1.0.13 → 1.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.
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