roar 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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