roar 0.8.0 → 0.8.1

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/lib/roar/model.rb DELETED
@@ -1,36 +0,0 @@
1
- module Roar
2
- # Basic methods needed to implement the ActiveModel API. Gives your model +#attributes+ and +model_name+.
3
- # Include this for quickly converting an object to a ROAR-compatible monster.
4
- #
5
- # #DISCUSS: are Models used both on client- and server-side? I'd say hell yeah.
6
- module Model
7
- extend ActiveSupport::Concern
8
-
9
- module ClassMethods
10
- def model_name
11
- ActiveSupport::Inflector.underscore(self) # We don't use AM::Naming for now.
12
- end
13
-
14
- def accessors(*names)
15
- names.each do |name|
16
- class_eval %Q{
17
- def #{name}=(v)
18
- attributes["#{name}"] = v
19
- end
20
-
21
- def #{name}
22
- attributes["#{name}"]
23
- end
24
- }
25
- end
26
- end
27
- end
28
-
29
-
30
- attr_accessor :attributes
31
-
32
- def initialize(attributes={})
33
- @attributes = attributes
34
- end
35
- end
36
- end
@@ -1,31 +0,0 @@
1
- module Roar
2
- module Model
3
- module Representable
4
- extend ActiveSupport::Concern
5
-
6
- included do |base|
7
- base.extend Hooks::InheritableAttribute
8
- base.inheritable_attr :representable
9
- base.representable = {} # FIXME: doesn't that break inheritance?
10
- end
11
-
12
- module ClassMethods
13
- def represents(mime_type, options)
14
- self.representable[mime_type] = options[:with]
15
- end
16
-
17
- def representer_class_for(mime_type)
18
- representable[mime_type]
19
- end
20
-
21
- def from(mime_type, representation)
22
- representer_class_for(mime_type).deserialize(self, mime_type, representation)
23
- end
24
- end
25
-
26
- def to(mime_type)
27
- self.class.representer_class_for(mime_type).new.serialize(self, mime_type)
28
- end
29
- end
30
- end
31
- end
@@ -1,72 +0,0 @@
1
- require 'representable'
2
-
3
- module Roar
4
- module Representer
5
- class Base
6
- include Representable
7
-
8
- class << self
9
- alias_method :property, :representable_property
10
- alias_method :collection, :representable_collection
11
-
12
- # Creates a representer instance and fills it with +attributes+.
13
- def from_attributes(attributes)
14
- new.tap do |representer|
15
- yield representer if block_given?
16
-
17
- representable_attrs.each do |definition|
18
- definition.populate(representer, attributes)
19
- end
20
- end
21
- end
22
- end
23
-
24
-
25
- def initialize(properties={})
26
- properties.each { |p,v| send("#{p}=", v) } # DISCUSS: check if valid property?
27
- end
28
-
29
- # Convert representer's attributes to a nested attributes hash.
30
- def to_attributes
31
- {}.tap do |attributes|
32
- self.class.representable_attrs.each do |definition|
33
- value = public_send(definition.accessor)
34
-
35
- if definition.typed?
36
- value = definition.apply(value) do |v|
37
- v.to_attributes # applied to each typed attribute (even in collections).
38
- end
39
- end
40
-
41
- attributes[definition.accessor] = value
42
- end
43
- end
44
- end
45
- end
46
-
47
- class LinksDefinition < Representable::Definition
48
- def rel2block
49
- @rel2block ||= []
50
- end
51
-
52
- def populate(representer, *args)
53
- representer.links ||= []
54
-
55
- rel2block.each do |link|
56
- representer.links << sought_type.from_attributes({ # create Hyperlink representer.
57
- "rel" => link[:rel],
58
- "href" => representer.instance_exec(&link[:block])}) # DISCUSS: run block in representer context?
59
- end
60
- end
61
- end
62
-
63
- end
64
- end
65
-
66
- # FIXME: move to some init asset.
67
- Representable::Definition.class_eval do
68
- # Populate the representer's attribute with the right value.
69
- def populate(representer, attributes)
70
- representer.public_send("#{accessor}=", attributes[accessor])
71
- end
72
- end
@@ -1,35 +0,0 @@
1
- require 'test_helper'
2
- require 'roar/representer/feature/hypermedia'
3
-
4
- class HypermediaTest
5
- describe "Hypermedia" do
6
- class Bookmarks
7
- include Roar::Representer::Feature::Hypermedia
8
- end
9
-
10
- before do
11
- Hyperlink = Roar::Representer::XML::Hyperlink # TODO: move to abstract module.
12
- @b = Bookmarks.new
13
- @b.links = [Hyperlink.from_attributes({"rel" => "self", "href" => "http://self"}), Hyperlink.from_attributes({"rel" => "next", "href" => "http://next"})]
14
- end
15
-
16
- describe "#links" do
17
- it "returns links" do
18
- assert_kind_of Roar::Representer::Feature::Hypermedia::LinkCollection, @b.links
19
- assert_equal 2, @b.links.size
20
- end
21
-
22
- it "works with empty links set" do
23
- assert_equal nil, Bookmarks.new.links # default empty array doesn't make sense.
24
- end
25
- end
26
-
27
-
28
- it "responds to links #[]" do
29
- assert_equal "http://self", @b.links["self"]
30
- assert_equal "http://self", @b.links[:self]
31
- assert_equal "http://next", @b.links[:next]
32
- assert_equal nil, @b.links[:prev]
33
- end
34
- end
35
- end
data/test/model_test.rb DELETED
@@ -1,50 +0,0 @@
1
- require 'test_helper'
2
-
3
- class ModelTest < MiniTest::Spec
4
- describe "HttpVerbs" do
5
- before do
6
- @klass = Class.new(TestModel) do
7
- include Roar::Model::HttpVerbs
8
-
9
- self.resource_base = "http://localhost:9999/test/"
10
- end
11
- @o = @klass.new
12
- end
13
-
14
- it "has resource_base* accessors for setting the uri base path" do
15
- assert_equal "http://localhost:9999/test/", @klass.resource_base
16
- end
17
-
18
- it ".get returns deserialized object from " do
19
- assert_equal({"id" => "4711"}, @klass.get(4711).attributes)
20
- end
21
- end
22
-
23
-
24
- describe "The Model API" do
25
- class Dog
26
- include Roar::Model
27
-
28
- accessors :name
29
- end
30
-
31
- before do
32
- @klass = Dog
33
- end
34
-
35
- it "the constructor accepts attributes" do
36
- assert_equal({"id" => "4711"}, @klass.new({"id" => "4711"}).attributes)
37
- end
38
-
39
- it "responds to .model_name" do
40
- assert_equal "model_test/dog", @klass.model_name
41
- end
42
-
43
- it "lets .accessors create accessors" do
44
- @o = @klass.new({"name" => "Joe"})
45
- assert_equal "Joe", @o.name
46
- @o.name= "Noe"
47
- assert_equal "Noe", @o.name
48
- end
49
- end
50
- end
data/test/proxy_test.rb DELETED
@@ -1,89 +0,0 @@
1
- require 'test_helper'
2
-
3
- class ProxyTest < MiniTest::Spec
4
- describe "Transport" do
5
- before do
6
- @klass = Class.new(Object) do
7
- include Roar::Client::Transport
8
- end
9
- @o = @klass.new
10
- end
11
-
12
- it "#get_uri returns Restfulie response" do
13
- assert_equal "<test><id>4711</id></test>", @o.get_uri("http://localhost:9999/test/4711").body
14
- end
15
- end
16
-
17
-
18
- describe "The public Proxy API" do
19
- before do
20
- @klass = Class.new(TestModel) do
21
- extend Roar::Client::Proxy
22
- end
23
- end
24
-
25
- it "responds to .get_model" do
26
- @o = @klass.get_model("http://localhost:9999/test/1", TestModel)
27
- assert_kind_of TestModel, @o
28
- assert_equal({"id" => "1"}, @o.attributes)
29
- end
30
- end
31
-
32
- describe "EntityProxy" do
33
- before do
34
- Proxy = Roar::Client::EntityProxy
35
- @proxy_class = Proxy.class_for(:class => TestModel)
36
- end
37
-
38
- it ".class_for returns an EntityProxy subclass" do
39
- @proxy = @proxy_class.new
40
- assert_kind_of Proxy, @proxy
41
- end
42
-
43
- it "responds to .options" do
44
- assert_equal({:class => TestModel}, @proxy_class.send(:options))
45
- end
46
-
47
- it "doesn't override superclass options" do
48
- assert_equal nil, Proxy.options
49
- end
50
-
51
- it "responds to .from_attributes and responds to #original_attributes" do
52
- assert_equal({:urn => "urn:item"}, @proxy_class.from_attributes(:urn => "urn:item").send(:original_attributes))
53
- end
54
-
55
- it "responds to .model_name with the proxied name" do
56
- assert_equal "test", @proxy_class.model_name
57
- end
58
-
59
- # finalize!
60
- describe "#finalize!" do
61
- before do
62
- @o = @proxy_class.from_attributes("uri" => "http://localhost:9999/test/1")
63
- end
64
-
65
- it "responds to #proxied_resource" do
66
- assert_nil @o.send(:proxied_resource)
67
- end
68
-
69
- it "#finalize! retrieves proxied resource" do
70
- @o.finalize!
71
- assert_kind_of TestModel, @o.send(:proxied_resource)
72
- end
73
-
74
- # delegation:
75
- it "#attributes are delegated" do
76
- @o.finalize!
77
- assert_equal({"id" => "1"}, @o.attributes)
78
- end
79
-
80
- it "#to_xml renders the unproxied entity" do
81
- assert_equal "<test>\n <uri>http://localhost:9999/test/1</uri>\n</test>\n", @o.to_xml
82
- end
83
-
84
- it "#attributes_for_xml returns the unfinalized EntityProxy#attributes hash" do
85
- assert_equal({"uri"=>"http://localhost:9999/test/1"}, @o.attributes_for_xml)
86
- end
87
- end
88
- end
89
- end
@@ -1,49 +0,0 @@
1
- require 'test_helper'
2
- require 'roar/model/representable'
3
-
4
- class RepresentableTest < MiniTest::Spec
5
-
6
- class ThisAsAppXml < Roar::Representer::Base
7
- def self.deserialize(represented_class, mime_type, data)
8
- "#{represented_class.name}->#{mime_type}: #{data}"
9
- end
10
-
11
- def serialize(represented, mime_type)
12
- "#{represented.class.name}->#{mime_type}"
13
- end
14
- end
15
-
16
- describe "Representable" do
17
- before do
18
- @c = Class.new do
19
- include Roar::Model::Representable
20
-
21
- represents "application/xml", :with => ThisAsAppXml
22
- end
23
- end
24
-
25
-
26
- describe ".representer_class_for" do
27
- it "returns the class" do
28
- assert_equal ThisAsAppXml, @c.representer_class_for("application/xml")
29
- end
30
-
31
- it "returns nil if unknown" do
32
- assert_equal nil, @c.representer_class_for("text/html")
33
- end
34
- end
35
-
36
-
37
- describe ".from" do
38
- it "receives mime_type and content" do
39
- assert_equal "->application/xml: <xml/>", @c.from("application/xml", "<xml/>")
40
- end
41
- end
42
-
43
- describe "#to" do
44
- it "receives mime_type" do
45
- assert_equal "->application/xml", @c.new.to("application/xml")
46
- end
47
- end
48
- end
49
- end
@@ -1,144 +0,0 @@
1
- require 'test_helper'
2
-
3
- module Roar
4
- module Representer
5
- class Ruby < Base
6
- def serialize(attributes)
7
- serializable_attributes = attributes.dup
8
-
9
- serialize_typed_attributes(serializable_attributes)
10
- Marshal.dump serializable_attributes
11
- end
12
-
13
- def deserialize(body)
14
- deserialized_hash = Marshal.load body
15
- deserialize_typed_attributes(deserialized_hash)
16
- end
17
-
18
-
19
-
20
- module ConfigurationDsl # FIXME: move to Representer::BaseDsl or so.
21
- def has_many(name, options={})
22
- collections[name] = options
23
- end
24
- alias_method :collection, :has_many
25
-
26
- def has_one(name, options={})
27
- typed_entities[name] = options
28
- end
29
-
30
- def has_proxied(name, options={})
31
- has_one(name, {:class => EntityProxy.class_for(options)})
32
- end
33
-
34
- def has_many_proxied(name, options={})
35
- has_many(name, {:class => EntityProxy.class_for(options)})
36
- end
37
- end
38
-
39
- self.extend ConfigurationDsl
40
- # FIXME: use one variable?
41
- extend Hooks::InheritableAttribute
42
- inheritable_attr :collections
43
- self.collections = {}
44
- inheritable_attr :typed_entities
45
- self.typed_entities = {}
46
-
47
-
48
-
49
- def serialize_typed_attributes(attributes)
50
- filter_attributes_for(attributes, self.class.typed_entities) do |name, options|
51
- next unless entity = attributes[name]
52
- attributes[name] = entity.to(mime_type)
53
- end
54
- end
55
-
56
- # Attributes can be typecasted with +has_one+.
57
- def deserialize_typed_attributes(attributes)
58
- filter_attributes_for(attributes, self.class.typed_entities) do |name, options|
59
- item = attributes.delete(name) # attributes[:sum]
60
- item = options[:class].from(mime_type, item) # Sum.from_xml_attributes
61
- attributes[name] = item
62
- end
63
- end
64
-
65
- def filter_attributes_for(attributes, config)
66
- config.each do |name, options|
67
- name = name.to_s
68
- yield name, options
69
- end
70
- attributes
71
- end
72
-
73
- end
74
-
75
-
76
- end
77
-
78
- end
79
-
80
-
81
- class RubyRepresenterFunctionalTest < MiniTest::Spec
82
- class Item
83
- attr_reader :value
84
-
85
- def initialize(value)
86
- @value = value
87
- end
88
-
89
- def to(mime_type)
90
- raise unless mime_type == "ruby/serialized"
91
- Marshal.dump(@value)
92
- end
93
-
94
- def self.from(mime_type, string)
95
- raise unless mime_type == "ruby/serialized"
96
- new Marshal.load(string)
97
- end
98
-
99
- def ==(b)
100
- value == b.value
101
- end
102
- end
103
-
104
- describe "RubyRepresenter" do
105
- before do
106
- @r = Class.new(Roar::Representer::Ruby).new("ruby/serialized")
107
- @m = {:id => 1}
108
- end
109
-
110
- describe "without options" do
111
- it "#serialize returns the serialized model" do
112
- assert_equal "\x04\b{\x06:\aidi\x06", @r.serialize(@m)
113
- end
114
-
115
- it "#deserialize returns the attributes" do
116
- assert_equal @m, @r.deserialize("\x04\b{\x06:\aidi\x06")
117
- end
118
- end
119
-
120
-
121
- describe "has_one" do
122
- before do
123
- @r.class.instance_eval do
124
- has_one :item, :class => Item
125
- end
126
- end
127
-
128
- it "#serialize skips empty :item" do
129
- assert_equal "\x04\b{\x06:\aidi\x06", @r.serialize(@m)
130
- end
131
-
132
- it "#serialize delegates to item#to" do
133
- @m = {:id => 1, "item" => Item.new("beer")}
134
- assert_equal "\x04\b{\a:\aidi\x06I\"\titem\x06:\x06EF\"\x13\x04\bI\"\tbeer\x06:\x06EF", @r.serialize(@m)
135
- end
136
-
137
- it "#deserialize typecasts :item" do
138
- m = @r.deserialize("\x04\b{\a:\aidi\x06I\"\titem\x06:\x06EF\"\x13\x04\bI\"\tbeer\x06:\x06EF")
139
- assert_equal({:id => 1, "item" => Item.new("beer")}, m)
140
- end
141
-
142
- end
143
- end
144
- end