api-presenter 0.0.2 → 0.0.3

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/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