livereload 1.2.2 → 1.3

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.
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