api-presenter 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -50,12 +50,9 @@ In order to be able to represent this resource we need to have a PersonResource
50
50
 
51
51
  ```ruby
52
52
  class PersonResource < Api::Presenter::Resource
53
- def self.hypermedia_properties
54
- {
55
- simple: [:name, :age],
56
- resource: []
57
- }
58
- end
53
+
54
+ property :name
55
+ property :age
59
56
 
60
57
  def self_link
61
58
  "/person/#{@resource.name}"
@@ -63,10 +60,7 @@ class PersonResource < Api::Presenter::Resource
63
60
  end
64
61
  ```
65
62
 
66
- The class method ```hypermedia_properties``` describes the model properties:
67
-
68
- * simple: An array containing method names that represent simple data (integers, strings, dates, etc.).
69
- * resource: An array containing method names thar represent related resources.
63
+ We should use the property method to define each of our resource properties.
70
64
 
71
65
  The self_link definition tell which is the link that represents itself.
72
66
 
@@ -88,7 +82,7 @@ data = Person.new("Alvaro", 27)
88
82
 
89
83
  resource = data.to_resource # or just PersonResource.new(data)
90
84
 
91
- Api::Presenter::Hypermedia.present resource
85
+ resource.present # or Api::Presenter::Hypermedia.present resource
92
86
  ```
93
87
 
94
88
  It will look like this:
@@ -115,12 +109,8 @@ Using the example above, now our person has a dog. So:
115
109
 
116
110
  ```ruby
117
111
  class DogResource < Api::Presenter::Resource
118
- def self.hypermedia_properties
119
- {
120
- simple: [:name],
121
- resource: [:owner]
122
- }
123
- end
112
+ property name
113
+ property owner
124
114
 
125
115
  def self_link
126
116
  "/dog/#{@resource.name}"
@@ -141,12 +131,9 @@ class Dog
141
131
  end
142
132
 
143
133
  class PersonResource < Api::Presenter::Resource
144
- def self.hypermedia_properties
145
- {
146
- simple: [:name, :age],
147
- resource: [:dog]
148
- }
149
- end
134
+ property :name
135
+ property :age
136
+ property :dog
150
137
  end
151
138
  ```
152
139
 
@@ -159,7 +146,7 @@ dog = Dog.new("Cleo", person)
159
146
 
160
147
  person_resource = person.to_resource # or just PersonResource.new(person)
161
148
 
162
- Api::Presenter::Hypermedia.present person_resource
149
+ person_resource.present # Api::Presenter::Hypermedia.present person_resource
163
150
  ```
164
151
 
165
152
  It will look like this:
@@ -20,7 +20,4 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
-
24
- spec.add_dependency "multi_json", "~> 1.7.4"
25
- spec.add_dependency "json", "~> 1.8.0"
26
23
  end
@@ -1,13 +1,12 @@
1
1
  module Api
2
2
  module Presenter
3
- class CollectionResource < Resource
4
- def self.hypermedia_properties
5
- {
6
- simple: [:offset, :limit, :total,:entries],
7
- resource: []
8
- }
9
- end
10
-
3
+ class CollectionResource < Resource
4
+
5
+ property :offset
6
+ property :limit
7
+ property :total
8
+ property :entries
9
+
11
10
  def entries
12
11
  @resource
13
12
  end
@@ -1,54 +1,53 @@
1
1
  module Api
2
2
  module Presenter
3
3
  class Hypermedia
4
- def self.present(resource)
5
- # Initialize representation with links
6
- representation = build_links resource
4
+ class << self
5
+ def present(resource)
6
+ # Initialize representation with links
7
+ representation = build_links resource
7
8
 
8
- present_properties resource, representation
9
+ present_properties resource, representation
9
10
 
10
- MultiJson.dump representation
11
- end
12
-
13
- # Process basic information from the resource such as
14
- # simlple fields(Dates, numbers, etc.) and also more
15
- # complex ones that may be subject of expansion.
16
- def self.present_properties(resource, representation)
17
- properties = resource.class.hypermedia_properties
18
-
19
- present_simple_properties resource, properties, representation
20
-
21
- present_resource_properties resource, properties, representation
22
- end
23
-
24
- def self.present_simple_properties(resource, properties, representation)
25
- entries_property = properties[:simple].delete(:entries)
26
-
27
- if entries_property
28
- representation[entries_property.to_s] = []
29
- resource.send(entries_property).each do |nested_resource|
30
- representation[entries_property.to_s] << build_links(nested_resource.to_resource, embed: true)
31
- end
11
+ representation
32
12
  end
33
13
 
