hypertemplate 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +29 -0
- data/README.md +134 -0
- data/lib/hypertemplate.rb +14 -0
- data/lib/hypertemplate/builder.rb +33 -0
- data/lib/hypertemplate/builder/base.rb +111 -0
- data/lib/hypertemplate/builder/json.rb +132 -0
- data/lib/hypertemplate/builder/values.rb +33 -0
- data/lib/hypertemplate/builder/xml.rb +119 -0
- data/lib/hypertemplate/errors.rb +3 -0
- data/lib/hypertemplate/hook.rb +6 -0
- data/lib/hypertemplate/hook/rails.rb +112 -0
- data/lib/hypertemplate/hook/sinatra.rb +44 -0
- data/lib/hypertemplate/hook/tilt.rb +68 -0
- data/lib/hypertemplate/recipes.rb +25 -0
- data/lib/hypertemplate/registry.rb +24 -0
- data/lib/hypertemplate/version.rb +13 -0
- data/script/console +7 -0
- data/test/hypertemplate/builder/base_test.rb +45 -0
- data/test/hypertemplate/builder/json_test.rb +506 -0
- data/test/hypertemplate/builder/values_test.rb +12 -0
- data/test/hypertemplate/builder/xml_test.rb +479 -0
- data/test/hypertemplate/helper_test.rb +116 -0
- data/test/hypertemplate/hook/rails_test.rb +77 -0
- data/test/hypertemplate/hook/sinatra_test.rb +89 -0
- data/test/hypertemplate/hook/tilt_test.rb +48 -0
- data/test/hypertemplate/recipes_test.rb +94 -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.hyper +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/log/development.log +10190 -0
- data/test/rails2_skel/script/console +3 -0
- data/test/test_helper.rb +9 -0
- metadata +207 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module Hypertemplate
|
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|object_id|send|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
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Hypertemplate
|
2
|
+
module Builder
|
3
|
+
class Xml < Hypertemplate::Builder::Base
|
4
|
+
|
5
|
+
def self.media_types
|
6
|
+
["application/xml", "text/xml"]
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :raw
|
10
|
+
|
11
|
+
def initialize(obj, options = {})
|
12
|
+
initialize_library
|
13
|
+
@raw = Nokogiri::XML::Document.new
|
14
|
+
@obj = obj
|
15
|
+
@parent = @raw.create_element(options[:root] || "root")
|
16
|
+
@parent.parent = @raw
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize_library
|
20
|
+
return if defined?(::Nokogiri)
|
21
|
+
require "nokogiri"
|
22
|
+
end
|
23
|
+
|
24
|
+
def members(options = {}, &block)
|
25
|
+
collection = options[:collection] || @obj
|
26
|
+
raise Hypertemplate::BuilderError.new("Members method require a collection to execute") unless collection.respond_to?(:each)
|
27
|
+
name = options[:root] || "member"
|
28
|
+
collection.each do |element|
|
29
|
+
member_root = @raw.create_element(name)
|
30
|
+
member_root.parent = @parent
|
31
|
+
@parent = member_root
|
32
|
+
if block.arity==1
|
33
|
+
# new dsl
|
34
|
+
block.call(element)
|
35
|
+
else
|
36
|
+
# old dsl (deprecate at 1.3?)
|
37
|
+
block.call(self, element)
|
38
|
+
end
|
39
|
+
@parent = member_root.parent
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def values(options = {}, &block)
|
44
|
+
options.each do |key,value|
|
45
|
+
apply_namespace(@parent, key.to_s, value)
|
46
|
+
end
|
47
|
+
yield Values.new(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def link(relationship, uri, options = {})
|
51
|
+
options["rel"] = relationship.to_s
|
52
|
+
options["href"] = uri
|
53
|
+
options["type"] ||= options[:type] || "application/xml"
|
54
|
+
insert_value("link", nil, options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def insert_value(name, prefix, *args, &block)
|
58
|
+
# Protected if empty array
|
59
|
+
unless args.size == 1 and args.first == []
|
60
|
+
node = create_element(name.to_s, prefix, *args)
|
61
|
+
node.parent = @parent
|
62
|
+
if block_given?
|
63
|
+
@parent = node
|
64
|
+
block.call
|
65
|
+
@parent = node.parent
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def representation
|
71
|
+
@raw.to_xml
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def apply_namespace(node, key, value)
|
77
|
+
if key =~ /^xmlns(:\w+)?$/
|
78
|
+
ns_name = key.split(":", 2)[1]
|
79
|
+
node.add_namespace_definition(ns_name, value)
|
80
|
+
return true
|
81
|
+
end
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_element(node, prefix, *args)
|
86
|
+
n = @raw.create_element(node)
|
87
|
+
if prefix
|
88
|
+
if namespace = find_prefix(prefix)
|
89
|
+
n.namespace = namespace
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
args.each do |arg|
|
94
|
+
if arg.kind_of? Hash
|
95
|
+
# Adding XML attributes
|
96
|
+
arg.each { |k,v|
|
97
|
+
key = k.to_s
|
98
|
+
n[key] = v.to_s unless apply_namespace(n, key, v)
|
99
|
+
}
|
100
|
+
elsif arg.kind_of?(Time) || arg.kind_of?(DateTime)
|
101
|
+
# Adding XML node content
|
102
|
+
n.content = arg.xmlschema
|
103
|
+
else
|
104
|
+
n.content = arg
|
105
|
+
end
|
106
|
+
end
|
107
|
+
n
|
108
|
+
end
|
109
|
+
|
110
|
+
def find_prefix(prefix)
|
111
|
+
all = [@parent] + @parent.ancestors
|
112
|
+
all.each do |a|
|
113
|
+
return a.namespace_definitions.find { |x| x.prefix == prefix.to_s } if a != @raw
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'hypertemplate' unless defined? ::Hypertemplate
|
2
|
+
|
3
|
+
module Hypertemplate
|
4
|
+
module RegistryContainer
|
5
|
+
|
6
|
+
def hypertemplate_registry
|
7
|
+
@hypertemplate || use_hypertemplate
|
8
|
+
end
|
9
|
+
|
10
|
+
def use_hypertemplate(&block)
|
11
|
+
@hypertemplate = ::Hypertemplate::Registry.new
|
12
|
+
if block_given?
|
13
|
+
yield @hypertemplate
|
14
|
+
else
|
15
|
+
@hypertemplate << ::Hypertemplate::Builder::Json
|
16
|
+
@hypertemplate << ::Hypertemplate::Builder::Xml
|
17
|
+
end
|
18
|
+
@hypertemplate
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ActionController
|
25
|
+
class Base
|
26
|
+
include Hypertemplate::RegistryContainer
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Hypertemplate
|
31
|
+
module Hook
|
32
|
+
module Rails
|
33
|
+
|
34
|
+
class Hypertemplate < ::ActionView::TemplateHandler
|
35
|
+
include ::ActionView::TemplateHandlers::Compilable
|
36
|
+
|
37
|
+
def compile(template)
|
38
|
+
"@content_type_helpers = @controller.hypertemplate_registry[self.response.content_type].helper; " +
|
39
|
+
"extend @content_type_helpers; " +
|
40
|
+
"extend Hypertemplate::Hook::Rails::Helpers; " +
|
41
|
+
"code_block = lambda { #{template.source} };" +
|
42
|
+
"builder = code_block.call; " +
|
43
|
+
"builder"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module Helpers
|
48
|
+
# Load a partial template to execute in describe
|
49
|
+
#
|
50
|
+
# For example:
|
51
|
+
#
|
52
|
+
# Passing the current context to partial in template:
|
53
|
+
#
|
54
|
+
# member(@album) do |member, album|
|
55
|
+
# partial('member', binding)
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# in partial:
|
59
|
+
#
|
60
|
+
# member.links << link(:rel => :artists, :href => album_artists_url(album))
|
61
|
+
#
|
62
|
+
# Or passing local variables assing
|
63
|
+
#
|
64
|
+
# collection(@albums) do |collection|
|
65
|
+
# collection.members do |member, album|
|
66
|
+
# partial("member", :locals => {:member => member, :album => album})
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
def partial(partial_path, caller_binding = nil)
|
71
|
+
template = _pick_partial_template(partial_path)
|
72
|
+
|
73
|
+
# Create a context to assing variables
|
74
|
+
if caller_binding.kind_of?(Hash)
|
75
|
+
Proc.new do
|
76
|
+
extend @content_type_helpers
|
77
|
+
context = eval("(class << self; self; end)", binding)
|
78
|
+
|
79
|
+
caller_binding.fetch(:locals, {}).each do |k, v|
|
80
|
+
context.send(:define_method, k.to_sym) { v }
|
81
|
+
end
|
82
|
+
|
83
|
+
partial(partial_path, binding)
|
84
|
+
end.call
|
85
|
+
else
|
86
|
+
eval(template.source, caller_binding, template.path)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def self.registry
|
94
|
+
if defined? ::ActionView::Template and ::ActionView::Template.respond_to?(:register_template_handler)
|
95
|
+
::ActionView::Template
|
96
|
+
else
|
97
|
+
if defined? ::ActionController::Base
|
98
|
+
::ActionController::Base.exempt_from_layout :hyper
|
99
|
+
::ActionController::Base.exempt_from_layout :hypertemplate
|
100
|
+
::ActionController::Base.exempt_from_layout :tokamak
|
101
|
+
end
|
102
|
+
::ActionView::Base
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
registry.register_template_handler(:hyper, Hypertemplate)
|
107
|
+
registry.register_template_handler(:hypertemplate, Hypertemplate)
|
108
|
+
registry.register_template_handler(:tokamak, Hypertemplate)
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'hypertemplate' unless defined? ::Hypertemplate
|
2
|
+
require "hypertemplate/hook/tilt"
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
class Hypertemplate
|
6
|
+
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
@registry = ::Hypertemplate::Registry.new
|
10
|
+
if block_given?
|
11
|
+
yield @registry
|
12
|
+
else
|
13
|
+
@registry << ::Hypertemplate::Builder::Json
|
14
|
+
@registry << ::Hypertemplate::Builder::Xml
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
env["hypertemplate"] = @registry
|
20
|
+
@app.call(env)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Hypertemplate
|
27
|
+
module Hook
|
28
|
+
module Sinatra
|
29
|
+
|
30
|
+
module ::Sinatra::Templates
|
31
|
+
|
32
|
+
def tokamak(template, options={}, locals={})
|
33
|
+
hyler(template, options, locals)
|
34
|
+
end
|
35
|
+
|
36
|
+
def hyper(template, options={}, locals={})
|
37
|
+
options.merge! :layout => false, :media_type => response["Content-Type"]
|
38
|
+
render :hyper, template, options, locals
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'hypertemplate' unless defined? ::Hypertemplate
|
2
|
+
|
3
|
+
module Hypertemplate
|
4
|
+
module Hook
|
5
|
+
module Tilt
|
6
|
+
|
7
|
+
class HypertemplateTilt
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@registry = Hypertemplate::Registry.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# unfortunately Tilt uses a global registry
|
14
|
+
def new(view = nil, line = 1, options = {}, &block)
|
15
|
+
HypertemplateTemplate.new(@registry, view, line,options, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class HypertemplateTemplate < ::Tilt::Template
|
21
|
+
|
22
|
+
def initialize(registry, view = nil, line = 1,options = {}, &block)
|
23
|
+
super(view, line, options, &block)
|
24
|
+
@registry = registry
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize_engine
|
28
|
+
return if defined?(::Hypertemplate)
|
29
|
+
require_template_library 'hypertemplate'
|
30
|
+
end
|
31
|
+
|
32
|
+
def prepare
|
33
|
+
@media_type = options[:media_type]
|
34
|
+
raise Hypertemplate::BuilderError.new("Content type required to build representation.") unless @media_type
|
35
|
+
end
|
36
|
+
|
37
|
+
def precompiled_preamble(locals)
|
38
|
+
local_assigns = super
|
39
|
+
<<-RUBY
|
40
|
+
begin
|
41
|
+
unless self.class.method_defined?(:hypertemplate_registry)
|
42
|
+
def hypertemplate_registry
|
43
|
+
env['hypertemplate']
|
44
|
+
end
|
45
|
+
end
|
46
|
+
extend hypertemplate_registry[#{@media_type.inspect}].helper
|
47
|
+
#{local_assigns}
|
48
|
+
RUBY
|
49
|
+
end
|
50
|
+
|
51
|
+
def precompiled_postamble(locals)
|
52
|
+
<<-RUBY
|
53
|
+
end
|
54
|
+
RUBY
|
55
|
+
end
|
56
|
+
|
57
|
+
def precompiled_template(locals)
|
58
|
+
data.to_str
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
::Tilt.register 'hypertemplate', HypertemplateTilt.new
|
63
|
+
::Tilt.register 'tokamak', HypertemplateTilt.new
|
64
|
+
::Tilt.register 'hyper', HypertemplateTilt.new
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Hypertemplate
|
2
|
+
class Recipes
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@recipes = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def add(recipe_name, &block)
|
9
|
+
@recipes[recipe_name] = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove(recipe_name)
|
13
|
+
@recipes.delete(recipe_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](recipe_name)
|
17
|
+
@recipes[recipe_name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def list
|
21
|
+
@recipes.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Hypertemplate
|
2
|
+
class Registry
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@media_types = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def <<(handler)
|
9
|
+
handler.media_types.each do |type|
|
10
|
+
@media_types[type] = handler
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](media_type)
|
15
|
+
@media_types[media_type[/^([^\s\;]+)/, 1]]
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(media_type, handler)
|
19
|
+
@media_types[media_type] = handler
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|