tokamak 1.0.0.beta2 → 1.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +14 -0
- data/README.md +159 -0
- data/lib/tokamak/builder/base.rb +74 -0
- data/lib/tokamak/{json/builder.rb → builder/json.rb} +38 -29
- data/lib/tokamak/builder/values.rb +33 -0
- data/lib/tokamak/{atom/builder.rb → builder/xml.rb} +46 -41
- data/lib/tokamak/builder.rb +22 -0
- data/lib/tokamak/errors.rb +3 -0
- data/lib/tokamak/hook/rails.rb +78 -0
- data/lib/tokamak/hook/sinatra.rb +18 -0
- data/lib/tokamak/hook/tilt.rb +42 -0
- data/lib/tokamak/hook.rb +6 -0
- data/lib/tokamak/recipes.rb +26 -0
- data/lib/tokamak/version.rb +13 -0
- data/lib/tokamak.rb +14 -18
- data/script/console +7 -0
- data/test/rails2_skel/Rakefile +16 -0
- data/test/rails2_skel/app/controllers/application_controller.rb +1 -0
- data/test/rails2_skel/app/controllers/test_controller.rb +18 -0
- data/test/rails2_skel/app/views/test/_feed_member.tokamak +9 -0
- data/test/rails2_skel/app/views/test/feed.tokamak +24 -0
- data/test/rails2_skel/app/views/test/show.tokamak +31 -0
- data/test/rails2_skel/config/boot.rb +110 -0
- data/test/rails2_skel/config/environment.rb +20 -0
- data/test/rails2_skel/config/environments/development.rb +17 -0
- data/test/rails2_skel/config/environments/production.rb +28 -0
- data/test/rails2_skel/config/environments/test.rb +28 -0
- data/test/rails2_skel/config/initializers/cookie_verification_secret.rb +2 -0
- data/test/rails2_skel/config/initializers/mime_types.rb +3 -0
- data/test/rails2_skel/config/initializers/new_rails_defaults.rb +10 -0
- data/test/rails2_skel/config/initializers/session_store.rb +5 -0
- data/test/rails2_skel/config/routes.rb +43 -0
- data/test/rails2_skel/script/console +3 -0
- data/test/test_helper.rb +7 -0
- data/test/tokamak/builder/base_test.rb +28 -0
- data/test/tokamak/builder/json_test.rb +227 -0
- data/test/tokamak/builder/xml_test.rb +254 -0
- data/test/tokamak/helper_test.rb +106 -0
- data/test/tokamak/hook/rails_test.rb +74 -0
- data/test/tokamak/hook/sinatra_test.rb +85 -0
- data/test/tokamak/hook/tilt_test.rb +35 -0
- data/test/tokamak/recipes_test.rb +90 -0
- metadata +106 -113
- data/.document +0 -5
- data/.rspec +0 -1
- data/Gemfile +0 -27
- data/Gemfile.lock +0 -77
- data/LICENSE.txt +0 -20
- data/README.rdoc +0 -69
- data/Rakefile +0 -50
- data/VERSION +0 -1
- data/lib/tokamak/atom/base.rb +0 -87
- data/lib/tokamak/atom/helpers.rb +0 -13
- data/lib/tokamak/atom.rb +0 -8
- data/lib/tokamak/error.rb +0 -6
- data/lib/tokamak/json/base.rb +0 -83
- data/lib/tokamak/json/helpers.rb +0 -13
- data/lib/tokamak/json.rb +0 -10
- data/lib/tokamak/representation/atom/atom.rng +0 -597
- data/lib/tokamak/representation/atom/base.rb +0 -140
- data/lib/tokamak/representation/atom/category.rb +0 -39
- data/lib/tokamak/representation/atom/entry.rb +0 -56
- data/lib/tokamak/representation/atom/factory.rb +0 -48
- data/lib/tokamak/representation/atom/feed.rb +0 -108
- data/lib/tokamak/representation/atom/link.rb +0 -66
- data/lib/tokamak/representation/atom/person.rb +0 -46
- data/lib/tokamak/representation/atom/source.rb +0 -57
- data/lib/tokamak/representation/atom/tag_collection.rb +0 -36
- data/lib/tokamak/representation/atom/xml.rb +0 -94
- data/lib/tokamak/representation/atom.rb +0 -18
- data/lib/tokamak/representation/generic.rb +0 -20
- data/lib/tokamak/representation/json/base.rb +0 -25
- data/lib/tokamak/representation/json/keys_as_methods.rb +0 -72
- data/lib/tokamak/representation/json/link.rb +0 -27
- data/lib/tokamak/representation/json/link_collection.rb +0 -21
- data/lib/tokamak/representation/json.rb +0 -11
- data/lib/tokamak/representation/links.rb +0 -9
- data/lib/tokamak/representation.rb +0 -3
- data/lib/tokamak/values.rb +0 -29
- data/lib/tokamak/xml/base.rb +0 -60
- data/lib/tokamak/xml/builder.rb +0 -115
- data/lib/tokamak/xml/helpers.rb +0 -13
- data/lib/tokamak/xml/link.rb +0 -31
- data/lib/tokamak/xml/links.rb +0 -35
- data/lib/tokamak/xml.rb +0 -12
- data/spec/integration/atom/atom_spec.rb +0 -191
- data/spec/integration/full_atom.xml +0 -92
- data/spec/integration/full_json.js +0 -46
- data/spec/integration/json/json_spec.rb +0 -172
- data/spec/integration/xml/xml_spec.rb +0 -203
- data/spec/spec_helper.rb +0 -12
data/LICENSE
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Copyright (c) 2010 Abril Midia
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
you may not use this file except in compliance with the License.
|
6
|
+
You may obtain a copy of the License at
|
7
|
+
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
See the License for the specific language governing permissions and
|
14
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# Tokamak
|
2
|
+
|
3
|
+
Is a template engine for hypermedia resources that provides a single DSL to generate several media types representations.
|
4
|
+
|
5
|
+
This version supports json and xml generation (you can add other media types
|
6
|
+
easily)
|
7
|
+
|
8
|
+
The lib provide hooks for:
|
9
|
+
|
10
|
+
* Rails
|
11
|
+
* Sinatra
|
12
|
+
* Tilt ([https://github.com/rtomayko/tilt](https://github.com/rtomayko/tilt))
|
13
|
+
|
14
|
+
Just put `require "tokamak/hook/[sinatra|rails|tilt]"` on your app. See unit
|
15
|
+
tests for hook samples.
|
16
|
+
|
17
|
+
You are also able to implement hooks for other frameworks.
|
18
|
+
|
19
|
+
## Sample
|
20
|
+
|
21
|
+
### Tokamak code
|
22
|
+
|
23
|
+
collection(@some_articles) do |collection|
|
24
|
+
collection.values do |values|
|
25
|
+
values.id "http://example.com/json"
|
26
|
+
values.title "Feed"
|
27
|
+
values.updated Time.now
|
28
|
+
|
29
|
+
values.author {
|
30
|
+
values.name "John Doe"
|
31
|
+
values.email "joedoe@example.com"
|
32
|
+
}
|
33
|
+
|
34
|
+
values.author {
|
35
|
+
values.name "Foo Bar"
|
36
|
+
values.email "foobar@example.com"
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
collection.link("next" , "http://a.link.com/next")
|
41
|
+
collection.link("previous", "http://a.link.com/previous")
|
42
|
+
|
43
|
+
collection.members(:root => "articles") do |member, article|
|
44
|
+
member.values do |values|
|
45
|
+
values.id "uri:#{article[:id]}"
|
46
|
+
values.title article[:title]
|
47
|
+
values.updated article[:updated]
|
48
|
+
end
|
49
|
+
|
50
|
+
member.link("image", "http://example.com/image/1")
|
51
|
+
member.link("image", "http://example.com/image/2", :type => "application/json")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Generates the following representations:
|
56
|
+
|
57
|
+
### JSON
|
58
|
+
|
59
|
+
{
|
60
|
+
"author": [{
|
61
|
+
"name": "John Doe",
|
62
|
+
"email": "joedoe@example.com"
|
63
|
+
},
|
64
|
+
{
|
65
|
+
"name": "Foo Bar",
|
66
|
+
"email": "foobar@example.com"
|
67
|
+
}],
|
68
|
+
"title": "Feed",
|
69
|
+
"id": "http://example.com/json",
|
70
|
+
"link": [{
|
71
|
+
"href": "http://a.link.com/next",
|
72
|
+
"rel": "next",
|
73
|
+
"type": "application/json"
|
74
|
+
},
|
75
|
+
{
|
76
|
+
"href": "http://a.link.com/previous",
|
77
|
+
"rel": "previous",
|
78
|
+
"type": "application/json"
|
79
|
+
}],
|
80
|
+
"articles": [{
|
81
|
+
"title": "a great article",
|
82
|
+
"id": "uri:1",
|
83
|
+
"link": [{
|
84
|
+
"href": "http://example.com/image/1",
|
85
|
+
"rel": "image",
|
86
|
+
"type": "application/json"
|
87
|
+
},
|
88
|
+
{
|
89
|
+
"type": "application/json",
|
90
|
+
"href": "http://example.com/image/2",
|
91
|
+
"rel": "image",
|
92
|
+
"type": "application/json"
|
93
|
+
}],
|
94
|
+
"updated": "2011-01-05T10:40:58-02:00"
|
95
|
+
},
|
96
|
+
{
|
97
|
+
"title": "another great article",
|
98
|
+
"id": "uri:2",
|
99
|
+
"link": [{
|
100
|
+
"href": "http://example.com/image/1",
|
101
|
+
"rel": "image",
|
102
|
+
"type": "application/json"
|
103
|
+
},
|
104
|
+
{
|
105
|
+
"type": "application/json",
|
106
|
+
"href": "http://example.com/image/2",
|
107
|
+
"rel": "image",
|
108
|
+
"type": "application/json"
|
109
|
+
}],
|
110
|
+
"updated": "2011-01-05T10:40:58-02:00"
|
111
|
+
}],
|
112
|
+
"updated": "2011-01-05T10:40:58-02:00"
|
113
|
+
}
|
114
|
+
|
115
|
+
### XML
|
116
|
+
|
117
|
+
<?xml version="1.0"?>
|
118
|
+
<root>
|
119
|
+
<id>http://example.com/json</id>
|
120
|
+
<title>Feed</title>
|
121
|
+
<updated>2011-01-05T10:40:58-02:00</updated>
|
122
|
+
<author>
|
123
|
+
<name>John Doe</name>
|
124
|
+
<email>joedoe@example.com</email>
|
125
|
+
</author>
|
126
|
+
<author>
|
127
|
+
<name>Foo Bar</name>
|
128
|
+
<email>foobar@example.com</email>
|
129
|
+
</author>
|
130
|
+
<link href="http://a.link.com/next" rel="next" type="application/xml"/>
|
131
|
+
<link href="http://a.link.com/previous" rel="previous" type="application/xml"/>
|
132
|
+
<articles>
|
133
|
+
<id>uri:1</id>
|
134
|
+
<title>a great article</title>
|
135
|
+
<updated>2011-01-05T10:40:58-02:00</updated>
|
136
|
+
<link href="http://example.com/image/1" rel="image" type="application/xml"/>
|
137
|
+
<link href="http://example.com/image/2" type="application/json" rel="image"/>
|
138
|
+
</articles>
|
139
|
+
<articles>
|
140
|
+
<id>uri:2</id>
|
141
|
+
<title>another great article</title>
|
142
|
+
<updated>2011-01-05T10:40:58-02:00</updated>
|
143
|
+
<link href="http://example.com/image/1" rel="image" type="application/xml"/>
|
144
|
+
<link href="http://example.com/image/2" type="application/json" rel="image"/>
|
145
|
+
</articles>
|
146
|
+
</root>
|
147
|
+
|
148
|
+
## Other features
|
149
|
+
|
150
|
+
* You can declare recipes once and reuse it later (see `Tokamak::Recipes`)
|
151
|
+
* You can extend `Tokamak::Builder::Base` to support a custom media type.
|
152
|
+
* You can customize the DSL entrypoint helpers, used by the hooks (see `Tokamak::Builder::HelperTest`)
|
153
|
+
|
154
|
+
## Want to know more?
|
155
|
+
|
156
|
+
Please check the unit tests, you can see a lot of richer samples, including tests for the hooks.
|
157
|
+
|
158
|
+
*This library was extracted from [Restfulie](https://github.com/caelum/restfulie) and then heavy refactored. The same terms apply, see LICENSE.txt*
|
159
|
+
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Tokamak
|
2
|
+
module Builder
|
3
|
+
class Base
|
4
|
+
|
5
|
+
@@global_media_types = {}
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def builder_for(*args)
|
9
|
+
# class instance variable to store media types handled by a builder
|
10
|
+
@media_types = args
|
11
|
+
args.each do |media_type|
|
12
|
+
@@global_media_types[media_type] = self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def media_types
|
17
|
+
@media_types
|
18
|
+
end
|
19
|
+
|
20
|
+
def global_media_types
|
21
|
+
@@global_media_types
|
22
|
+
end
|
23
|
+
|
24
|
+
def build(obj, options = {}, &block)
|
25
|
+
if block_given?
|
26
|
+
recipe = block
|
27
|
+
else
|
28
|
+
recipe = options.delete(:recipe)
|
29
|
+
end
|
30
|
+
|
31
|
+
unless recipe.respond_to?(:call)
|
32
|
+
recipe = Tokamak::Recipes[recipe]
|
33
|
+
raise Tokamak::BuilderError.new("Recipe required to build representation.") unless recipe.respond_to?(:call)
|
34
|
+
end
|
35
|
+
|
36
|
+
builder = self.new(obj, options)
|
37
|
+
|
38
|
+
recipe.call(*[builder, obj, options][0, recipe.arity])
|
39
|
+
|
40
|
+
builder.representation
|
41
|
+
end
|
42
|
+
|
43
|
+
def helper
|
44
|
+
unless instance_variable_get(:@helper_module)
|
45
|
+
@helper_module = Tokamak::Builder.helper_module_for(self)
|
46
|
+
end
|
47
|
+
@helper_module
|
48
|
+
end
|
49
|
+
|
50
|
+
def collection_helper_default_options(options = {}, &block)
|
51
|
+
generic_helper(:collection, options, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def member_helper_default_options(type, options = {}, &block)
|
55
|
+
generic_helper(:member, options, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def generic_helper(section, options = {}, &block)
|
59
|
+
helper.send(:remove_method, section)
|
60
|
+
var_name = "@@more_options_#{section.to_s}".to_sym
|
61
|
+
helper.send(:class_variable_set, var_name, options)
|
62
|
+
helper.module_eval <<-EOS
|
63
|
+
def #{section.to_s}(obj, *args, &block)
|
64
|
+
#{var_name}.merge!(args.shift)
|
65
|
+
args.unshift(#{var_name})
|
66
|
+
#{self.name}.build(obj, *args, &block)
|
67
|
+
end
|
68
|
+
EOS
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -1,67 +1,75 @@
|
|
1
|
+
require "json/pure"
|
2
|
+
|
1
3
|
module Tokamak
|
2
|
-
module
|
3
|
-
class Builder
|
4
|
-
|
5
|
-
|
4
|
+
module Builder
|
5
|
+
class Json < Tokamak::Builder::Base
|
6
|
+
|
7
|
+
builder_for "application/json"
|
8
|
+
|
9
|
+
attr_reader :raw
|
10
|
+
|
11
|
+
def initialize(obj, options = {})
|
12
|
+
@raw = options[:root] ? { options[:root] => {} } : {}
|
13
|
+
@current = options[:root] ? @raw[options[:root]] : @raw
|
6
14
|
@obj = obj
|
7
|
-
@current = @doc
|
8
15
|
end
|
9
|
-
|
10
|
-
def values(options = nil, &block)
|
11
|
-
yield Values.new(self)
|
12
|
-
end
|
13
|
-
|
16
|
+
|
14
17
|
def members(options = {}, &block)
|
15
|
-
collection = options[:collection] || @obj
|
16
|
-
raise
|
17
|
-
root = options[:root] ||
|
18
|
-
|
18
|
+
collection = options[:collection] || @obj
|
19
|
+
raise Tokamak::BuilderError.new("Members method require a collection to execute") unless collection.respond_to?(:each)
|
20
|
+
root = options[:root] || "members"
|
21
|
+
|
22
|
+
add_to_current(root, [])
|
19
23
|
collection.each do |member|
|
20
24
|
node = {}
|
21
|
-
|
25
|
+
|
22
26
|
parent = @current
|
23
27
|
@current = node
|
24
28
|
block.call(self, member)
|
25
29
|
@current = parent
|
26
|
-
|
30
|
+
|
27
31
|
add_to_current(root, node)
|
28
32
|
end
|
29
33
|
end
|
30
|
-
|
34
|
+
|
35
|
+
def values(options = {}, &block)
|
36
|
+
yield Values.new(self)
|
37
|
+
end
|
38
|
+
|
31
39
|
def link(relationship, uri, options = {})
|
32
40
|
options["rel"] = relationship.to_s
|
33
41
|
options["href"] = uri
|
34
|
-
options["type"] ||= "application/json"
|
42
|
+
options["type"] ||= options[:type] || "application/json"
|
35
43
|
insert_value("link", nil, options)
|
36
44
|
end
|
37
|
-
|
45
|
+
|
38
46
|
def insert_value(name, prefix, *args, &block)
|
39
47
|
node = create_element(block_given?, *args)
|
40
|
-
|
48
|
+
|
41
49
|
if block_given?
|
42
50
|
parent = @current
|
43
51
|
@current = node
|
44
52
|
block.call
|
45
53
|
@current = parent
|
46
54
|
end
|
47
|
-
|
55
|
+
|
48
56
|
add_to_current(name, node)
|
49
57
|
end
|
50
|
-
|
58
|
+
|
51
59
|
def representation
|
52
|
-
|
60
|
+
@raw.to_json
|
53
61
|
end
|
54
|
-
|
62
|
+
|
55
63
|
private
|
56
|
-
|
64
|
+
|
57
65
|
def create_element(has_block, *args)
|
58
66
|
vals = []
|
59
67
|
hashes = []
|
60
|
-
|
68
|
+
|
61
69
|
args.each do |arg|
|
62
70
|
arg.kind_of?(Hash) ? hashes << arg : vals << arg
|
63
71
|
end
|
64
|
-
|
72
|
+
|
65
73
|
if hashes.empty?
|
66
74
|
# only simple values
|
67
75
|
unless vals.empty?
|
@@ -72,7 +80,7 @@ module Tokamak
|
|
72
80
|
end
|
73
81
|
else
|
74
82
|
# yes we have hashes
|
75
|
-
node = {}
|
83
|
+
node = {}
|
76
84
|
hashes.each { |hash| node.merge!(hash) }
|
77
85
|
unless vals.empty?
|
78
86
|
vals = vals.first if vals.size == 1
|
@@ -81,7 +89,7 @@ module Tokamak
|
|
81
89
|
node
|
82
90
|
end
|
83
91
|
end
|
84
|
-
|
92
|
+
|
85
93
|
def add_to_current(name, value)
|
86
94
|
if @current[name]
|
87
95
|
if @current[name].kind_of?(Array)
|
@@ -93,6 +101,7 @@ module Tokamak
|
|
93
101
|
@current[name] = value
|
94
102
|
end
|
95
103
|
end
|
104
|
+
|
96
105
|
end
|
97
106
|
end
|
98
107
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Tokamak
|
2
|
+
module Builder
|
3
|
+
|
4
|
+
# This is a Blank Slate class to support the renderization of the values block of Builder DSLs
|
5
|
+
# Every Media type should implement a Builder with a insert_value method that renders the values block to a specific format
|
6
|
+
class Values
|
7
|
+
attr_accessor :builder
|
8
|
+
|
9
|
+
# BlankSlate
|
10
|
+
instance_methods.each do |m|
|
11
|
+
undef_method m unless m.to_s =~ /\[\]|method_missing|respond_to\?|^__/
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(builder)
|
15
|
+
@builder = builder
|
16
|
+
@current_prefix = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](prefix)
|
20
|
+
@current_prefix = prefix
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(symbol, *args, &block)
|
25
|
+
name = symbol.to_s
|
26
|
+
prefix = @current_prefix
|
27
|
+
@current_prefix = nil
|
28
|
+
@builder.insert_value(name, prefix, *args, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -1,15 +1,32 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
|
1
3
|
module Tokamak
|
2
|
-
module
|
3
|
-
class Builder
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@
|
4
|
+
module Builder
|
5
|
+
class Xml < Tokamak::Builder::Base
|
6
|
+
|
7
|
+
builder_for "application/xml", "text/xml"
|
8
|
+
|
9
|
+
attr_reader :raw
|
10
|
+
|
11
|
+
def initialize(obj, options = {})
|
12
|
+
@raw = Nokogiri::XML::Document.new
|
13
|
+
@obj = obj
|
14
|
+
@parent = @raw.create_element(options[:root] || "root")
|
15
|
+
@parent.parent = @raw
|
11
16
|
end
|
12
|
-
|
17
|
+
|
18
|
+
def members(options = {}, &block)
|
19
|
+
collection = options[:collection] || @obj
|
20
|
+
raise Tokamak::BuilderError.new("Members method require a collection to execute") unless collection.respond_to?(:each)
|
21
|
+
collection.each do |member|
|
22
|
+
member_root = @raw.create_element(options[:root] || "member")
|
23
|
+
member_root.parent = @parent
|
24
|
+
@parent = member_root
|
25
|
+
block.call(self, member)
|
26
|
+
@parent = member_root.parent
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
13
30
|
def values(options = {}, &block)
|
14
31
|
options.each do |key,value|
|
15
32
|
attr = key.to_s
|
@@ -17,47 +34,35 @@ module Tokamak
|
|
17
34
|
ns = attr.split(":", 2)[1]
|
18
35
|
@parent.add_namespace_definition(ns, value)
|
19
36
|
end
|
20
|
-
end
|
21
|
-
yield Values.new(self)
|
22
|
-
end
|
23
|
-
|
24
|
-
def members(options = {}, &block)
|
25
|
-
collection = options[:collection] || @obj
|
26
|
-
raise Error::BuilderError.new("Members method require a collection to execute") unless collection.respond_to?(:each)
|
27
|
-
collection.each do |member|
|
28
|
-
entry = @doc.create_element("entry")
|
29
|
-
entry.parent = @parent
|
30
|
-
@parent = entry
|
31
|
-
block.call(self, member)
|
32
|
-
@parent = entry.parent
|
33
37
|
end
|
38
|
+
yield Values.new(self)
|
34
39
|
end
|
35
|
-
|
40
|
+
|
36
41
|
def link(relationship, uri, options = {})
|
37
42
|
options["rel"] = relationship.to_s
|
38
43
|
options["href"] = uri
|
39
|
-
options["type"] ||= "application/
|
44
|
+
options["type"] ||= options[:type] || "application/xml"
|
40
45
|
insert_value("link", nil, options)
|
41
46
|
end
|
42
|
-
|
47
|
+
|
43
48
|
def insert_value(name, prefix, *args, &block)
|
44
49
|
node = create_element(name.to_s, prefix, *args)
|
45
|
-
node.parent = @parent
|
50
|
+
node.parent = @parent
|
46
51
|
if block_given?
|
47
52
|
@parent = node
|
48
53
|
block.call
|
49
54
|
@parent = node.parent
|
50
55
|
end
|
51
56
|
end
|
52
|
-
|
57
|
+
|
53
58
|
def representation
|
54
|
-
|
59
|
+
@raw.to_xml
|
55
60
|
end
|
56
|
-
|
61
|
+
|
57
62
|
private
|
58
|
-
|
63
|
+
|
59
64
|
def create_element(node, prefix, *args)
|
60
|
-
node = @
|
65
|
+
node = @raw.create_element(node) do |n|
|
61
66
|
if prefix
|
62
67
|
if namespace = prefix_valid?(prefix)
|
63
68
|
# Adding namespace prefix
|
@@ -65,7 +70,7 @@ module Tokamak
|
|
65
70
|
namespace = nil
|
66
71
|
end
|
67
72
|
end
|
68
|
-
|
73
|
+
|
69
74
|
args.each do |arg|
|
70
75
|
case arg
|
71
76
|
# Adding XML attributes
|
@@ -80,28 +85,28 @@ module Tokamak
|
|
80
85
|
n[k.to_s] = v.to_s
|
81
86
|
}
|
82
87
|
# Adding XML node content
|
83
|
-
else
|
84
|
-
arg.kind_of?(Time) || arg.kind_of?(DateTime) ?
|
88
|
+
else
|
89
|
+
content = arg.kind_of?(Time) || arg.kind_of?(DateTime) ? arg.xmlschema : arg
|
85
90
|
n.content = content
|
86
91
|
end
|
87
92
|
end
|
88
93
|
end
|
89
94
|
end
|
90
|
-
|
95
|
+
|
91
96
|
def prefix_valid?(prefix)
|
92
97
|
ns = @parent.namespace_definitions.find { |x| x.prefix == prefix.to_s }
|
93
|
-
|
98
|
+
|
94
99
|
unless ns
|
95
100
|
@parent.ancestors.each do |a|
|
96
|
-
next if a == @
|
101
|
+
next if a == @raw
|
97
102
|
ns = a.namespace_definitions.find { |x| x.prefix == prefix.to_s }
|
98
103
|
break if ns
|
99
|
-
end
|
104
|
+
end
|
100
105
|
end
|
101
|
-
|
106
|
+
|
102
107
|
return ns
|
103
|
-
#TODO: raise ArgumentError, "Namespace #{prefix} has not been defined" if wanted
|
104
108
|
end
|
109
|
+
|
105
110
|
end
|
106
111
|
end
|
107
112
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Tokamak
|
2
|
+
module Builder
|
3
|
+
autoload :Base , "tokamak/builder/base"
|
4
|
+
autoload :Values, "tokamak/builder/values"
|
5
|
+
autoload :Json , "tokamak/builder/json"
|
6
|
+
autoload :Xml , "tokamak/builder/xml"
|
7
|
+
|
8
|
+
def self.helper_module_for(const)
|
9
|
+
mod = Module.new
|
10
|
+
mod.module_eval <<-EOS
|
11
|
+
def collection(obj, *args, &block)
|
12
|
+
#{const.name}.build(obj, *args, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def member(obj, *args, &block)
|
16
|
+
#{const.name}.build(obj, *args, &block)
|
17
|
+
end
|
18
|
+
EOS
|
19
|
+
mod
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../tokamak.rb') unless defined? ::Tokamak
|
2
|
+
|
3
|
+
module Tokamak
|
4
|
+
module Hook
|
5
|
+
module Rails
|
6
|
+
|
7
|
+
class Tokamak < ::ActionView::TemplateHandler
|
8
|
+
include ::ActionView::TemplateHandlers::Compilable
|
9
|
+
|
10
|
+
def compile(template)
|
11
|
+
"@content_type_helpers = ::Tokamak.builder_lookup(self.response.content_type).helper; " +
|
12
|
+
"extend @content_type_helpers; " +
|
13
|
+
"extend Tokamak::Hook::Rails::Helpers; " +
|
14
|
+
"code_block = lambda { #{template.source} };" +
|
15
|
+
"builder = code_block.call; " +
|
16
|
+
"builder"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Helpers
|
21
|
+
# Load a partial template to execute in describe
|
22
|
+
#
|
23
|
+
# For example:
|
24
|
+
#
|
25
|
+
# Passing the current context to partial in template:
|
26
|
+
#
|
27
|
+
# member(@album) do |member, album|
|
28
|
+
# partial('member', binding)
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# in partial:
|
32
|
+
#
|
33
|
+
# member.links << link(:rel => :artists, :href => album_artists_url(album))
|
34
|
+
#
|
35
|
+
# Or passing local variables assing
|
36
|
+
#
|
37
|
+
# collection(@albums) do |collection|
|
38
|
+
# collection.members do |member, album|
|
39
|
+
# partial("member", :locals => {:member => member, :album => album})
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
def partial(partial_path, caller_binding = nil)
|
44
|
+
template = _pick_partial_template(partial_path)
|
45
|
+
|
46
|
+
# Create a context to assing variables
|
47
|
+
if caller_binding.kind_of?(Hash)
|
48
|
+
Proc.new do
|
49
|
+
extend @content_type_helpers
|
50
|
+
context = eval("(class << self; self; end)", binding)
|
51
|
+
|
52
|
+
unless caller_binding[:locals].nil?
|
53
|
+
caller_binding[:locals].each do |k, v|
|
54
|
+
context.send(:define_method, k.to_sym) { v }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
partial(partial_path, binding)
|
59
|
+
end.call
|
60
|
+
else
|
61
|
+
eval(template.source, caller_binding, template.path)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if defined? ::ActionView::Template and ::ActionView::Template.respond_to?(:register_template_handler)
|
67
|
+
::ActionView::Template
|
68
|
+
else
|
69
|
+
::ActionView::Base
|
70
|
+
end.register_template_handler(:tokamak, Tokamak)
|
71
|
+
|
72
|
+
if defined? ::ActionController::Base
|
73
|
+
::ActionController::Base.exempt_from_layout :tokamak
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../tokamak.rb') unless defined? ::Tokamak
|
2
|
+
require "tokamak/hook/tilt"
|
3
|
+
|
4
|
+
module Tokamak
|
5
|
+
module Hook
|
6
|
+
module Sinatra
|
7
|
+
|
8
|
+
module ::Sinatra::Templates
|
9
|
+
|
10
|
+
def tokamak(template, options={}, locals={})
|
11
|
+
options.merge! :layout => false, :media_type => response["Content-Type"]
|
12
|
+
render :tokamak, template, options, locals
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|