roar 0.11.4 → 0.11.5

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.
Files changed (39) hide show
  1. data/CHANGES.markdown +5 -1
  2. data/Gemfile +1 -1
  3. data/README.textile +22 -21
  4. data/lib/roar/representer.rb +1 -1
  5. data/lib/roar/representer/feature/client.rb +3 -3
  6. data/lib/roar/representer/feature/coercion.rb +1 -1
  7. data/lib/roar/representer/feature/http_verbs.rb +8 -9
  8. data/lib/roar/representer/feature/hypermedia.rb +83 -63
  9. data/lib/roar/representer/json.rb +12 -12
  10. data/lib/roar/representer/json/hal.rb +98 -25
  11. data/lib/roar/representer/transport/faraday.rb +5 -11
  12. data/lib/roar/representer/transport/net_http.rb +5 -5
  13. data/lib/roar/representer/xml.rb +11 -11
  14. data/lib/roar/version.rb +1 -1
  15. data/roar.gemspec +2 -2
  16. data/test/client_test.rb +2 -2
  17. data/test/coercion_feature_test.rb +2 -2
  18. data/test/dummy/app/controllers/albums_controller.rb +6 -6
  19. data/test/dummy/app/models/album.rb +1 -1
  20. data/test/dummy/app/representers/representer/xml/album.rb +3 -3
  21. data/test/dummy/app/representers/representer/xml/song.rb +1 -1
  22. data/test/dummy/config/boot.rb +1 -1
  23. data/test/fake_server.rb +14 -15
  24. data/test/faraday_http_transport_test.rb +25 -10
  25. data/test/hal_json_test.rb +73 -31
  26. data/test/http_verbs_feature_test.rb +12 -12
  27. data/test/hypermedia_feature_test.rb +38 -145
  28. data/test/hypermedia_test.rb +100 -0
  29. data/test/integration_test.rb +22 -22
  30. data/test/json_representer_test.rb +31 -31
  31. data/test/net_http_transport_test.rb +24 -10
  32. data/test/order_representers.rb +11 -11
  33. data/test/rails/controller_methods_test.rb +25 -25
  34. data/test/rails/rails_representer_methods_test.rb +3 -3
  35. data/test/representer_test.rb +6 -6
  36. data/test/test_helper.rb +33 -4
  37. data/test/xml_representer_test.rb +47 -47
  38. metadata +3 -3
  39. data/lib/roar/rails.rb +0 -21
@@ -9,48 +9,48 @@ module Roar
9
9
  base.class_eval do
10
10
  include Representer
11
11
  include Representable::JSON
12
-
12
+
13
13
  extend ClassMethods
14
14
  include InstanceMethods # otherwise Representable overrides our #to_json.
15
15
  end
16
16
  end
17
-
17
+
18
18
  module InstanceMethods
19
19
  def to_hash(*args)
20
20
  before_serialize(*args)
21
21
  super
22
22
  end
23
-
23
+
24
24
  def from_json(document, options={})
25
25
  document = '{}' if document.nil? or document.empty?
26
-
26
+
27
27
  super
28
28
  end
29
-
29
+
30
30
  # Generic entry-point for rendering.
31
31
  def serialize(*args)
32
32
  to_json(*args)
33
33
  end
34
-
34
+
35
35
  def deserialize(*args)
36
36
  from_json(*args)
37
37
  end
38
38
  end
39
-
40
-
39
+
40
+
41
41
  module ClassMethods
42
42
  def deserialize(*args)
43
43
  from_json(*args)
44
44
  end
45
-
45
+
46
46
  # TODO: move to instance method, or remove?
47
47
  def links_definition_options
48
- [:links, {:class => Feature::Hypermedia::Hyperlink, :extend => HyperlinkRepresenter, :collection => true}]
48
+ [:links_array, {:from => :links, :class => Feature::Hypermedia::Hyperlink, :extend => HyperlinkRepresenter, :collection => true}]
49
49
  end
50
50
  end
51
-
51
+
52
52
  require "representable/json/hash"
53
- # Represents a hyperlink in standard roar+json hash representation.
53
+ # Represents a hyperlink in standard roar+json hash representation.
54
54
  module HyperlinkRepresenter
55
55
  include Representable::JSON::Hash
56
56
  end
@@ -8,10 +8,10 @@ module Roar::Representer
8
8
  # :embedded => true option.
9
9
  #
10
10
  # Example:
11
- #
11
+ #
12
12
  # module OrderRepresenter
13
13
  # include Roar::Representer::JSON::HAL
