livereload 1.2.2 → 1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/bin/livereload +47 -5
  2. data/lib/livereload.rb +245 -25
  3. metadata +18 -5
@@ -1,9 +1,51 @@
1
1
  #! /usr/bin/env ruby
2
2
  require 'rubygems'
3
3
  require 'livereload'
4
+ require 'optparse'
4
5
 
5
- dir = File.expand_path('.')
6
- exts = "html,css,js,png,gif,jpg,php,py,rb,erb"
7
- host = "0.0.0.0"
8
- port = 10083
9
- LiveReload.run host, port, dir, exts
6
+ def parse_command_line_config(args)
7
+ LiveReload::Config.new do |config|
8
+
9
+ opts = OptionParser.new do |opts|
10
+ opts.banner = "Usage: livereload [options] [directory1 directory2 ...]"
11
+
12
+ opts.separator ""
13
+ opts.separator "Browser communication options:"
14
+
15
+ opts.on("--address [ADDRESS]", "Network interface to listen on (default is 0.0.0.0)") do |v|
16
+ config.host = v
17
+ end
18
+
19
+ opts.on("--port [PORT]", Integer, "TCP port to listen on (default is 10083)") do |v|
20
+ config.port = v.to_i
21
+ end
22
+
23
+ opts.separator ""
24
+ opts.separator "Debugging options:"
25
+
26
+ opts.on("-D", "--[no-]debug", "Print debugging info") do |v|
27
+ config.debug = v
28
+ end
29
+
30
+ opts.separator ""
31
+ opts.separator "Common options:"
32
+
33
+ opts.on_tail("-h", "--help", "Show this message") do
34
+ puts opts
35
+ exit
36
+ end
37
+
38
+ opts.on_tail("--version", "Show version") do
39
+ puts LiveReload::GEM_VERSION
40
+ exit
41
+ end
42
+ end
43
+
44
+ opts.parse!(args)
45
+ end
46
+ end
47
+
48
+ explicit_config = parse_command_line_config(ARGV)
49
+
50
+ directories = if ARGV.size == 0 then ['.'] else ARGV end
51
+ LiveReload.run directories, explicit_config
@@ -1,40 +1,260 @@
1
1
  require 'em-websocket'
2
2
  require 'directory_watcher'
3
+ require 'json/objects'
3
4
 
4
5
  # Chrome sometimes sends HTTP/1.0 requests in violation of WebSockets spec
6
+ # hide the warning about redifinition of a constant
7
+ saved_stderr = $stderr
8
+ $stderr = StringIO.new
5
9
  EventMachine::WebSocket::HandlerFactory::PATH = /^(\w+) (\/[^\s]*) HTTP\/1\.[01]$/
10
+ $stderr = saved_stderr
11
+
12
+ class Object
13
+ def method_missing_with_livereload id, *args, &block
14
+ if id == :config
15
+ Object.send(:instance_variable_get, '@livereload_config')
16
+ else
17
+ method_missing_without_livereload id, *args, &block
18
+ end
19
+ end
20
+ end
6
21
 
7
22
  module LiveReload
