rack-livejs 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ language: ruby
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gemspec
6
6
  gem "rake", "~> 10.0.4"
7
7
 
8
8
  group :test do
9
+ gem "nokogiri"
9
10
  gem "rack-test", "~> 0.6.2"
10
11
  gem "rspec", "~> 2.13.0"
11
12
  end
@@ -0,0 +1 @@
1
+ require "rack/livejs"
@@ -1,5 +1,3 @@
1
- require "rack/livejs/version"
2
-
3
1
  module Rack
4
2
  class Livejs
5
3
 
@@ -10,34 +8,42 @@ module Rack
10
8
  end
11
9
 
12
10
  def call(env)
13
- self.dup._call(env)
14
- end
15
-
16
- protected
17
-
18
- def _call(env)
11
+ return livejs_response if env["PATH_INFO"] == livejs_path
19
12
  status, headers, body = @app.call(env)
20
13
  if headers["Content-Type"] == "text/html"
21
- html = read_all(body)
22
- modified_html = inject_tag_into(html)
23
- headers["Content-Length"] = modified_html.size
24
- body = [modified_html]
14
+ new_html = inject_livejs(read_body(body))
15
+ headers["Content-Length"] = new_html.size.to_s
16
+ body = [new_html]
25
17
  end
26
18
  [status, headers, body]
27
19
  end
28
20
 
29
- def read_all(body)
30
- html = ""
31
- body.each { |fragment| html << fragment }
32
- html
21
+ private
22
+
23
+ def livejs_path
24
+ "/_rack_livejs_/live.js"
25
+ end
26
+
27
+ def read_body(body)
28
+ "".tap do |buffer|
29
+ body.each { |chunk| buffer << chunk }
30
+ end
31
+ end
32
+
33
+ def inject_livejs(html)
34
+ html.sub(%r{(<head( [^>]*)?>)}i) { $1 + %{<script src="#{livejs_path}"/>} }
35
+ end
36
+
37
+ def livejs_javascript
38
+ @@livejs_javascript ||= ::File.read(::File.expand_path("../../../vendor/livejs/live.js", __FILE__))
33
39
  end
34
40
 
35
- def inject_tag_into(html)
36
- doc = Nokogiri::HTML(html)
37
- head = doc.css("html head").first
38
- return html unless head
39
- head.add_child(%{<script src="http://livejs.com/live.js"/>})
40
- doc.to_html
41
+ def livejs_response
42
+ [
43
+ 200,
44
+ { 'Content-Type' => "application/javascript", 'Content-Length' => livejs_javascript.size.to_s },
45
+ [ livejs_javascript ]
46
+ ]
41
47
  end
42
48
 
43
49
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Livejs
3
- VERSION = "0.0.1"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_runtime_dependency "nokogiri", ">= 1.4.0"
22
21
  spec.add_development_dependency "bundler", "~> 1.3"
23
22
 
24
23
  end
@@ -2,7 +2,6 @@ require 'nokogiri'
2
2
  require 'rack'
3
3
  require 'rack/livejs'
4
4
  require 'rack/test'
5
- require 'rspec'
6
5
 
7
6
  describe Rack::Livejs do
8
7
 
@@ -13,7 +12,7 @@ describe Rack::Livejs do
13
12
  Rack::Directory.new(fixtures_dir)
14
13
  end
15
14
 
16
- let(:app) { described_class.new(decorated_app) }
15
+ let(:app) { Rack::Lint.new(described_class.new(decorated_app)) }
17
16
 
18
17
  describe "getting an HTML file" do
19
18
 
@@ -31,7 +30,7 @@ describe Rack::Livejs do
31
30
  it "injects the live.js script" do
32
31
  script_tag = html.css("head script").first
33
32
  expect(script_tag).to_not be_nil
34
- expect(script_tag[:src]).to eq("http://livejs.com/live.js")
33
+ expect(script_tag[:src]).to eq("/_rack_livejs_/live.js")
35
34
  end
36
35
 