34
- properties[:simple].each do |property|
35
- representation[property.to_s] = resource.send(property)
14
+ # Process basic information from the resource such as
15
+ # simlple fields(Dates, numbers, etc.) and also more
16
+ # complex ones that may be subject of expansion.
17
+ def present_properties(resource, representation)
18
+ resource_properties = resource.class.properties
19
+
20
+ # Special treatment for entries
21
+ entries_property = resource_properties.delete(:entries)
22
+
23
+ if entries_property
24
+ representation[entries_property.to_s] = []
25
+ resource.send(entries_property).each do |nested_resource|
26
+ representation[entries_property.to_s] << build_links(nested_resource.to_resource, embed: true)
27
+ end
28
+ end
29
+
30
+ # Now the other muggles
31
+ resource_properties.each do |property_name|
32
+ property_value = resource.send(property_name)
33
+
34
+ if property_value.kind_of?(Resource) || property_value.respond_to?(:to_resource)
35
+ # Resource like properties
36
+ property_value = property_value.to_resource if property_value.respond_to? :to_resource
37
+ representation["links"][property_name.to_s] = property_value.links(embed: resource.kind_of?(CollectionResource))
38
+ # we only need the "self" contents
39
+ representation["links"][property_name.to_s] = representation["links"][property_name.to_s]["self"] if representation["links"][property_name.to_s]["self"]
40
+ else
41
+ # Non-Resource like properties
42
+ representation[property_name.to_s] = property_value
43
+ end
44
+ end
36
45
  end
37
- end
38
46
 
39
- def self.present_resource_properties(resource, properties, representation)
40
- properties[:resource].each do |property|
41
- relation = resource.send(property)
42
- relation_resource = (relation.kind_of? Resource) ? relation : relation.to_resource
43
- representation["links"][property.to_s] = relation_resource.links(embed: resource.kind_of?(CollectionResource))
44
- # we only need the "self" contents
45
- representation["links"][property.to_s] = representation["links"][property.to_s]["self"] if representation["links"][property.to_s]["self"]
47
+ def build_links(resource, options = {})
48
+ { "links" => resource.links(options) }
46
49
  end
47
50
  end
48
-
49
- def self.build_links(resource, options = {})
50
- { "links" => resource.links(options) }
51
- end
52
51
  end
53
52
  end
54
53
  end
@@ -4,17 +4,30 @@ module Api
4
4
  def initialize(resource)
5
5
  @resource = resource
6
6
  end
7
-
7
+
8
+ class << self
9
+ def property(value)
10
+ properties << value unless properties.include? value
11
+ end
12
+
13
+ def properties
14
+ @properties ||= []
15
+ end
16
+
17
+ def inherited(subclass)
18
+ subclass.properties << properties
19
+ subclass.properties.flatten!
20
+ end
21
+ end
22
+
8
23
  def method_missing(method, *args, &block)
9
- allowed_methods = self.class.hypermedia_properties[:simple].concat self.class.hypermedia_properties[:resource]
10
-
11
- if allowed_methods.include? method
24
+ if self.class.properties.include? method
12
25
  @resource.send(method, *args, &block)
13
26
  else
14
27
  super
15
28
  end
16
29
  end
17
-
30
+
18
31
  def links(options = {})
19
32
  links = {}
20
33
 
@@ -22,13 +35,17 @@ module Api
22
35
  link_name = link_method.to_s.split("_").first
23
36
  links[link_name] = { "href" => self.send(link_method) } if self.send(link_method.to_s + "?", options)
24
37
  end
25
-
38
+
26
39
  links
27
40
  end
28
-
41
+
29
42
  def self_link?(options = {})
30
43
  true
31
44
  end
45
+
46
+ def present
47
+ Api::Presenter::Hypermedia.present self
48
+ end
32
49
  end
33
50
  end
34
51
  end
@@ -2,20 +2,14 @@ module Api
2
2
  module Presenter
3
3
  class SearchResource < CollectionResource
4
4
  attr_reader :query
5
-
6
- def initialize(resource, query = {})
5
+
6
+ def initialize(resource, query)
7
7
  @resource = resource
8
8
  @query = query
9
9
  end
10
10
 
11
- def self.hypermedia_properties
12
- properties = super
11
+ property :query
13
12
 
14
- properties[:simple] = properties[:simple].concat [:query]
15
-
16
- properties
17
- end
18
-
19
13
  def query_string
20
14
  result = self.class.hypermedia_query_parameters.inject([]) { |col, query_parameter| col << "query[#{query_parameter}]=#{@query[query_parameter]}" }
21
15
  "?" + result.join("&")
@@ -1,5 +1,5 @@
1
1
  module Api
2
2
  module Presenter
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
@@ -36,12 +36,11 @@ module HypertextPresenterMocks
36
36
  end
37
37
 
38
38
  class MockSingleResource < Api::Presenter::Resource