8
- VERSION = "1.2"
9
-
10
- def self.start_watching ws
11
- $dw.add_observer { |*args|
12
- args.each { |event|
13
- if event[:type] == :modified
14
- name = File.basename(event[:path])
15
- puts "Modified: #{name}"
16
- ws.send name
17
- end
18
- }
19
- }
20
- $dw.start
23
+ GEM_VERSION = "1.3"
24
+ API_VERSION = "1.3"
25
+
26
+ PROJECT_CONFIG_FILE_TEMPLATE = <<-END.strip.split("\n").collect { |line| line.strip + "\n" }.join("")
27
+ # Lines starting with pound sign (#) are ignored.
28
+
29
+ # additional extensions to monitor
30
+ #config.exts << 'haml'
31
+
32
+ # exclude files with NAMES matching this mask
33
+ #config.exclusions << '~*'
34
+ # exclude files with PATHS matching this mask (if the mask contains a slash)
35
+ #config.exclusions << '/excluded_dir/*'
36
+ # exclude files with PATHS matching this REGEXP
37
+ #config.exclusions << /somedir.*(ab){2,4}\.(css|js)$/
38
+
39
+ # reload the whole page when .js changes
40
+ #config.apply_js_live = false
41
+ # reload the whole page when .css changes
42
+ #config.apply_css_live = false
43
+ END
44
+
45
+ # note that host and port options do not make sense in per-project config files
46
+ class Config
47
+ attr_accessor :host, :port, :exts, :exclusions, :debug, :apply_js_live, :apply_css_live
48
+
49
+ def initialize &block
50
+ @host = nil
51
+ @port = nil
52
+ @debug = nil
53
+ @exts = []
54
+ @exclusions = []
55
+ @apply_js_live = nil
56
+ @apply_css_live = nil
57
+
58
+ update!(&block) if block
59
+ end
60
+
61
+ def update!
62
+ yield self
63
+
64
+ # remove leading dots
65
+ @exts = @exts.collect { |e| e.sub(/^\./, '') }
66
+ end
67
+
68
+ def merge! other
69
+ @host = other.host if other.host
70
+ @port = other.port if other.port
71
+ @exts += other.exts
72
+ @exclusions = other.exclusions + @exclusions
73
+ @debug = other.debug if other.debug != nil
74
+ @apply_js_live = other.apply_js_live if other.apply_js_live != nil
75
+ @apply_css_live = other.apply_css_live if other.apply_css_live != nil
76
+
77
+ self
78
+ end
79
+
80
+ class << self
81
+ def load_from file
82
+ Config.new do |config|
83
+ if File.file? file
84
+ Object.send(:instance_variable_set, '@livereload_config', config)
85
+ Object.send(:alias_method, :method_missing_without_livereload, :method_missing)
86
+ Object.send(:alias_method, :method_missing, :method_missing_with_livereload)
87
+ load file, true
88
+ Object.send(:alias_method, :method_missing, :method_missing_without_livereload)
89
+ Object.send(:instance_variable_set, '@livereload_config', nil)
90
+ end
91
+ end
92
+ end
93
+
94
+ def merge *configs
95
+ configs.reduce(Config.new) { |merged, config| config && merged.merge!(config) || merged }
96
+ end
97
+ end
98
+ end
99
+
100
+ DEFAULT_CONFIG = Config.new do |config|
101
+ config.debug = false
102
+ config.host = '0.0.0.0'
103
+ config.port = 10083
104
+ config.exts = %w/html css js png gif jpg php php5 py rb erb/
105
+ config.exclusions = %w!*/.git/* */.svn/* */.hg/*!
106
+ config.apply_js_live = true
107
+ config.apply_css_live = true
108
+ end
109
+
110
+ USER_CONFIG_FILE = File.expand_path("~/.livereload")
111
+ USER_CONFIG = Config.load_from(USER_CONFIG_FILE)
112
+
113
+ class Project
114
+ attr_reader :config
115
+
116
+ def initialize directory, explicit_config=nil
117
+ @directory = directory
118
+ @explicit_config = explicit_config
119
+ read_config
120
+ end
121
+
122
+ def read_config
123
+ project_config_file = File.join(@directory, '.livereload')
124
+ unless File.file? project_config_file
125
+ File.open(project_config_file, 'w') do |file|
126
+ file.write PROJECT_CONFIG_FILE_TEMPLATE
127
+ end
128
+ end
129
+ project_config = Config.load_from project_config_file
130
+ @config = Config.merge(DEFAULT_CONFIG, USER_CONFIG, project_config, @explicit_config)
131
+ end
132
+
133
+ def print_config
134
+ puts "Watching: #{@directory}"
135
+ puts " - extensions: " + @config.exts.collect {|e| ".#{e}"}.join(" ")
136
+ if !@config.apply_js_live && !@config.apply_css_live
137
+ puts " - live refreshing disabled for .css & .js: will reload the whole page on every change"
138
+ elsif !@config.apply_js_live
139
+ puts " - live refreshing disabled for .js: will reload the whole page when .js is changed"
140
+ elsif !@config.apply_css_live
141
+ puts " - live refreshing disabled for .css: will reload the whole page when .css is changed"
142
+ end
143
+ if @config.exclusions.size > 0
144
+ puts " - excluding changes in: " + @config.exclusions.join(" ")
145
+ end
146
+ end
147
+
148
+ def is_excluded? path
149
+ basename = File.basename(path)
150
+ @config.exclusions.any? do |exclusion|
151
+ if Regexp === exclusion
152
+ path =~ exclusion
153
+ elsif exclusion.include? '/'
154
+ File.fnmatch?(File.join(@directory, exclusion), path)
155
+ else
156
+ File.fnmatch?(exclusion, basename)
157
+ end
158
+ end
159
+ end
160
+
161
+ def when_changes_detected &block
162
+ @when_changes_detected = block
163
+ end
164
+
165
+ def restart_watching
166
+ if @dw
167
+ @dw.stop
168
+ end
169
+ @dw = DirectoryWatcher.new @directory, :glob => "{.livereload,**/*.{#{@config.exts.join(',')}}}", :scanner => :em, :pre_load => true
170
+ @dw.add_observer do |*args|
171
+ begin
172
+ args.each do |event|
173
+ path = event[:path]
174
+ if File.basename(path) == '.livereload'
175
+ @when_changes_detected.call [:config_changed, path]
176
+ elsif event[:type] == :modified
177
+ @when_changes_detected.call [if is_excluded?(path) then :excluded else :modified end, path]
178
+ end
179
+ end
180
+ rescue
181
+ puts $!
182
+ puts $!.backtrace
183
+ end
184
+ end
185
+ @dw.start
186
+ end
187
+ end
188
+
189
+ def self.configure
190
+ yield Config
21
191
  end
22
192
 
23
- def self.run(host, port, dir, exts)
193
+ def self.run(directories, explicit_config)
194
+ # EventMachine needs to run kqueue for the watching API to work
24
195
  EM.kqueue = true if EM.kqueue?
25
- # Could someone with more knowledge of glob specify that the ".git" directory should not be searched
26
- $dw = DirectoryWatcher.new dir, :glob => "**/*.{#{exts}}", :scanner => :em
196
+
197
+ web_sockets = []
198
+
199
+ # for host and port
200
+ global_config = Config.merge(DEFAULT_CONFIG, USER_CONFIG, explicit_config)
201
+ directories = directories.collect { |directory| File.expand_path(directory) }
202
+ projects = directories.collect { |directory| Project.new(directory, explicit_config) }
27
203
 
28
204
  puts
29
- puts "Version: #{VERSION}"
30
- puts "Port: #{port}"
31
- puts "Directory: #{dir}"
32
- puts "Extensions: " + exts.split(",").collect {|e| ".#{e}"}.join(" ")
33
- puts "LiveReload is waiting for browser to connect."
34
- EventMachine::WebSocket.start(:host => host, :port => port, :debug => false) do |ws|
35
- ws.onopen { puts "Browser connected."; ws.send "!!ver:#{VERSION}"; start_watching ws }
36
- ws.onmessage { |msg| puts "Browser URL: #{msg}" }
37
- ws.onclose { puts "Browser disconnected." }
205
+ puts "Version: #{GEM_VERSION} (compatible with browser extension versions #{API_VERSION}.x)"
206
+ puts "Port: #{global_config.port}"
207
+ projects.each { |project| project.print_config }
208
+
209
+ EventMachine.run do
210
+ projects.each do |project|
211
+ project.when_changes_detected do |event, modified_file|
212
+ case event
213
+ when :config_changed
214
+ puts
215
+ puts ">> Configuration change: " + modified_file
216
+ puts
217
+ EventMachine.next_tick do
218
+ projects.each { |project| project.read_config; project.print_config }
219
+ puts
220
+ projects.each { |project| project.restart_watching }
221
+ end
222
+ when :excluded
223
+ puts "Excluded: #{File.basename(modified_file)}"
224
+ when :modified
225
+ puts "Modified: #{File.basename(modified_file)}"
226
+ data = ['refresh', { :path => modified_file,
227
+ :apply_js_live => project.config.apply_js_live,
228
+ :apply_css_live => project.config.apply_css_live }].to_json
229
+ puts data if global_config.debug
230
+ web_sockets.each do |ws|
231
+ ws.send data
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ projects.each { |project| project.restart_watching }
238
+
239
+ puts
240
+ puts "LiveReload is waiting for browser to connect."
241
+ EventMachine::WebSocket.start(:host => global_config.host, :port => global_config.port, :debug => global_config.debug) do |ws|
242
+ ws.onopen do
243
+ begin
244
+ puts "Browser connected."; ws.send "!!ver:#{API_VERSION}"; web_sockets << ws
245
+ rescue
246
+ puts $!
247
+ puts $!.backtrace
248
+ end
249
+ end
250
+ ws.onmessage do |msg|
251
+ puts "Browser URL: #{msg}"
252
+ end
253
+ ws.onclose do
254
+ web_sockets.delete ws
255
+ puts "Browser disconnected."
256
+ end
257
+ end
38
258
  end
39
259
  end
40
260
  end
metadata CHANGED
@@ -4,9 +4,8 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
- - 2
8
- - 2
9
- version: 1.2.2
7
+ - 3
8
+ version: "1.3"
10
9
  platform: ruby
11
10
  authors:
12
11
  - Andrey Tarantsov
@@ -45,7 +44,21 @@ dependencies:
45
44
  version: 1.3.2
46
45
  type: :runtime
47
46
  version_requirements: *id002
48
- description: A command-line server for the LiveReload Safari extension
47
+ - !ruby/object:Gem::Dependency
48
+ name: ruby-json
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 1
56
+ - 1
57
+ - 2
58
+ version: 1.1.2
59
+ type: :runtime
60
+ version_requirements: *id003
61
+ description: " LiveReload is a Safari/Chrome extension + a command-line tool that:\n 1. Applies CSS and JavaScript file changes without reloading a page.\n 2. Automatically reloads a page when any other file changes (html, image, server-side script, etc).\n"
49
62
  email: andreyvit@gmail.com
50
63
  executables:
51
64
  - livereload
@@ -85,6 +98,6 @@ rubyforge_project:
85
98
  rubygems_version: 1.3.6
86
99
  signing_key:
87
100
  specification_version: 3
88
- summary: A command-line server for the LiveReload Safari extension
101
+ summary: A command-line server for the LiveReload Safari/Chrome extension
89
102
  test_files: []
90
103