37
36
  end
@@ -56,4 +55,20 @@ describe Rack::Livejs do
56
55
 
57
56
  end
58
57
 
58
+ describe "getting live.js" do
59
+
60
+ before do
61
+ get "/_rack_livejs_/live.js"
62
+ end
63
+
64
+ it "returns the live.js script" do
65
+ expect(last_response).to be_ok
66
+ expect(last_response.content_type).to eq "application/javascript"
67
+ root = Pathname(__FILE__).parent.parent.parent
68
+ live_js_script = root + "vendor/livejs/live.js"
69
+ expect(last_response.body).to eq(live_js_script.read)
70
+ end
71
+
72
+ end
73
+
59
74
  end
@@ -0,0 +1,22 @@
1
+ MIT License http://en.wikipedia.org/wiki/MIT_License
2
+ IN SHORT: It's free. You can use it commercially. Don't sue us.
3
+
4
+ Copyright (C) 2011 by Martin Kool and Q42
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE
@@ -0,0 +1,233 @@
1
+ /*
2
+ Live.js - One script closer to Designing in the Browser
3
+ Written for Handcraft.com by Martin Kool (@mrtnkl).
4
+
5
+ Version 4.
6
+ Recent change: Made stylesheet and mimetype checks case insensitive.
7
+
8
+ http://livejs.com
9
+ http://livejs.com/license (MIT)
10
+ @livejs
11
+
12
+ Include live.js#css to monitor css changes only.
13
+ Include live.js#js to monitor js changes only.
14
+ Include live.js#html to monitor html changes only.
15
+ Mix and match to monitor a preferred combination such as live.js#html,css
16
+
17
+ By default, just include live.js to monitor all css, js and html changes.
18
+
19
+ Live.js can also be loaded as a bookmarklet. It is best to only use it for CSS then,
20
+ as a page reload due to a change in html or css would not re-include the bookmarklet.
21
+ To monitor CSS and be notified that it has loaded, include it as: live.js#css,notify
22
+ */
23
+ (function () {
24
+
25
+ var headers = { "Etag": 1, "Last-Modified": 1, "Content-Length": 1, "Content-Type": 1 },
26
+ resources = {},
27
+ pendingRequests = {},
28
+ currentLinkElements = {},
29
+ oldLinkElements = {},
30
+ interval = 1000,
31
+ loaded = false,
32
+ active = { "html": 1, "css": 1, "js": 1 };
33
+
34
+ var Live = {
35
+
36
+ // performs a cycle per interval
37
+ heartbeat: function () {
38
+ if (document.body) {
39
+ // make sure all resources are loaded on first activation
40
+ if (!loaded) Live.loadresources();
41
+ Live.checkForChanges();
42
+ }
43
+ setTimeout(Live.heartbeat, interval);
44
+ },
45
+
46
+ // loads all local css and js resources upon first activation
47
+ loadresources: function () {
48
+
49
+ // helper method to assert if a given url is local
50
+ function isLocal(url) {
51
+ var loc = document.location,
52
+ reg = new RegExp("^\\.|^\/(?!\/)|^[\\w]((?!://).)*$|" + loc.protocol + "//" + loc.host);
53
+ return url.match(reg);
54
+ }
55
+
56
+ // gather all resources
57
+ var scripts = document.getElementsByTagName("script"),
58
+ links = document.getElementsByTagName("link"),
59
+ uris = [];
60
+
61
+ // track local js urls
62
+ for (var i = 0; i < scripts.length; i++) {
63
+ var script = scripts[i], src = script.getAttribute("src");
64
+ if (src && isLocal(src))
65
+ uris.push(src);
66
+ if (src && src.match(/\blive.js#/)) {
67
+ for (var type in active)
68
+ active[type] = src.match("[#,|]" + type) != null
69
+ if (src.match("notify"))
70
+ alert("Live.js is loaded.");
71
+ }
72
+ }
73
+ if (!active.js) uris = [];
74
+ if (active.html) uris.push(document.location.href);
75
+
76
+ // track local css urls
77
+ for (var i = 0; i < links.length && active.css; i++) {
78
+ var link = links[i], rel = link.getAttribute("rel"), href = link.getAttribute("href", 2);
79
+ if (href && rel && rel.match(new RegExp("stylesheet", "i")) && isLocal(href)) {
80
+ uris.push(href);
81
+ currentLinkElements[href] = link;
82
+ }
83
+ }
84
+
85
+ // initialize the resources info
86
+ for (var i = 0; i < uris.length; i++) {
87
+ var url = uris[i];
88
+ Live.getHead(url, function (url, info) {
89
+ resources[url] = info;
90
+ });
91
+ }
92
+
93
+ // add rule for morphing between old and new css files
94
+ var head = document.getElementsByTagName("head")[0],
95
+ style = document.createElement("style"),
96
+ rule = "transition: all .3s ease-out;"
97
+ css = [".livejs-loading * { ", rule, " -webkit-", rule, "-moz-", rule, "-o-", rule, "}"].join('');
98
+ style.setAttribute("type", "text/css");
99
+ head.appendChild(style);
100
+ style.styleSheet ? style.styleSheet.cssText = css : style.appendChild(document.createTextNode(css));
101
+
102
+ // yep
103
+ loaded = true;
104
+ },
105
+
106
+ // check all tracking resources for changes
107
+ checkForChanges: function () {
108
+ for (var url in resources) {
109
+ if (pendingRequests[url])
110
+ continue;
111
+
112
+ Live.getHead(url, function (url, newInfo) {
113
+ var oldInfo = resources[url],
114
+ hasChanged = false;
115
+ resources[url] = newInfo;
116
+ for (var header in oldInfo) {
117
+ // do verification based on the header type
118
+ var oldValue = oldInfo[header],
119
+ newValue = newInfo[header],
120
+ contentType = newInfo["Content-Type"];
121
+ switch (header.toLowerCase()) {
122
+ case "etag":
123
+ if (!newValue) break;
124
+ // fall through to default
125
+ default:
126
+ hasChanged = oldValue != newValue;
127
+ break;
128
+ }
129
+ // if changed, act
130
+ if (hasChanged) {
131
+ Live.refreshResource(url, contentType);
132
+ break;
133
+ }
134
+ }
135
+ });
136
+ }
137
+ },
138
+
139
+ // act upon a changed url of certain content type
140
+ refreshResource: function (url, type) {
141
+ switch (type.toLowerCase()) {
142
+ // css files can be reloaded dynamically by replacing the link element
143
+ case "text/css":
144
+ var link = currentLinkElements[url],
145
+ html = document.body.parentNode,
146
+ head = link.parentNode,
147
+ next = link.nextSibling,
148
+ newLink = document.createElement("link");
149
+
150
+ html.className = html.className.replace(/\s*livejs\-loading/gi, '') + ' livejs-loading';
151
+ newLink.setAttribute("type", "text/css");
152
+ newLink.setAttribute("rel", "stylesheet");
153
+ newLink.setAttribute("href", url + "?now=" + new Date() * 1);
154
+ next ? head.insertBefore(newLink, next) : head.appendChild(newLink);
155
+ currentLinkElements[url] = newLink;
156
+ oldLinkElements[url] = link;
157
+
158
+ // schedule removal of the old link
159
+ Live.removeoldLinkElements();
160
+ break;
161
+
162
+ // check if an html resource is our current url, then reload
163
+ case "text/html":
164
+ if (url != document.location.href)
165
+ return;
166
+
167
+ // local javascript changes cause a reload as well
168
+ case "text/javascript":
169
+ case "application/javascript":
170
+ case "application/x-javascript":
171
+ document.location.reload();
172
+ }
173
+ },
174
+
175
+ // removes the old stylesheet rules only once the new one has finished loading
176
+ removeoldLinkElements: function () {
177
+ var pending = 0;
178
+ for (var url in oldLinkElements) {
179
+ // if this sheet has any cssRules, delete the old link
180
+ try {
181
+ var link = currentLinkElements[url],
182
+ oldLink = oldLinkElements[url],
183
+ html = document.body.parentNode,
184
+ sheet = link.sheet || link.styleSheet,
185
+ rules = sheet.rules || sheet.cssRules;
186
+ if (rules.length >= 0) {
187
+ oldLink.parentNode.removeChild(oldLink);
188
+ delete oldLinkElements[url];
189
+ setTimeout(function () {
190
+ html.className = html.className.replace(/\s*livejs\-loading/gi, '');
191
+ }, 100);
192
+ }
193
+ } catch (e) {
194
+ pending++;
195
+ }
196
+ if (pending) setTimeout(Live.removeoldLinkElements, 50);
197
+ }
198
+ },
199
+
200
+ // performs a HEAD request and passes the header info to the given callback
201
+ getHead: function (url, callback) {
202
+ pendingRequests[url] = true;
203
+ var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XmlHttp");
204
+ xhr.open("HEAD", url, true);
205
+ xhr.onreadystatechange = function () {
206
+ delete pendingRequests[url];
207
+ if (xhr.readyState == 4 && xhr.status != 304) {
208
+ xhr.getAllResponseHeaders();
209
+ var info = {};
210
+ for (var h in headers) {
211
+ var value = xhr.getResponseHeader(h);
212
+ // adjust the simple Etag variant to match on its significant part
213
+ if (h.toLowerCase() == "etag" && value) value = value.replace(/^W\//, '');
214
+ if (h.toLowerCase() == "content-type" && value) value = value.replace(/^(.*?);.*?$/i, "$1");
215
+ info[h] = value;
216
+ }
217
+ callback(url, info);
218
+ }
219
+ }
220
+ xhr.send();
221
+ }
222
+ };
223
+
224
+ // start listening
225
+ if (document.location.protocol != "file:") {
226
+ if (!window.liveJsLoaded)
227
+ Live.heartbeat();
228
+
229
+ window.liveJsLoaded = true;
230
+ }
231
+ else if (window.console)
232
+ console.log("Live.js doesn't support the file protocol. It needs http.");
233
+ })();
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-livejs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,24 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-05 00:00:00.000000000 Z
12
+ date: 2013-06-23 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: nokogiri
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: 1.4.0
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: 1.4.0
30
14
  - !ruby/object:Gem::Dependency
31
15
  name: bundler
32
16
  requirement: !ruby/object:Gem::Requirement
@@ -52,16 +36,20 @@ extra_rdoc_files: []
52
36
  files:
53
37
  - .gitignore
54
38
  - .rspec
39
+ - .travis.yml
55
40
  - Gemfile
56
41
  - LICENSE.txt
57
42
  - README.md
58
43
  - Rakefile
44
+ - lib/rack-livejs.rb
59
45
  - lib/rack/livejs.rb
60
46
  - lib/rack/livejs/version.rb
61
47
  - rack-livejs.gemspec
62
48
  - spec/fixtures/page.html
63
49
  - spec/fixtures/text.txt
64
50
  - spec/rack/livejs_spec.rb
51
+ - vendor/livejs/LICENSE.txt
52
+ - vendor/livejs/live.js
65
53
  homepage: http://github.com/mdub/rack-livejs
66
54
  licenses:
67
55
  - MIT
@@ -77,7 +65,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
65
  version: '0'
78
66
  segments:
79
67
  - 0
80
- hash: -132143474654823442
68
+ hash: 2181008550174536603
81
69
  required_rubygems_version: !ruby/object:Gem::Requirement
82
70
  none: false
83
71
  requirements:
@@ -86,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
74
  version: '0'
87
75
  segments:
88
76
  - 0
89
- hash: -132143474654823442
77
+ hash: 2181008550174536603
90
78
  requirements: []
91
79
  rubyforge_project:
92
80
  rubygems_version: 1.8.23