tokamak 1.0.0.beta2

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 (52) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +27 -0
  4. data/Gemfile.lock +77 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +69 -0
  7. data/Rakefile +50 -0
  8. data/VERSION +1 -0
  9. data/lib/tokamak.rb +21 -0
  10. data/lib/tokamak/atom.rb +8 -0
  11. data/lib/tokamak/atom/base.rb +87 -0
  12. data/lib/tokamak/atom/builder.rb +107 -0
  13. data/lib/tokamak/atom/helpers.rb +13 -0
  14. data/lib/tokamak/error.rb +6 -0
  15. data/lib/tokamak/json.rb +10 -0
  16. data/lib/tokamak/json/base.rb +83 -0
  17. data/lib/tokamak/json/builder.rb +98 -0
  18. data/lib/tokamak/json/helpers.rb +13 -0
  19. data/lib/tokamak/representation.rb +3 -0
  20. data/lib/tokamak/representation/atom.rb +18 -0
  21. data/lib/tokamak/representation/atom/atom.rng +597 -0
  22. data/lib/tokamak/representation/atom/base.rb +140 -0
  23. data/lib/tokamak/representation/atom/category.rb +39 -0
  24. data/lib/tokamak/representation/atom/entry.rb +56 -0
  25. data/lib/tokamak/representation/atom/factory.rb +48 -0
  26. data/lib/tokamak/representation/atom/feed.rb +108 -0
  27. data/lib/tokamak/representation/atom/link.rb +66 -0
  28. data/lib/tokamak/representation/atom/person.rb +46 -0
  29. data/lib/tokamak/representation/atom/source.rb +57 -0
  30. data/lib/tokamak/representation/atom/tag_collection.rb +36 -0
  31. data/lib/tokamak/representation/atom/xml.rb +94 -0
  32. data/lib/tokamak/representation/generic.rb +20 -0
  33. data/lib/tokamak/representation/json.rb +11 -0
  34. data/lib/tokamak/representation/json/base.rb +25 -0
  35. data/lib/tokamak/representation/json/keys_as_methods.rb +72 -0
  36. data/lib/tokamak/representation/json/link.rb +27 -0
  37. data/lib/tokamak/representation/json/link_collection.rb +21 -0
  38. data/lib/tokamak/representation/links.rb +9 -0
  39. data/lib/tokamak/values.rb +29 -0
  40. data/lib/tokamak/xml.rb +12 -0
  41. data/lib/tokamak/xml/base.rb +60 -0
  42. data/lib/tokamak/xml/builder.rb +115 -0
  43. data/lib/tokamak/xml/helpers.rb +13 -0
  44. data/lib/tokamak/xml/link.rb +31 -0
  45. data/lib/tokamak/xml/links.rb +35 -0
  46. data/spec/integration/atom/atom_spec.rb +191 -0
  47. data/spec/integration/full_atom.xml +92 -0
  48. data/spec/integration/full_json.js +46 -0
  49. data/spec/integration/json/json_spec.rb +172 -0
  50. data/spec/integration/xml/xml_spec.rb +203 -0
  51. data/spec/spec_helper.rb +12 -0
  52. metadata +248 -0