39
- def self.hypermedia_properties
40
- {
41
- simple: [:number, :string, :date],
42
- resource: [:sibling]
43
- }
44
- end
39
+
40
+ property :number
41
+ property :string
42
+ property :date
43
+ property :sibling
45
44
 
46
45
  def self_link
47
46
  "/path/to/single_resource/#{@resource.number}"
@@ -59,6 +59,7 @@ describe Api::Presenter::Hypermedia do
59
59
  ]
60
60
  }
61
61
  end
62
+
62
63
  let(:single_resource_standard) do
63
64
  {
64
65
  "links" =>
@@ -78,9 +79,10 @@ describe Api::Presenter::Hypermedia do
78
79
  },
79
80
  "number" => 1,
80
81
  "string" => 'This is a string',
81
- "date" => Date.today.to_s
82
+ "date" => Date.today
82
83
  }
83
84
  end
85
+
84
86
  let(:search_resource_standard)do
85
87
  {
86
88
  "links" =>
@@ -147,37 +149,39 @@ describe Api::Presenter::Hypermedia do
147
149
  describe "when presenting a single resource" do
148
150
 
149
151
  let(:mock_data) { MockData.new(number: 1, string: 'This is a string', date: Date.today) }
150
- let(:single_resource){ MockSingleResource.new mock_data }
151
- let(:presented_single_resource){ Api::Presenter::Hypermedia.present single_resource }
152
+ let(:single_resource) { MockSingleResource.new mock_data }
153
+ let(:presented_single_resource) { single_resource.present }
152
154
 
153
155
  it "must respect the standard" do
154
- MultiJson.load(presented_single_resource).must_equal single_resource_standard
156
+ presented_single_resource.must_equal single_resource_standard
155
157
  end
156
158
  end
157
159
 
158
- describe "presenting a collection resource" do
159
-
160
- let(:mock_data_collection) do
161
- collection = []
162
- 4.times { |number| collection << MockData.new(number: number + 1, string: 'This is a string', date: Date.today) }
163
- Collection.new(collection)
164
- end
165
-
166
- let(:collection_resource){ MockCollectionResource.new mock_data_collection }
167
-
168
- let(:presented_collection_resource){ Api::Presenter::Hypermedia.present collection_resource }
169
-
170
- it "must respect the standard" do
171
- MultiJson.load(presented_collection_resource).must_equal collection_resource_standard
172
- end
173
-
174
- describe "presenting a search resource" do
175
- let(:search_resource){ MockSearchResource.new mock_data_collection, "page" => 1, "param1" => 1, "param2" => "string" }
176
- let(:presented_search_resource){ Api::Presenter::Hypermedia.present search_resource }
177
-
178
- it "must respect the standard" do
179
- MultiJson.load(presented_search_resource).must_equal search_resource_standard
180
- end
160
+ describe "presenting a collection resource" do
161
+
162
+ let(:mock_data_collection) do
163
+ collection = []
164
+ 4.times { |number| collection << MockData.new(number: number + 1, string: 'This is a string', date: Date.today) }
165
+ Collection.new(collection)
166
+ end
167
+
168
+ let(:collection_resource) { MockCollectionResource.new mock_data_collection }
169
+
170
+ let(:presented_collection_resource) { collection_resource.present }
171
+
172
+ it "must respect the standard" do
173
+ presented_collection_resource.must_equal collection_resource_standard
174
+ end
175
+
176
+ describe "presenting a search resource" do
177
+
178
+ let(:search_resource) { MockSearchResource.new mock_data_collection, "page" => 1, "param1" => 1, "param2" => "string" }
179
+
180
+ let(:presented_search_resource) { search_resource.present }
181
+
182
+ it "must respect the standard" do
183
+ presented_search_resource.must_equal search_resource_standard
184
+ end
185
+ end
181
186
  end
182
- end
183
187
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'bundler'
2
2
  require 'minitest/autorun'
3
- require 'multi_json'
4
3
  require 'date'
5
4
  require 'time'
6
5
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-presenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-04 00:00:00.000000000 Z
12
+ date: 2013-06-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -43,38 +43,6 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
- - !ruby/object:Gem::Dependency
47
- name: multi_json
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ~>
52
- - !ruby/object:Gem::Version
53
- version: 1.7.4
54
- type: :runtime
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
- requirements:
59
- - - ~>
60
- - !ruby/object:Gem::Version
61
- version: 1.7.4
62
- - !ruby/object:Gem::Dependency
63
- name: json
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ~>
68
- - !ruby/object:Gem::Version
69
- version: 1.8.0
70
- type: :runtime
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- version: 1.8.0
78
46
  description: A JSON resource presenter for a specific api media type
79
47
  email:
80
48
  - alvarola@gmail.com