hypertemplate 1.2.0

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.
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