hypertemplate 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/LICENSE +29 -0
  2. data/README.md +134 -0
  3. data/lib/hypertemplate.rb +14 -0
  4. data/lib/hypertemplate/builder.rb +33 -0
  5. data/lib/hypertemplate/builder/base.rb +111 -0
  6. data/lib/hypertemplate/builder/json.rb +132 -0
  7. data/lib/hypertemplate/builder/values.rb +33 -0
  8. data/lib/hypertemplate/builder/xml.rb +119 -0
  9. data/lib/hypertemplate/errors.rb +3 -0
  10. data/lib/hypertemplate/hook.rb +6 -0
  11. data/lib/hypertemplate/hook/rails.rb +112 -0
  12. data/lib/hypertemplate/hook/sinatra.rb +44 -0
  13. data/lib/hypertemplate/hook/tilt.rb +68 -0
  14. data/lib/hypertemplate/recipes.rb +25 -0
  15. data/lib/hypertemplate/registry.rb +24 -0
  16. data/lib/hypertemplate/version.rb +13 -0
  17. data/script/console +7 -0
  18. data/test/hypertemplate/builder/base_test.rb +45 -0
  19. data/test/hypertemplate/builder/json_test.rb +506 -0
  20. data/test/hypertemplate/builder/values_test.rb +12 -0
  21. data/test/hypertemplate/builder/xml_test.rb +479 -0
  22. data/test/hypertemplate/helper_test.rb +116 -0
  23. data/test/hypertemplate/hook/rails_test.rb +77 -0
  24. data/test/hypertemplate/hook/sinatra_test.rb +89 -0
  25. data/test/hypertemplate/hook/tilt_test.rb +48 -0
  26. data/test/hypertemplate/recipes_test.rb +94 -0
  27. data/test/rails2_skel/Rakefile +16 -0
  28. data/test/rails2_skel/app/controllers/application_controller.rb +1 -0
  29. data/test/rails2_skel/app/controllers/test_controller.rb +18 -0
  30. data/test/rails2_skel/app/views/test/_feed_member.tokamak +9 -0
  31. data/test/rails2_skel/app/views/test/feed.tokamak +24 -0
  32. data/test/rails2_skel/app/views/test/show.hyper +31 -0
  33. data/test/rails2_skel/config/boot.rb +110 -0
  34. data/test/rails2_skel/config/environment.rb +20 -0
  35. data/test/rails2_skel/config/environments/development.rb +17 -0
  36. data/test/rails2_skel/config/environments/production.rb +28 -0
  37. data/test/rails2_skel/config/environments/test.rb +28 -0
  38. data/test/rails2_skel/config/initializers/cookie_verification_secret.rb +2 -0
  39. data/test/rails2_skel/config/initializers/mime_types.rb +3 -0
  40. data/test/rails2_skel/config/initializers/new_rails_defaults.rb +10 -0
  41. data/test/rails2_skel/config/initializers/session_store.rb +5 -0
  42. data/test/rails2_skel/config/routes.rb +43 -0
  43. data/test/rails2_skel/log/development.log +10190 -0
  44. data/test/rails2_skel/script/console +3 -0
  45. data/test/test_helper.rb +9 -0
  46. 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,3 @@
1
+ module Hypertemplate
2
+ class BuilderError < StandardError; end
3
+ end
@@ -0,0 +1,6 @@
1
+ module Hypertemplate
2
+ module Hook
3
+ autoload :Rails , "hypertemplate/hook/rails"
4
+ autoload :Tilt , "hypertemplate/hook/tilt"
5
+ end
6
+ 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
+
@@ -0,0 +1,13 @@
1
+ module Hypertemplate
2
+ module VERSION #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 2
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+
9
+ def self.to_s
10
+ STRING
11
+ end
12
+ end
13
+ end