matryoshka 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +18 -0
- data/Rakefile +15 -0
- data/config.ru +7 -0
- data/lib/matryoshka.rb +33 -0
- data/lib/matryoshka/answer.rb +158 -0
- data/lib/matryoshka/data.rb +41 -0
- data/lib/matryoshka/document.rb +41 -0
- data/lib/matryoshka/document/delegate.rb +19 -0
- data/lib/matryoshka/document/html.rb +171 -0
- data/lib/matryoshka/document/html/merge.rb +201 -0
- data/lib/matryoshka/document/unknown.rb +13 -0
- data/lib/matryoshka/external.rb +9 -0
- data/matryoshka.gemspec +33 -0
- data/public/README +2 -0
- data/templates/image.jpg +0 -0
- data/templates/index.html +15 -0
- data/templates/proxy/index.html +3 -0
- data/templates/test/about.html +12 -0
- data/templates/test/index.html +8 -0
- metadata +93 -0
data/Manifest
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Manifest
|
2
|
+
Rakefile
|
3
|
+
config.ru
|
4
|
+
lib/matryoshka.rb
|
5
|
+
lib/matryoshka/answer.rb
|
6
|
+
lib/matryoshka/data.rb
|
7
|
+
lib/matryoshka/document.rb
|
8
|
+
lib/matryoshka/document/delegate.rb
|
9
|
+
lib/matryoshka/document/html.rb
|
10
|
+
lib/matryoshka/document/html/merge.rb
|
11
|
+
lib/matryoshka/document/unknown.rb
|
12
|
+
lib/matryoshka/external.rb
|
13
|
+
public/README
|
14
|
+
templates/image.jpg
|
15
|
+
templates/index.html
|
16
|
+
templates/proxy/index.html
|
17
|
+
templates/test/about.html
|
18
|
+
templates/test/index.html
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Rakefile
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'echoe'
|
5
|
+
|
6
|
+
Echoe.new('matryoshka', '0.0.1') do |p|
|
7
|
+
p.description = "Rack middleware for parsing html templates."
|
8
|
+
p.url = "http://samsm.com/"
|
9
|
+
p.author = "Sam Schenkman-Moore"
|
10
|
+
p.email = "samsm@samsm.com"
|
11
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
12
|
+
p.development_dependencies = ['hpricot']
|
13
|
+
end
|
14
|
+
|
15
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/config.ru
ADDED
data/lib/matryoshka.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class Matryoshka
|
2
|
+
ROOT_DIR = File.expand_path(File.dirname(__FILE__) + '/../')
|
3
|
+
|
4
|
+
def self.mrequire(filename)
|
5
|
+
Kernel.require ROOT_DIR + "/lib/matryoshka/#{filename}"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Matryoshka.mrequire 'answer'
|
10
|
+
Matryoshka.mrequire 'data'
|
11
|
+
Matryoshka.mrequire 'document'
|
12
|
+
Matryoshka.mrequire 'document/delegate'
|
13
|
+
Matryoshka.mrequire 'document/html'
|
14
|
+
Matryoshka.mrequire 'document/html/merge'
|
15
|
+
Matryoshka.mrequire 'document/unknown'
|
16
|
+
Matryoshka.mrequire 'external'
|
17
|
+
|
18
|
+
|
19
|
+
class Matryoshka
|
20
|
+
def initialize(app = nil)
|
21
|
+
@application = app
|
22
|
+
end
|
23
|
+
|
24
|
+
def application
|
25
|
+
@application
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(rack_env)
|
29
|
+
# puts rack_env['REQUEST_URI']
|
30
|
+
Answer.new(rack_env, application).to_rack
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
class Matryoshka::Answer
|
2
|
+
|
3
|
+
attr_accessor :env, :chained_application, :data
|
4
|
+
|
5
|
+
def initialize(rack_env, chained_application)
|
6
|
+
self.env = rack_env
|
7
|
+
self.chained_application = chained_application
|
8
|
+
|
9
|
+
self.data = Matryoshka::Data.new(env['HTTP_HOST'])
|
10
|
+
end
|
11
|
+
|
12
|
+
def requested_page
|
13
|
+
@requested_page ||=
|
14
|
+
if chained_application # Rails or other Framework handles page
|
15
|
+
handler.new(chained_application.call(env))
|
16
|
+
else
|
17
|
+
local_template or
|
18
|
+
proxy_template or
|
19
|
+
not_found_template
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def local_template
|
24
|
+
return false unless template = load_template(path_with_index)
|
25
|
+
if template.proxy?
|
26
|
+
context_templates << template
|
27
|
+
proxy_template
|
28
|
+
else
|
29
|
+
template
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def proxy_template
|
35
|
+
top_context_template = context_templates.last
|
36
|
+
if (top_context_template and top_context_template.proxy?)
|
37
|
+
proxy = top_context_template.prepare_proxy(path)
|
38
|
+
proxy
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def not_found_template
|
43
|
+
# For now ...
|
44
|
+
handler.new [404,{},'<div id="content">404\'d!</div>']
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_rack
|
48
|
+
# can we handle this request? if not, just pass it along
|
49
|
+
return requested_page.call(env) unless matryoshkable?
|
50
|
+
|
51
|
+
# gather the files necessary for this request
|
52
|
+
# merge them together
|
53
|
+
# merged_context # referenced below
|
54
|
+
requested_page
|
55
|
+
|
56
|
+
result = if merged_context
|
57
|
+
merge(merged_context, requested_page)
|
58
|
+
else
|
59
|
+
requested_page
|
60
|
+
end
|
61
|
+
|
62
|
+
result.call(env)
|
63
|
+
end
|
64
|
+
|
65
|
+
def path
|
66
|
+
env['REQUEST_URI']
|
67
|
+
end
|
68
|
+
|
69
|
+
def path_without_extention
|
70
|
+
path.sub(/\.\w+\Z/,'')
|
71
|
+
end
|
72
|
+
|
73
|
+
def path_with_index
|
74
|
+
if path[/\/\Z/] # path ends with /
|
75
|
+
path + 'index'
|
76
|
+
else
|
77
|
+
path
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def merge(a,b)
|
82
|
+
handler.new [
|
83
|
+
b.status,
|
84
|
+
b.headers,
|
85
|
+
handler::Merge.new(a,b).run
|
86
|
+
]
|
87
|
+
end
|
88
|
+
|
89
|
+
def merged_context
|
90
|
+
@merged_context ||=
|
91
|
+
context_templates.inject do |result, template|
|
92
|
+
result = merge(result,template)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def context_paths(path_to_context_array = path)
|
97
|
+
segments = path_to_context_array.split('/')
|
98
|
+
|
99
|
+
# Remove the last element (the requested page)
|
100
|
+
# Empty array only happens on '/'. No context. Shortcut out.
|
101
|
+
return [] unless segments.pop
|
102
|
+
|
103
|
+
paths = []
|
104
|
+
|
105
|
+
# ['',1,2,3] becomes ['/','/1','/1/2','/1/2/3']
|
106
|
+
segments.each_with_index do |segment,index|
|
107
|
+
paths << segments[0..index].join('/')
|
108
|
+
end
|
109
|
+
|
110
|
+
paths # paths.collect {|p| p + '/index.html'}
|
111
|
+
end
|
112
|
+
|
113
|
+
# Get file_data and full_path from here for a given context
|
114
|
+
def context_templates(opts = {})
|
115
|
+
return @context_templates if @context_templates
|
116
|
+
options = {:path_array => context_paths,
|
117
|
+
:file_name => 'index',
|
118
|
+
:current_handler => handler}.merge(opts)
|
119
|
+
path_array = options[:path_array]
|
120
|
+
@context_templates = path_array.collect do |p|
|
121
|
+
unless "#{p}/#{options[:file_name]}" == path_without_extention
|
122
|
+
load_template("#{p}/#{options[:file_name]}", options[:current_handler])
|
123
|
+
end
|
124
|
+
end.compact
|
125
|
+
end
|
126
|
+
|
127
|
+
# The request path, and the current handler
|
128
|
+
def load_template(extention_optional_path,current_handler = handler)
|
129
|
+
rack_arr = data.find extention_optional_path, current_handler.extentions
|
130
|
+
if rack_arr
|
131
|
+
current_handler.new(rack_arr)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def handler
|
136
|
+
matryoshka_handler or fallback_handler
|
137
|
+
end
|
138
|
+
|
139
|
+
def matryoshkable?
|
140
|
+
matryoshka_handler
|
141
|
+
end
|
142
|
+
|
143
|
+
def matryoshka_handler
|
144
|
+
# handler = Matryoshka::Document.detect_handler(@env)
|
145
|
+
# handler.new(nil) if handler
|
146
|
+
Matryoshka::Document.detect_handler(env)
|
147
|
+
end
|
148
|
+
|
149
|
+
# This is for when a request makes it here, but cannot be handled.
|
150
|
+
def fallback_handler
|
151
|
+
Matryoshka::Document::Unknown
|
152
|
+
end
|
153
|
+
|
154
|
+
def delegate_handler
|
155
|
+
Matryoshka::Document::Delegate
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Matryoshka::Data
|
2
|
+
def initialize(*args)
|
3
|
+
# args = whatever is necessary to find correct dir
|
4
|
+
# Right now that means nothing
|
5
|
+
end
|
6
|
+
|
7
|
+
def expand(path)
|
8
|
+
if File.directory?('./templates')
|
9
|
+
'./templates' + path
|
10
|
+
else
|
11
|
+
Matryoshka::ROOT_DIR + '/templates' + path
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def find(path, extentions = [''])
|
16
|
+
path = strip_extention(path)
|
17
|
+
extentions.each do |extention|
|
18
|
+
path_with_extention = "#{path}#{extention}"
|
19
|
+
if exists? path_with_extention
|
20
|
+
return [200,
|
21
|
+
{:template_path => path_with_extention },
|
22
|
+
read(path_with_extention)
|
23
|
+
]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
false # No results found.
|
27
|
+
end
|
28
|
+
|
29
|
+
def read(path_with_extention)
|
30
|
+
IO.read(expand(path_with_extention))
|
31
|
+
end
|
32
|
+
|
33
|
+
def exists?(path)
|
34
|
+
File.exists? expand(path)
|
35
|
+
end
|
36
|
+
|
37
|
+
# This may not be smart.
|
38
|
+
def strip_extention(path)
|
39
|
+
path.sub(/\.[^.]{1,4}\Z/,'')
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Matryoshka::Document
|
2
|
+
|
3
|
+
def self.detect_handler(env)
|
4
|
+
|
5
|
+
accepts_header = env['HTTP_ACCEPT']
|
6
|
+
request_path = env['REQUEST_URI']
|
7
|
+
|
8
|
+
detect_using_extention(request_path) or
|
9
|
+
detect_using_accepts(accepts_header)
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.detect(method,matcher)
|
14
|
+
document_classes.each do |klass|
|
15
|
+
klass.send(method).each do |detection_test|
|
16
|
+
if matcher =~ Regexp.new(Regexp.escape(detection_test))
|
17
|
+
return klass
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# nothing matched, so ...
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.detect_using_extention(path)
|
27
|
+
detect :extentions, File.extname(path)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.detect_using_accepts(accepts_header)
|
31
|
+
detect :accepts, accepts_header
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.document_classes
|
35
|
+
constants.collect do |const|
|
36
|
+
klass = const_get(const)
|
37
|
+
klass if klass.kind_of?(Class)
|
38
|
+
end.compact
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Matryoshka::Document::Delegate
|
2
|
+
attr_accessor :application
|
3
|
+
|
4
|
+
def initialize(app)
|
5
|
+
self.application = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
application.class(env)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.extentions
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.accepts
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
gem 'hpricot'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
class Matryoshka::Document::Html
|
5
|
+
|
6
|
+
attr_accessor :body, :content, :path, :status, :headers
|
7
|
+
|
8
|
+
def externals_loaded?
|
9
|
+
@externals_loaded
|
10
|
+
end
|
11
|
+
|
12
|
+
def proxy_loaded?
|
13
|
+
@proxy_loaded
|
14
|
+
end
|
15
|
+
|
16
|
+
EXTERNAL_SEARCH_PATTERN = '[@rel="external"]'
|
17
|
+
PROXY_SEARCH_PATTERN = '[@rel="proxy"]'
|
18
|
+
|
19
|
+
def proxy?
|
20
|
+
content.at PROXY_SEARCH_PATTERN
|
21
|
+
end
|
22
|
+
|
23
|
+
def prepare_proxy(full_request_path = '')
|
24
|
+
element = content.at PROXY_SEARCH_PATTERN
|
25
|
+
proxy_link = if full_request_path == @headers[:template_path]
|
26
|
+
element[:href]
|
27
|
+
else
|
28
|
+
closest_directory(element[:href]) +
|
29
|
+
full_request_path.sub(/\A#{closest_directory(@headers[:template_path])}/,'')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Need to prime external with template_path
|
33
|
+
external = self.class.new(Matryoshka::External.find(proxy_link, {:template_path => @headers[:template_path]}))
|
34
|
+
# external.make_links_absolute(element[:href])
|
35
|
+
base_dir = closest_directory(element[:href])
|
36
|
+
|
37
|
+
# if doc is not a full (<html><head> ...) don't bother with transform?
|
38
|
+
|
39
|
+
# merge the external with a copy of the element
|
40
|
+
# convoluted copy element (.dup is weird with Hpricot)
|
41
|
+
element_copy = Hpricot(element.to_html).at '*'
|
42
|
+
external.content = Merge.new(element_copy,external.content,Merge::EXTERNAL_MERGE).run
|
43
|
+
external.convert_proxy_links(base_dir)
|
44
|
+
external.make_links_absolute(base_dir,{'img'=>'src'})
|
45
|
+
|
46
|
+
# Tag the element and the proxy doc so that they cleanly merge.
|
47
|
+
# This is to keep the pattern established in answer.rb
|
48
|
+
element.set_attribute('id', 'matryoshka_proxy_target')
|
49
|
+
external.content.set_attribute('id','matryoshka_proxy_target')
|
50
|
+
external
|
51
|
+
end
|
52
|
+
|
53
|
+
def closest_directory(path)
|
54
|
+
path[/\A.+\//]
|
55
|
+
end
|
56
|
+
|
57
|
+
def convert_proxy_links(proxy_base)
|
58
|
+
(content/'a').each do |link|
|
59
|
+
href = link.attributes['href']
|
60
|
+
|
61
|
+
# Remove base of urls that we are proxying.
|
62
|
+
href.sub!(/\A#{proxy_base}/,'')
|
63
|
+
|
64
|
+
# Except for absolute links, add template path to complete relative urls
|
65
|
+
unless true #is_absolute?(href)
|
66
|
+
if href[/\A\//] # link begins with /
|
67
|
+
href = '?'
|
68
|
+
else
|
69
|
+
href = closest_directory(@headers[:template_path]) + href
|
70
|
+
end
|
71
|
+
link.set_attribute('href', href)
|
72
|
+
end
|
73
|
+
|
74
|
+
case href
|
75
|
+
when /\A#{proxy_base}/
|
76
|
+
href.sub!(/#{proxy_base}/,'').sub!(/\A\//,'')
|
77
|
+
when /\A#{URI.parse(proxy_base).path}/
|
78
|
+
href.sub!(/#{URI.parse(proxy_base).path}/,'').sub!(/\A\//,'')
|
79
|
+
end
|
80
|
+
|
81
|
+
unless is_absolute?(href)
|
82
|
+
href = closest_directory(@headers[:template_path]) + href
|
83
|
+
end
|
84
|
+
|
85
|
+
link.set_attribute('href', href)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def load_externals
|
91
|
+
(content/EXTERNAL_SEARCH_PATTERN).each do |element|
|
92
|
+
external = self.class.new(Matryoshka::External.find(element[:href]))
|
93
|
+
external.make_links_absolute(element[:href])
|
94
|
+
if external.status == 200
|
95
|
+
Merge.new(element,external.content,Merge::EXTERNAL_MERGE).run
|
96
|
+
# Cleanup external code
|
97
|
+
element.remove_attribute(:href)
|
98
|
+
element.remove_attribute(:rel)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
@externals_loaded = true
|
102
|
+
end
|
103
|
+
|
104
|
+
def make_links_absolute(base_url, replace_pairs = {'a'=>'href', 'img'=>'src'})
|
105
|
+
base_dir = base_url[/\A.+\//]
|
106
|
+
replace_pairs.each_pair do |search_pattern, attribute|
|
107
|
+
(content/search_pattern).each do |link|
|
108
|
+
absolute_link link, attribute, base_dir
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def is_absolute?(uri)
|
114
|
+
uri[/\Ahttp:\/\//]
|
115
|
+
end
|
116
|
+
|
117
|
+
def absolute_link(elem,attribute,base_dir)
|
118
|
+
relative_uri = elem.attributes[attribute]
|
119
|
+
unless (relative_uri.include?(base_dir) or is_absolute?(relative_uri))
|
120
|
+
elem.set_attribute(attribute, (base_dir + relative_uri))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def initialize(rack_array)
|
125
|
+
self.status = rack_array[0]
|
126
|
+
self.headers = rack_array[1]
|
127
|
+
|
128
|
+
html = rack_array[2]
|
129
|
+
if html.respond_to?(:to_html)
|
130
|
+
self.content = html
|
131
|
+
else
|
132
|
+
self.body = html
|
133
|
+
end
|
134
|
+
load_externals unless externals_loaded?
|
135
|
+
end
|
136
|
+
|
137
|
+
def content
|
138
|
+
@content ||= Hpricot(body)
|
139
|
+
end
|
140
|
+
|
141
|
+
def body
|
142
|
+
@content ? content.to_html : @body
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.extentions
|
146
|
+
['.html','.htm']
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.accepts
|
150
|
+
['text/html']
|
151
|
+
end
|
152
|
+
|
153
|
+
def status
|
154
|
+
@status or 200
|
155
|
+
end
|
156
|
+
|
157
|
+
# this needs to update headers like 'Content-Length' and remove Matryoshka-specific ones
|
158
|
+
# Might need to make this cleaned_headers or something
|
159
|
+
def headers
|
160
|
+
@headers.
|
161
|
+
reject {|k,v| k.is_a? Symbol}.
|
162
|
+
merge({"Content-Type" => "text/html",
|
163
|
+
'Content-Length' => content.to_html.length.to_s})
|
164
|
+
end
|
165
|
+
|
166
|
+
def call(env)
|
167
|
+
# content = IO.read Matryoshka::ROOT_DIR + '/public/index.html'
|
168
|
+
[status, headers, body]
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
class Matryoshka::Document::Html::Merge
|
2
|
+
@@round = 0
|
3
|
+
|
4
|
+
DEFAULT_MERGE = [
|
5
|
+
{:empty=>:full},
|
6
|
+
{:doc=>:remerge_children},
|
7
|
+
{:id=>:replace},
|
8
|
+
{:before_id=>:before},
|
9
|
+
{:after_id=>:append},
|
10
|
+
{:default=>:end_of_tag}
|
11
|
+
]
|
12
|
+
|
13
|
+
EXTERNAL_MERGE = [
|
14
|
+
{:original_id => :replace_id},
|
15
|
+
{:original_selector => :selector},
|
16
|
+
{:nochildren=>:inside_body},
|
17
|
+
{:doc => :remerge_original_children},
|
18
|
+
# {:id=>:replace},
|
19
|
+
# {:parent=>:insert}, # This translate to before_id or after_id
|
20
|
+
{:all=>:remerge_children}
|
21
|
+
]
|
22
|
+
|
23
|
+
# original is the parent document
|
24
|
+
# additional is the document to be merged
|
25
|
+
# corresponding is the matching portion of the original doc
|
26
|
+
# methodologies are the techniques used to merge, in order
|
27
|
+
# [{matching_pattern => what_to_do_if_match}, ...]
|
28
|
+
attr_accessor :original, :additional, :corresponding, :methodologies
|
29
|
+
|
30
|
+
def initialize(orig,add, meth = DEFAULT_MERGE)
|
31
|
+
self.original = convert_to_parse_format(orig)
|
32
|
+
self.additional = convert_to_parse_format(add)
|
33
|
+
self.methodologies = meth
|
34
|
+
end
|
35
|
+
|
36
|
+
def run
|
37
|
+
# puts "Round #{@@round += 1}"
|
38
|
+
methodologies.each do |methodology|
|
39
|
+
methodology.each_pair do |find_method, merge_technique|
|
40
|
+
# puts "#{@@round} - Doing #{find_method}:#{merge_technique} on #{additional.to_html[0..50].gsub(/\n/,'')}"
|
41
|
+
# debugger if @@round < 2
|
42
|
+
if (mergeable? and corresponding_match(find_method))
|
43
|
+
send merge_technique
|
44
|
+
return original
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
# This wasn't in previous versions ... may be some reason to avoid.
|
49
|
+
return original
|
50
|
+
end
|
51
|
+
|
52
|
+
def corresponding_match(find_method)
|
53
|
+
self.corresponding = send(find_method)
|
54
|
+
end
|
55
|
+
|
56
|
+
def convert_to_parse_format(data)
|
57
|
+
if data.respond_to? :content # Html object
|
58
|
+
# This should already be in Hpricot
|
59
|
+
data.content
|
60
|
+
elsif data.respond_to? :to_html # Hpricot object
|
61
|
+
data
|
62
|
+
else # probably string or IO
|
63
|
+
Hpricot data
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Below are matchign and replacing methods
|
68
|
+
# Perhaps move them to a separate module later
|
69
|
+
def empty
|
70
|
+
original if original.inner_html.empty?
|
71
|
+
end
|
72
|
+
|
73
|
+
def full
|
74
|
+
corresponding.inner_html = additional.inner_html
|
75
|
+
end
|
76
|
+
|
77
|
+
def doc
|
78
|
+
# original unless additional.kind_of? Nokogiri::HTML::Element
|
79
|
+
original if additional.class == Hpricot::Doc
|
80
|
+
end
|
81
|
+
|
82
|
+
def remerge_children
|
83
|
+
additional.children.each do |elem|
|
84
|
+
# if elem.kind_of? Nokogiri::XML::Element
|
85
|
+
if elem.kind_of? Hpricot::Elem
|
86
|
+
self.class.new(corresponding,elem,methodologies).run
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
rescue
|
91
|
+
puts '*' * 75
|
92
|
+
puts "#{@@round} - Rescuing: #{additional.class} cannot be children'd"
|
93
|
+
puts "#{@@round} - #{additional}"
|
94
|
+
# debugger
|
95
|
+
end
|
96
|
+
|
97
|
+
def remerge_original_children
|
98
|
+
corresponding.children.each do |elem|
|
99
|
+
if elem.kind_of? Hpricot::Elem
|
100
|
+
self.class.new(elem, additional, methodologies).run
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Some external-specific merging methods
|
106
|
+
def original_id
|
107
|
+
original if additional.at("##{original.attributes['id']}") if original.attributes['id']
|
108
|
+
end
|
109
|
+
|
110
|
+
def replace_id
|
111
|
+
corresponding.swap additional.at("##{corresponding.attributes['id']}").to_html
|
112
|
+
end
|
113
|
+
|
114
|
+
def original_selector
|
115
|
+
if original.attributes['rel'] == 'selector'
|
116
|
+
original if additional.at(original.attributes['href'])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def selector
|
121
|
+
corresponding.swap additional.at(corresponding.attributes['href']).to_html
|
122
|
+
end
|
123
|
+
|
124
|
+
# End externa-specific merging methods
|
125
|
+
|
126
|
+
def id
|
127
|
+
original.at("##{additional.attributes['id']}") if additional.attributes['id']
|
128
|
+
end
|
129
|
+
|
130
|
+
def replace
|
131
|
+
corresponding.swap additional.to_html
|
132
|
+
end
|
133
|
+
|
134
|
+
## These are quickly tacked on.
|
135
|
+
|
136
|
+
def additional_id
|
137
|
+
additional.attributes['id'] if additional.respond_to? :attributes
|
138
|
+
end
|
139
|
+
|
140
|
+
def before_id
|
141
|
+
if additional_id
|
142
|
+
if additional_id.index('before__') == 0
|
143
|
+
original.at("##{additional_id.sub('before__','')}")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def before
|
149
|
+
corresponding.before(additional.to_html)
|
150
|
+
end
|
151
|
+
|
152
|
+
def after_id
|
153
|
+
if additional_id
|
154
|
+
if additional_id.index('after__') == 0
|
155
|
+
original.at("##{additional_id.sub('after__','')}")
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def append
|
161
|
+
corresponding.after(additional.to_html)
|
162
|
+
end
|
163
|
+
|
164
|
+
def default
|
165
|
+
original.at('body') or original
|
166
|
+
end
|
167
|
+
|
168
|
+
def end_of_tag
|
169
|
+
corresponding.inner_html = corresponding.inner_html + additional.to_html
|
170
|
+
end
|
171
|
+
|
172
|
+
def nochildren
|
173
|
+
# Fails on Hpricot::Docs
|
174
|
+
#
|
175
|
+
# if original.class == Hpricot::Doc
|
176
|
+
# return false
|
177
|
+
# end
|
178
|
+
original.children.each do |child|
|
179
|
+
if child.class == Hpricot::Elem
|
180
|
+
return false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
original.at('*')
|
184
|
+
end
|
185
|
+
|
186
|
+
def inside_body
|
187
|
+
corresponding.inner_html = begin
|
188
|
+
additional.at('body') or additional
|
189
|
+
end.to_html
|
190
|
+
end
|
191
|
+
|
192
|
+
def all
|
193
|
+
original
|
194
|
+
end
|
195
|
+
|
196
|
+
def mergeable?
|
197
|
+
acceptable_classes_for_merging = [Hpricot::Elem, Hpricot::Doc]
|
198
|
+
acceptable_classes_for_merging.include? additional.class
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Matryoshka::Document::Unknown
|
2
|
+
def initialize(*args)
|
3
|
+
end
|
4
|
+
|
5
|
+
# These never match.
|
6
|
+
def self.extentions ; [] ; end
|
7
|
+
def self.accepts ; [] ;end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
content = "Can't handle that kind of request."
|
11
|
+
[501, {"Content-Type" => "text/html", 'Content-Length' => content.length.to_s}, content]
|
12
|
+
end
|
13
|
+
end
|
data/matryoshka.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{matryoshka}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Sam Schenkman-Moore"]
|
9
|
+
s.date = %q{2009-11-04}
|
10
|
+
s.description = %q{Rack middleware for parsing html templates.}
|
11
|
+
s.email = %q{samsm@samsm.com}
|
12
|
+
s.extra_rdoc_files = ["lib/matryoshka.rb", "lib/matryoshka/answer.rb", "lib/matryoshka/data.rb", "lib/matryoshka/document.rb", "lib/matryoshka/document/delegate.rb", "lib/matryoshka/document/html.rb", "lib/matryoshka/document/html/merge.rb", "lib/matryoshka/document/unknown.rb", "lib/matryoshka/external.rb"]
|
13
|
+
s.files = ["Manifest", "Rakefile", "config.ru", "lib/matryoshka.rb", "lib/matryoshka/answer.rb", "lib/matryoshka/data.rb", "lib/matryoshka/document.rb", "lib/matryoshka/document/delegate.rb", "lib/matryoshka/document/html.rb", "lib/matryoshka/document/html/merge.rb", "lib/matryoshka/document/unknown.rb", "lib/matryoshka/external.rb", "public/README", "templates/image.jpg", "templates/index.html", "templates/proxy/index.html", "templates/test/about.html", "templates/test/index.html", "matryoshka.gemspec"]
|
14
|
+
s.homepage = %q{http://samsm.com/}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Matryoshka"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{matryoshka}
|
18
|
+
s.rubygems_version = %q{1.3.5}
|
19
|
+
s.summary = %q{Rack middleware for parsing html templates.}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
26
|
+
s.add_development_dependency(%q<hpricot>, [">= 0"])
|
27
|
+
else
|
28
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
29
|
+
end
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
32
|
+
end
|
33
|
+
end
|
data/public/README
ADDED
data/templates/image.jpg
ADDED
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div id="content">
|
2
|
+
This is from /test/about.html
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<div id="test">
|
6
|
+
Test div! Should appear above the "this is from" message thanks to a second replace (shows that remerge children probably works).
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div id="before__test">This should come before the test div.</div>
|
10
|
+
<div id="after__test">This should come after the test div.</div>
|
11
|
+
|
12
|
+
<div>No matryoshka code, should be placed using a default rule.</div>
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: matryoshka
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sam Schenkman-Moore
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-04 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hpricot
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: Rack middleware for parsing html templates.
|
26
|
+
email: samsm@samsm.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- lib/matryoshka.rb
|
33
|
+
- lib/matryoshka/answer.rb
|
34
|
+
- lib/matryoshka/data.rb
|
35
|
+
- lib/matryoshka/document.rb
|
36
|
+
- lib/matryoshka/document/delegate.rb
|
37
|
+
- lib/matryoshka/document/html.rb
|
38
|
+
- lib/matryoshka/document/html/merge.rb
|
39
|
+
- lib/matryoshka/document/unknown.rb
|
40
|
+
- lib/matryoshka/external.rb
|
41
|
+
files:
|
42
|
+
- Manifest
|
43
|
+
- Rakefile
|
44
|
+
- config.ru
|
45
|
+
- lib/matryoshka.rb
|
46
|
+
- lib/matryoshka/answer.rb
|
47
|
+
- lib/matryoshka/data.rb
|
48
|
+
- lib/matryoshka/document.rb
|
49
|
+
- lib/matryoshka/document/delegate.rb
|
50
|
+
- lib/matryoshka/document/html.rb
|
51
|
+
- lib/matryoshka/document/html/merge.rb
|
52
|
+
- lib/matryoshka/document/unknown.rb
|
53
|
+
- lib/matryoshka/external.rb
|
54
|
+
- public/README
|
55
|
+
- templates/image.jpg
|
56
|
+
- templates/index.html
|
57
|
+
- templates/proxy/index.html
|
58
|
+
- templates/test/about.html
|
59
|
+
- templates/test/index.html
|
60
|
+
- matryoshka.gemspec
|
61
|
+
has_rdoc: true
|
62
|
+
homepage: http://samsm.com/
|
63
|
+
licenses: []
|
64
|
+
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options:
|
67
|
+
- --line-numbers
|
68
|
+
- --inline-source
|
69
|
+
- --title
|
70
|
+
- Matryoshka
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "1.2"
|
84
|
+
version:
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project: matryoshka
|
88
|
+
rubygems_version: 1.3.5
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Rack middleware for parsing html templates.
|
92
|
+
test_files: []
|
93
|
+
|