matryoshka 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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 }
@@ -0,0 +1,7 @@
1
+ # config.ru
2
+ require 'rubygems'
3
+ require 'rack'
4
+ require 'lib/matryoshka'
5
+
6
+ use Rack::ShowExceptions
7
+ run Matryoshka.new
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'open-uri'
2
+
3
+ class Matryoshka::External
4
+ def self.find(uri,options = {})
5
+ [200,{}.merge(options),open(uri)]
6
+ rescue
7
+ [404,{}.merge(options),'<p>404 on external</p>']
8
+ end
9
+ end
@@ -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
@@ -0,0 +1,2 @@
1
+ File placed in this folder will be read directly by the upstream
2
+ server (Apache, etc) with no special Matryoshka processing.
Binary file
@@ -0,0 +1,15 @@
1
+ <html>
2
+ <head>
3
+ <title>Hi</title>
4
+ </head>
5
+ <body>
6
+ <div>
7
+ <img src="/image.jpg">
8
+ <a href="a.pdf">A link to a pdf</a>
9
+ </div>
10
+ <div id="test"></div>
11
+ <div id="content">
12
+ This is from /index.html
13
+ </div>
14
+ </body>
15
+ </html>
@@ -0,0 +1,3 @@
1
+ <div rel="proxy" href="http://tumble.samsm.com/">
2
+ <div id="container">Proxy div, unloaded, apparently.</div>
3
+ </div>
@@ -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>
@@ -0,0 +1,8 @@
1
+ <div id="test">
2
+ This is from /test/index.html
3
+ </div>>
4
+
5
+ <div rel="external" href="http://samsm.com/">
6
+ <div id="test"></div>
7
+ <ul rel="selector" href="ul"></ul>
8
+ </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
+