matryoshka 0.0.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.
- 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
|
+
|