14
- #
14
+ #
15
15
  # property :id
16
16
  # collection :items, :class => Item, :extend => ItemRepresenter, :embedded => true
17
17
  #
@@ -32,33 +32,48 @@ module Roar::Representer
32
32
  include Resources
33
33
  end
34
34
  end
35
-
35
+
36
36
  module Resources
37
37
  # Write the property to the +_embedded+ hash when it's a resource.
38
38
  def compile_fragment(bin, doc)
39
39
  return super unless bin.options[:embedded]
40
40
  super(bin, doc[:_embedded] ||= {})
41
41
  end
42
-
42
+
43
43
  def uncompile_fragment(bin, doc)
44
44
  return super unless bin.options[:embedded]
45
45
  super(bin, doc["_embedded"] || {})
46
46
  end
47
47
  end
48
-
48
+
49
49
  module ClassMethods
50
50
  def links_definition_options
51
- super.tap { |options| options[1].merge!({:from => :_links}) }
51
+ super.tap { |options| options[1].merge!(
52
+ :from => :_links,
53
+ :extend => HAL::Links::LinkCollectionRepresenter,
54
+ :instance => lambda { |hsh| LinkCollection.new(link_array_rels) }) # defined in InstanceMethods as this is executed in represented context.
55
+ }
56
+ end
57
+ end
58
+
59
+ class LinkCollection < Feature::Hypermedia::LinkCollection
60
+ def initialize(array_rels, *args)
61
+ super(*args)
62
+ @array_rels = array_rels.map(&:to_s)
63
+ end
64
+
65
+ def is_array?(rel)
66
+ @array_rels.include?(rel.to_s)
52
67
  end
53
68
  end
54
-
69
+
55
70
  # Including this module in your representer will render and parse your embedded hyperlinks
56
71
  # following the HAL specification: http://stateless.co/hal_specification.html
57
72
  #
58
73
  # module SongRepresenter
59
74
  # include Roar::Representer::JSON
60
75
  # include Roar::Representer::JSON::HAL::Links
61
- #
76
+ #
62
77
  # link :self { "http://self" }
63
78
  # end
64
79
  #
@@ -68,40 +83,98 @@ module Roar::Representer
68
83
  #
69
84
  # Note that the HAL::Links module alone doesn't prepend an underscore to +links+. Use the JSON::HAL module for that.
70
85
  module Links
71
- # TODO: allow more attributes besides :href in Hyperlink.
72
86
  def self.included(base)
73
87
  base.class_eval do
74
88
  include Roar::Representer::Feature::Hypermedia
89
+ include InstanceMethods
75
90
  extend Links::ClassMethods
76
91
  end
77
92
  end
78
93
 
94
+ module InstanceMethods
95
+ private
96
+ def prepare_link_for(href, options)
97
+ return super(href, options) unless options[:array] # TODO: remove :array and use special instan
98
+
99
+ list = href.collect { |opts| Feature::Hypermedia::Hyperlink.new(opts.merge!(:rel => options[:rel])) }
100
+ LinkArray.new(list)
101
+ end
102
+
103
+ # TODO: move to LinksDefinition.
104
+ def link_array_rels
105
+ links_definition.collect { |cfg| cfg.first[:array] ? cfg.first[:rel] : nil }.compact
106
+ end
107
+ end
108
+
79
109
 
110
+ require 'representable/json/hash'
80
111
  module LinkCollectionRepresenter
81
- include JSON
82
-
83
- def to_hash(*)
84
- {}.tap do |hash|
85
- each do |link|
86
- # TODO: we statically use JSON::HyperlinkRepresenter here.
87
- hash[link.rel] = link.extend(JSON::HyperlinkRepresenter).to_hash(:exclude => [:rel])
88
- end
112
+ include Representable::JSON::Hash
113
+
114
+ values :extend => lambda { |item| item.is_a?(Array) ? LinkArrayRepresenter : Roar::Representer::JSON::HyperlinkRepresenter },
115
+ :class => lambda { |hsh| hsh.is_a?(LinkArray) ? nil : Roar::Representer::Feature::Hypermedia::Hyperlink }
116
+
117
+ def to_hash(options)
118
+ super.tap do |hsh| # TODO: cool: super(:exclude => [:rel]).
119
+ hsh.each { |k,v| v.delete(:rel) }
89
120
  end
90
121
  end
91
122
 
