kuntoaji-harmony 0.5.7 → 0.5.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +13 -125
- data/Rakefile +13 -2
- data/harmony.gemspec +3 -3
- data/lib/harmony/page.rb +51 -40
- data/lib/harmony/version.rb +1 -1
- data/spec/page_spec.rb +142 -0
- data/spec/spec_helper.rb +8 -0
- metadata +11 -5
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Harmony
|
1
|
+
Harmony - the memory reduced version ...
|
2
2
|
=======
|
3
3
|
|
4
4
|
.,ad88888888baa,
|
@@ -21,89 +21,19 @@ Harmony
|
|
21
21
|
""88888888888888888P"
|
22
22
|
""""""""""""
|
23
23
|
|
24
|
-
|
24
|
+
Why this new version of an already great gem?
|
25
25
|
-------
|
26
26
|
|
27
|
-
Harmony
|
27
|
+
We, at Cohuman, have been using Harmony in our Rspec text suite for javascript unit testing. For performance reasons, the original Harmony gem instantiated one Johnson js runtime per Harmony universe. Envjs was loaded into the runtime once and then each page was instantiated and loaded into this singleton runtime. This lead to a memory explosion since none of the pages created in our test suite are released until the entire suite finished running. Our 700 tests were taking over a gig of memory :(
|
28
28
|
|
29
|
-
|
30
|
-
--------
|
31
|
-
|
32
|
-
### Simple Javascript Parsing
|
33
|
-
|
34
|
-
require 'harmony'
|
35
|
-
|
36
|
-
page = Harmony::Page.new(<<-HTML)
|
37
|
-
<html>
|
38
|
-
<head>
|
39
|
-
<title>Foo</title>
|
40
|
-
</head>
|
41
|
-
<body></body>
|
42
|
-
</html>
|
43
|
-
HTML
|
44
|
-
|
45
|
-
page.execute_js("1+1") #=> 2
|
46
|
-
page.execute_js("document.title") #=> "Foo"
|
47
|
-
|
48
|
-
The Page object's `#execute_js` method (aliased as `#x` for convenience) takes a
|
49
|
-
string of javascript code, executes it and returns the last statement's value
|
50
|
-
(just like a ruby method).
|
51
|
-
|
52
|
-
### Javascript Unit Tests
|
53
|
-
|
54
|
-
One interesting use of Harmony is to test your javascript code within your ruby
|
55
|
-
application's own tests (test/unit, minitest, RSpec, nanotest, etc). Which
|
56
|
-
consequently means that you can now run browser-less, fully command-line
|
57
|
-
based, DOM-javascript tests.
|
58
|
-
|
59
|
-
require 'test/unit'
|
60
|
-
require 'harmony'
|
61
|
-
|
62
|
-
class JavascriptTest < Test::Unit::TestCase
|
63
|
-
def setup
|
64
|
-
@page = Harmony::Page.new
|
65
|
-
@page.load('public/javascripts/foo.js')
|
66
|
-
end
|
67
|
-
|
68
|
-
def test_foo
|
69
|
-
assert_equal "world", @page.execute_js(<<-JS)
|
70
|
-
foo = new Foo;
|
71
|
-
foo.hello();
|
72
|
-
JS
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
### DOM Handling
|
77
|
-
|
78
|
-
Don't be affraid to throw in your favorite client-side js framework, like
|
79
|
-
JQuery or Prototype. And notice that scripts linked to in `<script>` tags will
|
80
|
-
automatically get pulled in.
|
81
|
-
|
82
|
-
require 'harmony'
|
83
|
-
|
84
|
-
page = Harmony::Page.new(<<-HTML)
|
85
|
-
<html>
|
86
|
-
<head>
|
87
|
-
<script src="javascripts/jquery.js" type="text/javascript"></script>
|
88
|
-
</head>
|
89
|
-
<body>
|
90
|
-
<div id="widget">ohaie</div>
|
91
|
-
</body>
|
92
|
-
</html>
|
93
|
-
HTML
|
94
|
-
|
95
|
-
page.execute_js("$('#widget').innerHTML") #=> "ohaie"
|
29
|
+
This is an experimental version of Harmony that instantiates a new Johnson runtime with Envjs for each page created. This means that we maintain the unit test isolation that Harmony pages provide while allowing each page to be garbage collected after the test is run. The penalty is, of course, performance. Creating a new Johnson runtime and loading it with the Envjs methods takes time.
|
96
30
|
|
97
|
-
|
31
|
+
Ideally, Harmony should allow configuration for performance or memory optimization. Should there be demand we will make it happen!
|
98
32
|
|
99
|
-
Use `Harmony::Page.fetch(uri)` to create a page from a remote document.
|
100
33
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
page.execute_js('document.title') #=> "Example Web Page"
|
105
|
-
|
106
|
-
`fetch` also accepts "file://" uris.
|
34
|
+
Get the original, and learn more about the API
|
35
|
+
--------
|
36
|
+
http://github.com/mynyml/harmony
|
107
37
|
|
108
38
|
Install
|
109
39
|
-------
|
@@ -113,50 +43,8 @@ Install
|
|
113
43
|
gem install stackdeck
|
114
44
|
gem install johnson -v "2.0.0.pre3"
|
115
45
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
* [holygrail][20]: Harmony plugin for Rails tests
|
122
|
-
|
123
|
-
Acknowledgement
|
124
|
-
---------------
|
125
|
-
|
126
|
-
Harmony is a thin DSL wrapper around three **amazing** libs, [Johnson][1],
|
127
|
-
[env.js][30] and [Envjs][2] . The authors of those libs have been doing a huge
|
128
|
-
amount of great work for quite a while, so please go recommend them on
|
129
|
-
WorkingWithRails right now and/or follow them on github:
|
130
|
-
|
131
|
-
[jbarnette][3], [tenderlove][4], [smparkes][5], [wycats][6], [matthewd][7], [thatcher][8], [jeresig][9]
|
132
|
-
|
133
|
-
Special thanks go to [smparkes][10] for his patient help, and for providing the
|
134
|
-
last puzzle pieces that made [everything][12] [work][11] [together][13].
|
135
|
-
|
136
|
-
Links
|
137
|
-
-----
|
138
|
-
* code: <http://github.com/mynyml/harmony>
|
139
|
-
* docs: <http://yardoc.org/docs/mynyml-harmony>
|
140
|
-
* wiki: <http://wiki.github.com/mynyml/harmony>
|
141
|
-
* bugs: <http://github.com/mynyml/harmony/issues>
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
YinYang ASCII art is © Normand Veilleux (nveilleuATemr1.emrDOTca)
|
146
|
-
|
147
|
-
|
148
|
-
[1]: http://github.com/jbarnette/johnson/
|
149
|
-
[2]: http://env-js.appspot.com/
|
150
|
-
[3]: http://www.workingwithrails.com/person/10668-john-barnette
|
151
|
-
[4]: http://github.com/tenderlove/
|
152
|
-
[5]: http://www.workingwithrails.com/person/11739-steven-parkes
|
153
|
-
[6]: http://www.workingwithrails.com/person/1805-yehuda-katz
|
154
|
-
[7]: http://www.workingwithrails.com/person/6221-matthew-draper
|
155
|
-
[8]: http://github.com/thatcher/
|
156
|
-
[9]: http://ejohn.org/
|
157
|
-
[10]: http://github.com/smparkes/
|
158
|
-
[11]: http://github.com/smparkes/env-js/commit/49abe259813a505b0761e6d31dde671344b5bc87#L0R279
|
159
|
-
[12]: http://groups.google.com/group/envjs/msg/4ac719f7db7912f5
|
160
|
-
[13]: http://gemcutter.org/gems/envjs
|
161
|
-
[20]: http://github.com/mynyml/holygrail
|
162
|
-
[30]: http://github.com/thatcher/env-js
|
46
|
+
# download this version of the gem and install it manually:
|
47
|
+
git clone http://github.com/baccigalupi/harmony.git
|
48
|
+
cd harmony
|
49
|
+
sudo rake install
|
50
|
+
|
data/Rakefile
CHANGED
@@ -8,10 +8,21 @@ end
|
|
8
8
|
# --------------------------------------------------
|
9
9
|
# Tests
|
10
10
|
# --------------------------------------------------
|
11
|
-
task(:default => "test:all")
|
12
11
|
|
13
|
-
|
12
|
+
# new rspec version
|
13
|
+
begin
|
14
|
+
require 'spec/rake/spectask'
|
15
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
16
|
+
spec.libs << 'lib' << 'spec'
|
17
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
18
|
+
end
|
19
|
+
task :default => :spec
|
20
|
+
rescue
|
21
|
+
task :default => "test:all"
|
22
|
+
end
|
14
23
|
|
24
|
+
# original minitest version
|
25
|
+
namespace(:test) do
|
15
26
|
desc "Run all tests"
|
16
27
|
task(:all) do
|
17
28
|
exit system("ruby #{gem_opt} -I.:lib:test -e'%w( #{Dir['test/**/*_test.rb'].join(' ')} ).each {|p| require p }'")
|
data/harmony.gemspec
CHANGED
@@ -6,11 +6,11 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "kuntoaji-harmony"
|
7
7
|
s.version = Harmony::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
|
-
s.authors = ["mynyml", "Kunto Aji Kristianto"]
|
10
|
-
s.email = ["mynyml@gmail.com", "kunto.aji.kr@gmail.com"]
|
9
|
+
s.authors = ["mynyml", "Kunto Aji Kristianto", "baccigalupi"]
|
10
|
+
s.email = ["mynyml@gmail.com", "kunto.aji.kr@gmail.com", "baccigalupi@gmail.com"]
|
11
11
|
s.homepage = "http://github.com/kuntoaji/harmony"
|
12
12
|
s.summary = %q{Javascript + DOM in your ruby, the simple way}
|
13
|
-
s.description = %q{Javascript + DOM in your ruby, the simple way. Kuntoaji's version.}
|
13
|
+
s.description = %q{Javascript + DOM in your ruby, the simple way. More memory sensitive based on baccigalupi's repos. Kuntoaji's version.}
|
14
14
|
|
15
15
|
#s.rubyforge_project = ""
|
16
16
|
|
data/lib/harmony/page.rb
CHANGED
@@ -1,40 +1,20 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'tempfile'
|
3
|
+
require 'forwardable'
|
3
4
|
|
4
5
|
require 'johnson/tracemonkey'
|
5
6
|
require 'envjs/runtime'
|
6
7
|
|
7
8
|
module Harmony
|
8
9
|
class Page
|
9
|
-
|
10
|
-
#
|
10
|
+
# DOM document's `window` object. Equivalent to the return value of
|
11
|
+
# `page.execute_js('window')`
|
11
12
|
#
|
12
|
-
# @
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# Envjs::Runtime is extended) takes a while, so we only want to do this
|
18
|
-
# once.
|
19
|
-
#
|
20
|
-
# @private
|
21
|
-
BASE_RUNTIME = Johnson::Runtime.new
|
22
|
-
BASE_RUNTIME.extend(Envjs::Runtime)
|
23
|
-
|
24
|
-
def from_uri(uri)
|
25
|
-
BASE_RUNTIME.evaluate("window.open('#{uri}')")
|
26
|
-
end
|
27
|
-
|
28
|
-
def from_document(document)
|
29
|
-
Tempfile.open('harmony') {|f| f << document; @path = f.path }
|
30
|
-
from_uri("file://#{@path}")
|
31
|
-
end
|
32
|
-
|
33
|
-
def blank
|
34
|
-
from_uri('about:blank')
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
13
|
+
# @return [Object]
|
14
|
+
# window DOM object
|
15
|
+
#
|
16
|
+
attr_reader :window
|
17
|
+
|
38
18
|
# Create page from remote document.
|
39
19
|
#
|
40
20
|
# @example
|
@@ -51,7 +31,7 @@ module Harmony
|
|
51
31
|
#
|
52
32
|
def self.fetch(uri)
|
53
33
|
page = new
|
54
|
-
page.
|
34
|
+
page.window.open(uri)
|
55
35
|
page
|
56
36
|
end
|
57
37
|
|
@@ -62,7 +42,7 @@ module Harmony
|
|
62
42
|
# structure: `<html><head><title></title></head><body></body></html>`
|
63
43
|
#
|
64
44
|
def initialize(document=nil)
|
65
|
-
@window = Window.from_document(document)
|
45
|
+
@window = document ? Window.from_document(document) : Window.blank
|
66
46
|
end
|
67
47
|
|
68
48
|
# Load one or more javascript files in page's context
|
@@ -93,16 +73,6 @@ module Harmony
|
|
93
73
|
end
|
94
74
|
alias :x :execute_js
|
95
75
|
|
96
|
-
# DOM document's `window` object. Equivalent to the return value of
|
97
|
-
# `page.execute_js('window')`
|
98
|
-
#
|
99
|
-
# @return [Object]
|
100
|
-
# window DOM object
|
101
|
-
#
|
102
|
-
def window
|
103
|
-
@window ||= Window.blank
|
104
|
-
end
|
105
|
-
|
106
76
|
# Convenience method, equivalent to the return value of
|
107
77
|
# `page.execute_js('window.document')`
|
108
78
|
#
|
@@ -120,6 +90,47 @@ module Harmony
|
|
120
90
|
def to_html
|
121
91
|
document.innerHTML
|
122
92
|
end
|
93
|
+
|
94
|
+
# Window factory
|
95
|
+
#
|
96
|
+
# @private
|
97
|
+
class Window
|
98
|
+
extend Forwardable
|
99
|
+
|
100
|
+
attr_reader :run_time
|
101
|
+
def_delegators :@run_time, :evaluate, :load
|
102
|
+
def_delegators :@browser, :document
|
103
|
+
|
104
|
+
def initialize(uri='about:blank')
|
105
|
+
open(uri)
|
106
|
+
end
|
107
|
+
|
108
|
+
def run_time
|
109
|
+
unless @run_time
|
110
|
+
@run_time = Johnson::Runtime.new
|
111
|
+
@run_time.extend Envjs::Runtime
|
112
|
+
end
|
113
|
+
@run_time
|
114
|
+
end
|
115
|
+
|
116
|
+
def open(uri)
|
117
|
+
@browser = run_time.evaluate("window.open('#{uri}')")
|
118
|
+
run_time
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.from_uri(uri)
|
122
|
+
new(uri)
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.from_document(document)
|
126
|
+
Tempfile.open('harmony') {|f| f << document; @path = f.path }
|
127
|
+
new("file://#{@path}")
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.blank
|
131
|
+
new
|
132
|
+
end
|
133
|
+
end
|
123
134
|
end
|
124
135
|
end
|
125
136
|
|
data/lib/harmony/version.rb
CHANGED
data/spec/page_spec.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
Page = Harmony::Page unless defined?( Page )
|
4
|
+
|
5
|
+
describe Harmony::Page do
|
6
|
+
before do
|
7
|
+
@blank = Page.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'api' do
|
11
|
+
CLASS_METHODS = [:fetch, :new]
|
12
|
+
INSTANCE_METHODS = [:window, :document, :execute_js, :x]
|
13
|
+
|
14
|
+
CLASS_METHODS.each do |page_method|
|
15
|
+
it "Page should respond to #{page_method}" do
|
16
|
+
Page.should respond_to(page_method)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
INSTANCE_METHODS.each do |page_method|
|
21
|
+
it "Page instance should respond to #{page_method}" do
|
22
|
+
@blank.should respond_to(page_method)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'Page.document should be a shortcut for Page.window.document' do
|
27
|
+
window = mock('Window')
|
28
|
+
@blank.should_receive(:window).and_return(window)
|
29
|
+
window.should_receive(:document)
|
30
|
+
@blank.document
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'execution' do
|
35
|
+
it 'performs basic js' do
|
36
|
+
@blank.x('5+2').should == 7
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'executes DOM-accessing js' do
|
40
|
+
page = Page.new(<<-HTML)
|
41
|
+
<html>
|
42
|
+
<head>
|
43
|
+
<title>Harmony</title>
|
44
|
+
</head>
|
45
|
+
<body>
|
46
|
+
<div></div>
|
47
|
+
<div></div>
|
48
|
+
</body>
|
49
|
+
</html>
|
50
|
+
HTML
|
51
|
+
page.document.title.should == 'Harmony'
|
52
|
+
|
53
|
+
(page.x(<<-JS)
|
54
|
+
document.getElementsByTagName('div').length
|
55
|
+
JS
|
56
|
+
).should == 2
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'basic characteristics' do
|
61
|
+
it "Page fetches documents from remote locations" do
|
62
|
+
path = tempfile(<<-HTML)
|
63
|
+
<html><head><title>foo</title></head><body></body></html>
|
64
|
+
HTML
|
65
|
+
page = Page.fetch("file://#{path}")
|
66
|
+
page.document.title.should == 'foo'
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'default empty page' do
|
70
|
+
it "has no title" do
|
71
|
+
@blank.document.title.should be_empty
|
72
|
+
end
|
73
|
+
|
74
|
+
it '#to_html should produce an empty document' do
|
75
|
+
@blank.to_html.should == "<html><head><title></title></head><body></body></html>"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'loading js files' do
|
81
|
+
it "works with one one path" do
|
82
|
+
path = tempfile(<<-JS)
|
83
|
+
function foo() { return 'bar' };
|
84
|
+
JS
|
85
|
+
|
86
|
+
page = Page.new.load(path)
|
87
|
+
page.x('foo()').should == 'bar'
|
88
|
+
end
|
89
|
+
|
90
|
+
it "takes an array of paths" do
|
91
|
+
paths = []
|
92
|
+
paths << tempfile(<<-JS)
|
93
|
+
function foo() { return 'bar' };
|
94
|
+
JS
|
95
|
+
|
96
|
+
paths << tempfile(<<-JS)
|
97
|
+
function moo() { return 'boo' };
|
98
|
+
JS
|
99
|
+
|
100
|
+
page = Page.new.load(paths)
|
101
|
+
page.x('foo()').should == 'bar'
|
102
|
+
page.x('moo()').should == 'boo'
|
103
|
+
end
|
104
|
+
|
105
|
+
it "can load multiple files with splat" do
|
106
|
+
paths = []
|
107
|
+
paths << tempfile(<<-JS)
|
108
|
+
function foo() { return 'bar' };
|
109
|
+
JS
|
110
|
+
|
111
|
+
paths << tempfile(<<-JS)
|
112
|
+
function moo() { return 'boo' };
|
113
|
+
JS
|
114
|
+
|
115
|
+
page = Page.new.load(*paths)
|
116
|
+
page.x('foo()').should == 'bar'
|
117
|
+
page.x('moo()').should == 'boo'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe 'document context' do
|
122
|
+
it 'should use a different window for each page' do
|
123
|
+
@blank.window.should_not === Page.new.window
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should use a different runtime for each page' do
|
127
|
+
@blank.window.run_time.should_not === Page.new.window.run_time
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should keep a window/browser run time within the same page' do
|
131
|
+
@blank.window.run_time.should == @blank.window.run_time
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def tempfile(content)
|
136
|
+
Tempfile.open('abc') do |f|
|
137
|
+
f << content
|
138
|
+
@__path = f.path
|
139
|
+
end
|
140
|
+
@__path
|
141
|
+
end
|
142
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kuntoaji-harmony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 0.5.
|
9
|
+
- 8
|
10
|
+
version: 0.5.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- mynyml
|
14
14
|
- Kunto Aji Kristianto
|
15
|
+
- baccigalupi
|
15
16
|
autorequire:
|
16
17
|
bindir: bin
|
17
18
|
cert_chain: []
|
18
19
|
|
19
|
-
date: 2011-02-
|
20
|
+
date: 2011-02-20 00:00:00 +07:00
|
20
21
|
default_executable:
|
21
22
|
dependencies:
|
22
23
|
- !ruby/object:Gem::Dependency
|
@@ -67,10 +68,11 @@ dependencies:
|
|
67
68
|
version: "0"
|
68
69
|
type: :development
|
69
70
|
version_requirements: *id003
|
70
|
-
description: Javascript + DOM in your ruby, the simple way. Kuntoaji's version.
|
71
|
+
description: Javascript + DOM in your ruby, the simple way. More memory sensitive based on baccigalupi's repos. Kuntoaji's version.
|
71
72
|
email:
|
72
73
|
- mynyml@gmail.com
|
73
74
|
- kunto.aji.kr@gmail.com
|
75
|
+
- baccigalupi@gmail.com
|
74
76
|
executables: []
|
75
77
|
|
76
78
|
extensions: []
|
@@ -89,6 +91,8 @@ files:
|
|
89
91
|
- lib/harmony.rb
|
90
92
|
- lib/harmony/page.rb
|
91
93
|
- lib/harmony/version.rb
|
94
|
+
- spec/page_spec.rb
|
95
|
+
- spec/spec_helper.rb
|
92
96
|
- specs.watchr
|
93
97
|
- test/harmony_test.rb
|
94
98
|
- test/page_test.rb
|
@@ -128,6 +132,8 @@ signing_key:
|
|
128
132
|
specification_version: 3
|
129
133
|
summary: Javascript + DOM in your ruby, the simple way
|
130
134
|
test_files:
|
135
|
+
- spec/page_spec.rb
|
136
|
+
- spec/spec_helper.rb
|
131
137
|
- test/harmony_test.rb
|
132
138
|
- test/page_test.rb
|
133
139
|
- test/test_helper.rb
|