@@ -0,0 +1,46 @@
1
+ module Tokamak
2
+ module Representation
3
+ module Atom
4
+ class Person < XML
5
+ def initialize(node_type, options_or_obj)
6
+ if options_or_obj.kind_of?(Hash)
7
+ @doc = Nokogiri::XML::Document.new()
8
+ node = @doc.create_element(node_type)
9
+ node.add_namespace_definition(nil, "http://www.w3.org/2005/Atom")
10
+ node.parent = @doc
11
+ super(node)
12
+ options_or_obj.each do |key,value|
13
+ self.send("#{key}=".to_sym, value)
14
+ end
15
+ else
16
+ super(options_or_obj)
17
+ end
18
+ end
19
+
20
+ def name
21
+ text("name")
22
+ end
23
+
24
+ def name=(value)
25
+ set_text("name", value)
26
+ end
27
+
28
+ def uri
29
+ text("uri")
30
+ end
31
+
32
+ def uri=(value)
33
+ set_text("uri", value)
34
+ end
35
+
36
+ def email
37
+ text("email")
38
+ end
39
+
40
+ def email=(value)
41
+ set_text("email", value)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,57 @@
1
+ module Tokamak
2
+ module Representation
3
+ module Atom
4
+ class Source < XML
5
+ def initialize(options_or_obj)
6
+ if options_or_obj.kind_of?(Hash)
7
+ @doc = Nokogiri::XML::Document.new()
8
+ node = @doc.create_element("source")
9
+ node.add_namespace_definition(nil, "http://www.w3.org/2005/Atom")
10
+ node.parent = @doc
11
+ super(node)
12
+ options_or_obj.each do |key,value|
13
+ self.send("#{key}=".to_sym, value)
14
+ end
15
+ else
16
+ super(options_or_obj)
17
+ end
18
+ end
19
+
20
+ def id
21
+ text("id")
22
+ end
23
+
24
+ def id=(value)
25
+ set_text("id", value)
26
+ end
27
+
28
+ # text
29
+ def title
30
+ text("title")
31
+ end
32
+
33
+ def title=(value)
34
+ set_text("title", value)
35
+ end
36
+
37
+ def updated
38
+ value = text("updated")
39
+ Time.parse(value) unless value.nil?
40
+ end
41
+
42
+ def updated=(value)
43
+ set_text("updated", value)
44
+ end
45
+
46
+ # text
47
+ def rights
48
+ text("rights")
49
+ end
50
+
51
+ def rights=(value)
52
+ set_text("rights", value)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,36 @@
1
+ module Tokamak
2
+ module Representation
3
+ module Atom
4
+ class TagCollection < ::Array
5
+ def initialize(parent_node, &block)
6
+ @node = parent_node
7
+ @method_missing_block = block_given? ? block : nil
8
+ super(0)
9
+ end
10
+
11
+ def <<(obj)
12
+ obj = [obj] unless obj.kind_of?(Array)
13
+ obj.each do |o|
14
+ o.doc.parent = @node
15
+ super(o)
16
+ end
17
+ end
18
+
19
+ def delete(obj)
20
+ if super(obj)
21
+ obj.doc.unlink
22
+ obj = nil
23
+ end
24
+ end
25
+
26
+ def method_missing(symbol, *args, &block)
27
+ if @method_missing_block
28
+ @method_missing_block.call(self, symbol, *args)
29
+ else
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,94 @@
1
+ module Tokamak
2
+ module Representation
3
+ module Atom
4
+ class XML
5
+ attr_accessor :doc
6
+
7
+ def initialize(xml_obj = nil)
8
+ if xml_obj
9
+ xml_obj.kind_of?(Nokogiri::XML::Document) ? @doc = xml_obj.root : @doc = xml_obj
10
+ else
11
+ @doc = Nokogiri::XML::Document.new
12
+ root_node = @doc.create_element(atom_type.to_s)
13
+ root_node.add_namespace_definition(nil, "http://www.w3.org/2005/Atom")
14
+ root_node.parent = @doc
15
+ @doc = @doc.root
16
+ end
17
+ end
18
+
19
+ # Tools
20
+ def css(*args)
21
+ @doc.css(*args)
22
+ end
23
+
24
+ def xpath(*args)
25
+ @doc.xpath(*args)
26
+ end
27
+
28
+ def atom_type
29
+ self.class.name.demodulize.downcase
30
+ end
31
+
32
+ def to_xml
33
+ @doc.to_xml
34
+ end
35
+
36
+ def to_s
37
+ to_xml
38
+ end
39
+
40
+ def method_missing(method, *args, &block)
41
+ text(method) || super
42
+ end
43
+
44
+ protected
45
+
46
+ def text(node_name)
47
+ (node = @doc.at_xpath("xmlns:#{node_name}")) ? node.content : nil
48
+ end
49
+
50
+ def set_text(node_name, value)
51
+ if (node = @doc.at_xpath("xmlns:#{node_name}"))
52
+ value = value.xmlschema if value.kind_of?(Time) || value.kind_of?(DateTime)
53
+ node.content = value
54
+ else
55
+ node = create_element(node_name, value)
56
+ node.parent = @doc
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def create_element(node, *args)
63
+ node = @doc.document.create_element(node) do |n|
64
+ args.each do |arg|
65
+ include_arg n, arg
66
+ end
67
+ end
68
+ end
69
+
70
+ def include_arg(n, arg)
71
+ case arg
72
+ when Hash
73
+ # Adding XML attributes
74
+ arg.each { |k,v|
75
+ key = k.to_s
76
+ if key =~ /^xmlns(:\w+)?$/
77
+ ns_name = key.split(":", 2)[1]
78
+ n.add_namespace_definition(ns_name, v)
79
+ return
80
+ end
81
+ n[k.to_s] = v.to_s
82
+ }
83
+ else
84
+ # Adding XML node content
85
+ arg.kind_of?(Time) || arg.kind_of?(DateTime) ? content = arg.xmlschema : content = arg
86
+ n.content = content
87
+ end
88
+ end
89
+
90
+
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,20 @@
1
+ module Tokamak
2
+ # Unknown representation's unmarshalling on the client side
3
+ module Representation
4
+ class Generic
5
+
6
+ # Because there is no media type registered, return the content itself
7
+ def unmarshal(content)
8
+ def content.links
9
+ []
10
+ end
11
+ content
12
+ end
13
+
14
+ def marshal(string, rel)
15
+ string
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ module Tokamak
2
+ module Representation
3
+ class Json
4
+ autoload :Base, 'tokamak/representation/json/base'
5
+ autoload :KeysAsMethods, 'tokamak/representation/json/keys_as_methods'
6
+ autoload :Link, 'tokamak/representation/json/link'
7
+ autoload :LinkCollection, 'tokamak/representation/json/link_collection'
8
+ extend Base::ClassMethods
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ module Tokamak
2
+ module Representation
3
+ class Json
4
+ module Base
5
+ module ClassMethods
6
+
7
+ # creates a json unmarshalled version of this object
8
+ def create(obj = nil)
9
+ @json = {}
10
+ return @json.extend(KeysAsMethods) unless obj
11
+
12
+ if obj.kind_of?(Hash) || obj.kind_of?(Array)
13
+ @json = obj
14
+ else
15
+ @json = ::JSON.parse(obj)
16
+ end
17
+
18
+ @json.extend(KeysAsMethods)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,72 @@
1
+ module Tokamak
2
+ module Representation
3
+ class Json
4
+ module KeysAsMethods
5
+ def self.extended(base)
6
+ [:type, :id].each { |m| base.__free_method__(m) }
7
+ end
8
+
9
+ def [](key)
10
+ __normalize__(super(key))
11
+ end
12
+
13
+ def []=(key, value)
14
+ super(key,value)
15
+ end
16
+
17
+ def to_s
18
+ super.to_json
19
+ end
20
+
21
+ def method_missing(name, *args)
22
+ method_name = name.to_s
23
+ if method_name[-1] == "="[-1] #1.8 and 1.9 compatible
24
+ self[method_name.chop] = args[0]
25
+ else
26
+ self[method_name]
27
+ end
28
+ end
29
+
30
+ # if you have a key that is also a method (such as Array#size)
31
+ # you can use this to free the method and use the method obj.size
32
+ # to access the value of key "size".
33
+ # you still can access the old method with __[method_name]__
34
+ def __free_method__(sym)
35
+ if self.__metaclass__.method_defined?(sym) && !respond_to?("__#{sym}__")
36
+ self.__metaclass__.send(:alias_method, "__#{sym}__", sym)
37
+ end
38
+ self.__metaclass__.send(:define_method, sym) { method_missing(sym.to_s) }
39
+ self
40
+ end
41
+
42
+ def __metaclass__
43
+ class << self; self; end
44
+ end
45
+
46
+ include Tokamak::Representation::Links
47
+
48
+ # easy accessors to links
49
+ def links
50
+ some_links = self["link"]
51
+ return nil unless some_links
52
+ some_links = [some_links] unless some_links.kind_of? Array
53
+ LinkCollection.new(some_links)
54
+ end
55
+
56
+ private
57
+
58
+ def __normalize__(value)
59
+ case value
60
+ when Hash
61
+ value.extend(KeysAsMethods)
62
+ when Array
63
+ value.map { |v| __normalize__(v) }
64
+ else
65
+ value
66
+ end
67
+ value
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,27 @@
1
+ module Tokamak
2
+ module Representation
3
+ class Json
4
+ class Link
5
+ def initialize(obj)
6
+ @obj = obj
7
+ end
8
+
9
+ def type
10
+ @obj.type
11
+ end
12
+
13
+ def href
14
+ @obj.href
15
+ end
16
+
17
+ def rel
18
+ @obj.rel
19
+ end
20
+
21
+ def method_missing(symbol, *args, &block)
22
+ @obj.send(symbol, *args, &block)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module Tokamak
2
+ module Representation
3
+ class Json
4
+ class LinkCollection
5
+ def initialize(parent_node)
6
+ @node = parent_node
7
+ end
8
+
9
+ def method_missing(symbol, *args, &block)
10
+ linkset = @node.select {|link| link.rel == symbol.to_s }
11
+ linkset.map! { |link| Link.new(link) }
12
+ unless linkset.empty?
13
+ linkset.size == 1 ? linkset.first : linkset
14
+ else
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Tokamak
2
+ module Representation
3
+ module Links
4
+ def self.extract_link_header(links)
5
+ links.collect {|link| "<#{link.href}>; rel=#{link.rel}"}.join(', ')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module Tokamak
2
+ # This is a Blank Slate class to support the renderization of the values block for Resource Representation Interfaces
3
+ # Every Media type should implement a Builder with a insert_value method that renders the values block to a specific format
4
+ class Values
5
+ attr_accessor :builder
6
+
7
+ # remove all methods from this class
8
+ instance_methods.each do |m|
9
+ undef_method m unless m.to_s =~ /\[\]|method_missing|send|respond_to\?|^__/
10
+ end
11
+
12
+ def initialize(builder)
13
+ @builder = builder
14
+ @current_prefix = nil
15
+ end
16
+
17
+ def [](prefix)
18
+ @current_prefix = prefix
19
+ self
20
+ end
21
+
22
+ def method_missing(symbol, *args, &block)
23
+ name = symbol.to_s
24
+ prefix = @current_prefix
25
+ @current_prefix = nil
26
+ @builder.insert_value(name, prefix, *args, &block)
27
+ end
28
+ end
29
+ end