92
- def from_hash(json, *)
93
- json ||= {} # since we override #from_hash we're responsible for this.
94
- json.each do |k, v|
95
- self << Feature::Hypermedia::Hyperlink.new(v.merge :rel => k)
123
+
124
+ def from_hash(hash, options={})
125
+ hash.each { |k,v| hash[k] = LinkArray.new(v) if is_array?(k) }
126
+
127
+ hsh = super(hash) # this is where :class and :extend do the work.
128
+
129
+ hsh.each { |k, v| v.rel = k }
130
+ end
131
+ end
132
+
133
+ class LinkArray < Array
134
+ def rel
135
+ first.rel
136
+ end
137
+
138
+ def rel=(rel)
139
+ each { |lnk| lnk.rel = rel }
140
+ end
141
+ end
142
+
143
+ require 'representable/json/collection'
144
+ module LinkArrayRepresenter
145
+ include Representable::JSON::Collection
146
+
147
+ items :extend => Roar::Representer::JSON::HyperlinkRepresenter,
148
+ :class => Roar::Representer::Feature::Hypermedia::Hyperlink
149
+
150
+ def to_hash(*)
151
+ super.tap do |ary|
152
+ ary.each { |lnk| rel = lnk.delete(:rel) }
96
153
  end
97
- self
98
154
  end
99
155
  end
100
-
101
-
156
+
157
+
102
158
  module ClassMethods
159
+ # TODO: remove Links support in non-HAL.
103
160
  def links_definition_options
104
- super.tap { |options| options[1] = {:class => Feature::Hypermedia::LinkCollection, :extend => LinkCollectionRepresenter} }
161
+ super.tap { |options|
162
+ options[0] = :links
163
+ options[1] = {:class => Feature::Hypermedia::LinkCollection, :extend => LinkCollectionRepresenter, :from => :_links}
164
+ }
165
+
166
+ end
167
+
168
+ # Use this to define link arrays. It accepts the shared rel attribute and an array of options per link object.
169
+ #
170
+ # links :self do
171
+ # [{:lang => "en", :href => "http://en.hit"},
172
+ # {:lang => "de", :href => "http://de.hit"}]
173
+ # end
174
+ def links(options, &block)
175
+ options = {:rel => options} if options.is_a?(Symbol)
176
+ options[:array] = true # FIXME: we need to save this information somewhere.
177
+ link(options, &block)
105
178
  end
106
179
  end
107
180
  end
@@ -1,9 +1,4 @@
1
- begin
2
- require 'faraday'
3
- rescue LoadError
4
- puts 'You must add faraday as a dependency to use the FaradayTransport'
5
- end
6
- require 'logger'
1
+ require 'faraday'
7
2
 
8
3
  module Roar
9
4
  module Representer
@@ -23,15 +18,15 @@ module Roar
23
18
  end
24
19
 
25
20
  def post_uri(uri, body, as)
26
- build_connection(uri, as).post
21
+ build_connection(uri, as).post(nil, body)
27
22
  end
28
23
 
29
24
  def put_uri(uri, body, as)
30
- build_connection(uri, as).put
25
+ build_connection(uri, as).put(nil, body)
31
26
  end
32
27
 
33
28
  def patch_uri(uri, body, as)
34
- build_connection(uri, as).patch
29
+ build_connection(uri, as).patch(nil, body)
35
30
  end
36
31
 
37
32
  def delete_uri(uri, as)
@@ -43,9 +38,8 @@ module Roar
43
38
  def build_connection(uri, as)
44
39
  ::Faraday::Connection.new(
45
40
  :url => uri,
46
- :headers => { :accept => as }
41
+ :headers => { :accept => as, :content_type => as }
47
42
  ) do |builder|
48
- # builder.use Faraday::Response::Logger, Logger.new('faraday.log')
49
43
  builder.use ::Faraday::Response::RaiseError
50
44
  builder.adapter ::Faraday.default_adapter
51
45
  end
@@ -11,23 +11,23 @@ module Roar
11
11
  def get_uri(uri, as)
12
12
  do_request(Net::HTTP::Get, uri, as)
13
13
  end
14
-
14
+
15
15
  def post_uri(uri, body, as)
16
16
  do_request(Net::HTTP::Post, uri, as, body)
17
17
  end
18
-
18
+
19
19
  def put_uri(uri, body, as)
20
20
  do_request(Net::HTTP::Put, uri, as, body)
21
21
  end
22
-
22
+
23
23
  def patch_uri(uri, body, as)
24
24
  do_request(Net::HTTP::Patch, uri, as, body)
25
25
  end
26
-
26
+
27
27
  def delete_uri(uri, as)
