roar 0.11.4 → 0.11.5

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