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.
Files changed (91) hide show
  1. data/LICENSE +14 -0
  2. data/README.md +159 -0
  3. data/lib/tokamak/builder/base.rb +74 -0
  4. data/lib/tokamak/{json/builder.rb → builder/json.rb} +38 -29
  5. data/lib/tokamak/builder/values.rb +33 -0
  6. data/lib/tokamak/{atom/builder.rb → builder/xml.rb} +46 -41
  7. data/lib/tokamak/builder.rb +22 -0
  8. data/lib/tokamak/errors.rb +3 -0
  9. data/lib/tokamak/hook/rails.rb +78 -0
  10. data/lib/tokamak/hook/sinatra.rb +18 -0
  11. data/lib/tokamak/hook/tilt.rb +42 -0
  12. data/lib/tokamak/hook.rb +6 -0
  13. data/lib/tokamak/recipes.rb +26 -0
  14. data/lib/tokamak/version.rb +13 -0
  15. data/lib/tokamak.rb +14 -18
  16. data/script/console +7 -0
  17. data/test/rails2_skel/Rakefile +16 -0
  18. data/test/rails2_skel/app/controllers/application_controller.rb +1 -0
  19. data/test/rails2_skel/app/controllers/test_controller.rb +18 -0
  20. data/test/rails2_skel/app/views/test/_feed_member.tokamak +9 -0
  21. data/test/rails2_skel/app/views/test/feed.tokamak +24 -0
  22. data/test/rails2_skel/app/views/test/show.tokamak +31 -0
  23. data/test/rails2_skel/config/boot.rb +110 -0
  24. data/test/rails2_skel/config/environment.rb +20 -0
  25. data/test/rails2_skel/config/environments/development.rb +17 -0
  26. data/test/rails2_skel/config/environments/production.rb +28 -0
  27. data/test/rails2_skel/config/environments/test.rb +28 -0
  28. data/test/rails2_skel/config/initializers/cookie_verification_secret.rb +2 -0
  29. data/test/rails2_skel/config/initializers/mime_types.rb +3 -0
  30. data/test/rails2_skel/config/initializers/new_rails_defaults.rb +10 -0
  31. data/test/rails2_skel/config/initializers/session_store.rb +5 -0
  32. data/test/rails2_skel/config/routes.rb +43 -0
  33. data/test/rails2_skel/script/console +3 -0
  34. data/test/test_helper.rb +7 -0
  35. data/test/tokamak/builder/base_test.rb +28 -0
  36. data/test/tokamak/builder/json_test.rb +227 -0
  37. data/test/tokamak/builder/xml_test.rb +254 -0
  38. data/test/tokamak/helper_test.rb +106 -0
  39. data/test/tokamak/hook/rails_test.rb +74 -0
  40. data/test/tokamak/hook/sinatra_test.rb +85 -0
  41. data/test/tokamak/hook/tilt_test.rb +35 -0
  42. data/test/tokamak/recipes_test.rb +90 -0
  43. metadata +106 -113
  44. data/.document +0 -5
  45. data/.rspec +0 -1
  46. data/Gemfile +0 -27
  47. data/Gemfile.lock +0 -77
  48. data/LICENSE.txt +0 -20
  49. data/README.rdoc +0 -69
  50. data/Rakefile +0 -50
  51. data/VERSION +0 -1
  52. data/lib/tokamak/atom/base.rb +0 -87
  53. data/lib/tokamak/atom/helpers.rb +0 -13
  54. data/lib/tokamak/atom.rb +0 -8
  55. data/lib/tokamak/error.rb +0 -6
  56. data/lib/tokamak/json/base.rb +0 -83
  57. data/lib/tokamak/json/helpers.rb +0 -13
  58. data/lib/tokamak/json.rb +0 -10
  59. data/lib/tokamak/representation/atom/atom.rng +0 -597
  60. data/lib/tokamak/representation/atom/base.rb +0 -140
  61. data/lib/tokamak/representation/atom/category.rb +0 -39
  62. data/lib/tokamak/representation/atom/entry.rb +0 -56
  63. data/lib/tokamak/representation/atom/factory.rb +0 -48
  64. data/lib/tokamak/representation/atom/feed.rb +0 -108
  65. data/lib/tokamak/representation/atom/link.rb +0 -66
  66. data/lib/tokamak/representation/atom/person.rb +0 -46
  67. data/lib/tokamak/representation/atom/source.rb +0 -57
  68. data/lib/tokamak/representation/atom/tag_collection.rb +0 -36
  69. data/lib/tokamak/representation/atom/xml.rb +0 -94
  70. data/lib/tokamak/representation/atom.rb +0 -18
  71. data/lib/tokamak/representation/generic.rb +0 -20
  72. data/lib/tokamak/representation/json/base.rb +0 -25
  73. data/lib/tokamak/representation/json/keys_as_methods.rb +0 -72
  74. data/lib/tokamak/representation/json/link.rb +0 -27
  75. data/lib/tokamak/representation/json/link_collection.rb +0 -21
  76. data/lib/tokamak/representation/json.rb +0 -11
  77. data/lib/tokamak/representation/links.rb +0 -9
  78. data/lib/tokamak/representation.rb +0 -3
  79. data/lib/tokamak/values.rb +0 -29
  80. data/lib/tokamak/xml/base.rb +0 -60
  81. data/lib/tokamak/xml/builder.rb +0 -115
  82. data/lib/tokamak/xml/helpers.rb +0 -13
  83. data/lib/tokamak/xml/link.rb +0 -31
  84. data/lib/tokamak/xml/links.rb +0 -35
  85. data/lib/tokamak/xml.rb +0 -12
  86. data/spec/integration/atom/atom_spec.rb +0 -191
  87. data/spec/integration/full_atom.xml +0 -92
  88. data/spec/integration/full_json.js +0 -46
  89. data/spec/integration/json/json_spec.rb +0 -172
  90. data/spec/integration/xml/xml_spec.rb +0 -203
  91. 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 Json
3
- class Builder
4
- def initialize(obj, initial_obj = {})
5
- @doc = initial_obj
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 Error::BuilderError.new("Members method require a collection to execute") unless collection.respond_to?(:each)
17
- root = options[:root] || Tokamak.root_element_for(collection)
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
- Tokamak::Representation::Json.create(@doc).to_json
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 Atom
3
- class Builder
4
- attr_accessor :atom_type
5
- def initialize(atom_type, obj)
6
- @doc = Nokogiri::XML::Document.new
7
- @obj = obj
8
- @parent = @doc.create_element(atom_type.to_s)
9
- @parent.add_namespace_definition(nil, "http://www.w3.org/2005/Atom")
10
- @parent.parent = @doc
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/atom+xml"
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
- Tokamak::Representation::Atom::Factory.create(@doc)
59
+ @raw.to_xml
55
60
  end
56
-
61
+
57
62
  private
58
-
63
+
59
64
  def create_element(node, prefix, *args)
60
- node = @doc.create_element(node) do |n|
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) ? content = arg.xmlschema : content = arg
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 == @doc
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,3 @@
1
+ module Tokamak
2
+ class BuilderError < StandardError; end
3
+ 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