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.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +27 -0
- data/Gemfile.lock +77 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +69 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/lib/tokamak.rb +21 -0
- data/lib/tokamak/atom.rb +8 -0
- data/lib/tokamak/atom/base.rb +87 -0
- data/lib/tokamak/atom/builder.rb +107 -0
- data/lib/tokamak/atom/helpers.rb +13 -0
- data/lib/tokamak/error.rb +6 -0
- data/lib/tokamak/json.rb +10 -0
- data/lib/tokamak/json/base.rb +83 -0
- data/lib/tokamak/json/builder.rb +98 -0
- data/lib/tokamak/json/helpers.rb +13 -0
- data/lib/tokamak/representation.rb +3 -0
- data/lib/tokamak/representation/atom.rb +18 -0
- data/lib/tokamak/representation/atom/atom.rng +597 -0
- data/lib/tokamak/representation/atom/base.rb +140 -0
- data/lib/tokamak/representation/atom/category.rb +39 -0
- data/lib/tokamak/representation/atom/entry.rb +56 -0
- data/lib/tokamak/representation/atom/factory.rb +48 -0
- data/lib/tokamak/representation/atom/feed.rb +108 -0
- data/lib/tokamak/representation/atom/link.rb +66 -0
- data/lib/tokamak/representation/atom/person.rb +46 -0
- data/lib/tokamak/representation/atom/source.rb +57 -0
- data/lib/tokamak/representation/atom/tag_collection.rb +36 -0
- data/lib/tokamak/representation/atom/xml.rb +94 -0
- data/lib/tokamak/representation/generic.rb +20 -0
- data/lib/tokamak/representation/json.rb +11 -0
- data/lib/tokamak/representation/json/base.rb +25 -0
- data/lib/tokamak/representation/json/keys_as_methods.rb +72 -0
- data/lib/tokamak/representation/json/link.rb +27 -0
- data/lib/tokamak/representation/json/link_collection.rb +21 -0
- data/lib/tokamak/representation/links.rb +9 -0
- data/lib/tokamak/values.rb +29 -0
- data/lib/tokamak/xml.rb +12 -0
- data/lib/tokamak/xml/base.rb +60 -0
- data/lib/tokamak/xml/builder.rb +115 -0
- data/lib/tokamak/xml/helpers.rb +13 -0
- data/lib/tokamak/xml/link.rb +31 -0
- data/lib/tokamak/xml/links.rb +35 -0
- data/spec/integration/atom/atom_spec.rb +191 -0
- data/spec/integration/full_atom.xml +92 -0
- data/spec/integration/full_json.js +46 -0
- data/spec/integration/json/json_spec.rb +172 -0
- data/spec/integration/xml/xml_spec.rb +203 -0
- data/spec/spec_helper.rb +12 -0
- 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,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
|