tokamak 1.0.0.beta2 → 1.0.0.beta4
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/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
|