28
28
  do_request(Net::HTTP::Delete, uri, as)
29
29
  end
30
-
30
+
31
31
  private
32
32
  def do_request(what, uri, as, body="")
33
33
  # DISCUSS: can this be made easier?
@@ -16,42 +16,42 @@ module Roar
16
16
  include InstanceMethods # otherwise Representable overrides our #to_xml.
17
17
  end
18
18
  end
19
-
19
+
20
20
  module InstanceMethods
21
21
  def to_xml(*args)
22
22
  before_serialize(*args)
23
23
  super
24
24
  end
25
-
25
+
26
26
  # Generic entry-point for rendering.
27
27
  def serialize(*args)
28
28
  to_xml(*args)
29
29
  end
30
-
30
+
31
31
  def deserialize(*args)
32
32
  from_xml(*args)
33
33
  end
34
34
  end
35
-
36
-
35
+
36
+
37
37
  module ClassMethods
38
38
  include Representable::XML::ClassMethods
39
-
39
+
40
40
  def links_definition_options
41
- [:links, {:from => :link, :class => Feature::Hypermedia::Hyperlink, :collection => true, :extend => XML::HyperlinkRepresenter}]
41
+ [:links_array, {:from => :link, :class => Feature::Hypermedia::Hyperlink, :collection => true, :extend => XML::HyperlinkRepresenter}]
42
42
  end
43
-
43
+
44
44
  # Generic entry-point for parsing.
45
45
  def deserialize(*args)
46
46
  from_xml(*args)
47
47
  end
48
48
  end
49
-
50
-
49
+
50
+
51
51
  require 'representable/xml/hash'
52
52
  module HyperlinkRepresenter
53
53
  include Representable::XML::AttributeHash
54
-
54
+
55
55
  self.representation_wrap = :link
56
56
  end
57
57
  end
@@ -1,3 +1,3 @@
1
1
  module Roar
2
- VERSION = "0.11.4"
2
+ VERSION = "0.11.5"
3
3
  end
@@ -18,9 +18,9 @@ Gem::Specification.new do |s|
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
-
21
+
22
22
  s.add_runtime_dependency "representable", ">= 1.2.8"
23
-
23
+
24
24
  s.add_development_dependency "rake"
25
25
  s.add_development_dependency "test_xml"
26
26
  s.add_development_dependency "minitest", ">= 2.8.1"
@@ -9,10 +9,10 @@ class ClientTest < MiniTest::Spec
9
9
  property :name
10
10
  property :band
11
11
  end
12
-
12
+
13
13
  @song = Object.new.extend(@representer)
14
14
  end
15
-
15
+
16
16
  it "should add accessors" do
17
17
  @song.extend Roar::Representer::Feature::Client
18
18
  @song.name = "Social Suicide"
@@ -6,10 +6,10 @@ class CoercionFeatureTest < MiniTest::Spec
6
6
  class ImmigrantSong
7
7
  include Roar::Representer::JSON
8
8
  include Roar::Representer::Feature::Coercion
9
-
9
+
10
10
  property :composed_at, :type => DateTime, :default => "May 12th, 2012"
11
11
  end
12
-
12
+
13
13
  it "coerces into the provided type" do
14
14
  song = ImmigrantSong.new.from_json("{\"composed_at\":\"November 18th, 1983\"}")
15
15
  assert_equal DateTime.parse("Fri, 18 Nov 1983 00:00:00 +0000"), song.composed_at
@@ -2,26 +2,26 @@ require 'roar/rails'
2
2
 
3
3
  class AlbumsController < ActionController::Base
4
4
  include Roar::Rails::ControllerMethods
5
-
5
+
6
6
  respond_to :xml
7
7
  represents Album
8
-
8
+
9
9
  def show
10
10
  @album = Album.find(params[:id])
11
11
  respond_with @album
12
12
  end
13
-
13
+
14
14
  def create
15
15
  @album = Album.create(representation)
16
-
16
+
17
17
  respond_with @album
18
18
  end
19
-
19
+
20
20
  def update
21
21
  @album = Album.find(params[:id])
22
22
  @album.songs.delete_all # make PUT behave REST-compliant.
23
23
  @album.update_attributes(representation)
24
-
24
+
25
25
  respond_with @album
26
26
  end
27
27
  end
@@ -1,6 +1,6 @@
1
1
  class Album < ActiveRecord::Base
2
2
  has_many :songs
3
3
  validates_presence_of :year
4
-
4
+
5
5
  accepts_nested_attributes_for :songs, :allow_destroy => true
6
6
  end