roar 0.12.9 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +8 -10
  4. data/CHANGES.markdown +24 -2
  5. data/Gemfile +5 -2
  6. data/README.markdown +132 -28
  7. data/TODO.markdown +9 -7
  8. data/examples/example.rb +2 -0
  9. data/examples/example_server.rb +19 -3
  10. data/gemfiles/Gemfile.representable-2.0 +5 -0
  11. data/gemfiles/Gemfile.representable-2.1 +5 -0
  12. data/lib/roar.rb +1 -1
  13. data/lib/roar/client.rb +38 -0
  14. data/lib/roar/{representer/feature/coercion.rb → coercion.rb} +2 -1
  15. data/lib/roar/decorator.rb +3 -11
  16. data/lib/roar/http_verbs.rb +88 -0
  17. data/lib/roar/hypermedia.rb +174 -0
  18. data/lib/roar/json.rb +55 -0
  19. data/lib/roar/json/collection.rb +3 -0
  20. data/lib/roar/{representer/json → json}/collection_json.rb +20 -20
  21. data/lib/roar/{representer/json → json}/hal.rb +33 -31
  22. data/lib/roar/json/hash.rb +3 -0
  23. data/lib/roar/json/json_api.rb +208 -0
  24. data/lib/roar/representer.rb +3 -36
  25. data/lib/roar/transport/faraday.rb +49 -0
  26. data/lib/roar/transport/net_http.rb +57 -0
  27. data/lib/roar/transport/net_http/request.rb +72 -0
  28. data/lib/roar/version.rb +1 -1
  29. data/lib/roar/xml.rb +54 -0
  30. data/roar.gemspec +5 -4
  31. data/test/client_test.rb +3 -3
  32. data/test/coercion_feature_test.rb +6 -3
  33. data/test/collection_json_test.rb +8 -10
  34. data/test/decorator_test.rb +27 -15
  35. data/test/faraday_http_transport_test.rb +13 -15
  36. data/test/hal_json_test.rb +16 -16
  37. data/test/hal_links_test.rb +3 -3
  38. data/test/http_verbs_test.rb +17 -22
  39. data/test/hypermedia_feature_test.rb +23 -45
  40. data/test/hypermedia_test.rb +11 -23
  41. data/test/integration/band_representer.rb +2 -2
  42. data/test/integration/runner.rb +4 -3
  43. data/test/integration/server.rb +13 -2
  44. data/test/integration/ssl_server.rb +1 -1
  45. data/test/json_api_test.rb +336 -0
  46. data/test/json_representer_test.rb +16 -12
  47. data/test/lib/runner.rb +134 -0
  48. data/test/lonely_test.rb +9 -0
  49. data/test/net_http_transport_test.rb +4 -4
  50. data/test/representer_test.rb +2 -2
  51. data/test/{lib/roar/representer/transport/net_http/request_test.rb → ssl_client_certs_test.rb} +43 -5
  52. data/test/test_helper.rb +12 -5
  53. data/test/xml_representer_test.rb +26 -166
  54. metadata +49 -29
  55. data/gemfiles/Gemfile.representable-1.7 +0 -6
  56. data/gemfiles/Gemfile.representable-1.8 +0 -6
  57. data/lib/roar/representer/feature/client.rb +0 -39
  58. data/lib/roar/representer/feature/http_verbs.rb +0 -95
  59. data/lib/roar/representer/feature/hypermedia.rb +0 -175
  60. data/lib/roar/representer/json.rb +0 -67
  61. data/lib/roar/representer/transport/faraday.rb +0 -50
  62. data/lib/roar/representer/transport/net_http.rb +0 -59
  63. data/lib/roar/representer/transport/net_http/request.rb +0 -75
  64. data/lib/roar/representer/xml.rb +0 -61
@@ -1,6 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- gemspec path: '../'
4
-
5
- gem 'representable', '~> 1.7.0'
6
- gem "sinatra-contrib", :git => "git@github.com:apotonick/sinatra-contrib.git", :branch => "runner"
@@ -1,6 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- gemspec path: '../'
4
-
5
- gem 'representable', '~> 1.8.0'
6
- gem "sinatra-contrib", :git => "git@github.com:apotonick/sinatra-contrib.git", :branch => "runner"
@@ -1,39 +0,0 @@
1
- require "roar/representer/feature/http_verbs"
2
-
3
- module Roar
4
- # Automatically add accessors for properties and collections. Also mixes in HttpVerbs.
5
- module Representer
6
- module Feature
7
- module Client
8
- include HttpVerbs
9
-
10
- def self.extended(base)
11
- base.instance_eval do
12
- representable_attrs.each do |attr|
13
- next unless attr.instance_of? Representable::Definition # ignore hyperlinks etc for now.
14
- name = attr.name
15
-
16
- # TODO: could anyone please make this better?
17
- instance_eval %Q{
18
- def #{name}=(v)
19
- @#{name} = v
20
- end
21
-
22
- def #{name}
23
- @#{name}
24
- end
25
- }
26
- end
27
- end
28
- end
29
-
30
- # DISCUSS: should we just override #serialize here? otherwise if you later include Hypermedia, it'll run before that method.
31
- def before_serialize(options={})
32
- options[:links] ||= false
33
-
34
- super(options)
35
- end
36
- end
37
- end
38
- end
39
- end
@@ -1,95 +0,0 @@
1
- require 'roar/representer/transport/net_http'
2
-
3
- module Roar
4
- # Gives HTTP-power to representers. They can serialize, send, process and deserialize HTTP-requests.
5
- module Representer
6
- module Feature
7
- module HttpVerbs
8
-
9
- class << self
10
- attr_accessor :transport_engine
11
-
12
- def included(base)
13
- base.extend ClassMethods
14
- end
15
- end
16
- self.transport_engine = ::Roar::Representer::Transport::NetHTTP
17
-
18
-
19
- module ClassMethods
20
- # GETs +url+ with +format+ and returns deserialized represented object.
21
- def get(*args)
22
- new.get(*args)
23
- end
24
- end
25
-
26
-
27
- attr_writer :transport_engine
28
- def transport_engine
29
- @transport_engine || HttpVerbs.transport_engine
30
- end
31
-
32
- # Serializes the object, POSTs it to +url+ with +format+, deserializes the returned document
33
- # and updates properties accordingly.
34
- def post(*args, &block)
35
- options = handle_deprecated_args(serialize, *args)
36
- response = http.post_uri(options, &block)
37
- handle_response(response)
38
- end
39
-
40
- # GETs +url+ with +format+, deserializes the returned document and updates properties accordingly.
41
- def get(*args, &block)
42
- response = http.get_uri(*args, &block)
43
- handle_response(response)
44
- end
45
-
46
- # Serializes the object, PUTs it to +url+ with +format+, deserializes the returned document
47
- # and updates properties accordingly.
48
- def put(*args, &block)
49
- options = handle_deprecated_args(serialize, *args)
50
- response = http.put_uri(options, &block)
51
- handle_response(response)
52
- self
53
- end
54
-
55
- def patch(*args, &block)
56
- options = handle_deprecated_args(serialize, *args)
57
- response = http.patch_uri(options, &block)
58
- handle_response(response)
59
- self
60
- end
61
-
62
- def delete(*args, &block)
63
- http.delete_uri(*args, &block)
64
- self
65
- end
66
-
67
- private
68
- def handle_response(response)
69
- document = response.body
70
- deserialize(document)
71
- end
72
-
73
- def http
74
- transport_engine.new
75
- end
76
-
77
- def handle_deprecated_args(body, *args) # TODO: remove in 1.0.
78
- options = args.first
79
-
80
- if args.size > 1
81
- warn %{DEPRECATION WARNING: #get, #post, #put, #delete and #patch no longer accept positional arguments. Please call them as follows:
82
- get(uri: "http://localhost/songs", as: "application/json")
83
- post(uri: "http://localhost/songs", as: "application/json")
84
- Thank you and have a beautiful day.}
85
- options = {:uri => args[0], :as => args[1]} if args.size == 2
86
- options = {:uri => args[0], :as => args[2]}
87
- end
88
-
89
- options[:body] = body
90
- options
91
- end
92
- end
93
- end
94
- end
95
- end
@@ -1,175 +0,0 @@
1
- module Roar
2
- module Representer
3
- module Feature
4
- # Define hypermedia links in your representations.
5
- #
6
- # Example:
7
- #
8
- # class Order
9
- # include Roar::Representer::JSON
10
- #
11
- # property :id
12
- #
13
- # link :self do
14
- # "http://orders/#{id}"
15
- # end
16
- #
17
- # If you want more attributes, just pass a hash to #link.
18
- #
19
- # link :rel => :next, :title => "Next, please!" do
20
- # "http://orders/#{id}"
21
- # end
22
- #
23
- # If you need dynamic attributes, the block can return a hash.
24
- #
25
- # link :preview do
26
- # {:href => image.url, :title => image.name}
27
- # end
28
- #
29
- # Sometimes you need values from outside when the representation links are rendered. Just pass them
30
- # to the render method, they will be available as block parameters.
31
- #
32
- # link :self do |opts|
33
- # "http://orders/#{opts[:id]}"
34
- # end
35
- #
36
- # model.to_json(:id => 1)
37
- module Hypermedia
38
- def self.included(base)
39
- base.extend ClassMethods
40
- base.extend InheritableArray
41
- end
42
-
43
- def before_serialize(options={})
44
- super(options) # Representer::Base
45
- prepare_links!(options) unless options[:links] == false # DISCUSS: doesn't work when links are already setup (e.g. from #deserialize).
46
- end
47
-
48
- attr_writer :links
49
-
50
- def links
51
- @links ||= LinkCollection.new
52
- end
53
-
54
- def links_array
55
- links.values # FIXME: move to LinkCollection#to_a.
56
- end
57
-
58
- def links_array=(ary)
59
- # FIXME: move to LinkCollection
60
- self.links= LinkCollection.new
61
- ary.each { |lnk| links.add(lnk) }
62
- end
63
-
64
- module LinkConfigsMethod
65
- def link_configs
66
- representable_attrs.inheritable_array(:links)
67
- end
68
- end
69
-
70
- include LinkConfigsMethod
71
-
72
- private
73
- def links_definition_options
74
- # TODO: this method is never called.
75
- [:links_array, {:as => :link, :class => Feature::Hypermedia::Hyperlink, :collection => true,
76
- :decorator_scope => true}] # TODO: merge with JSON.
77
- end
78
-
79
- # Setup hypermedia links by invoking their blocks. Usually called by #serialize.
80
- def prepare_links!(*args)
81
- # TODO: move this method to _links or something so it doesn't need to be called in #serialize.
82
- compile_links_for(link_configs, *args).each do |lnk|
83
- links.add(lnk) # TODO: move to LinkCollection.new.
84
- end
85
- end
86
-
87
- def compile_links_for(configs, *args)
88
- configs.collect do |config|
89
- options, block = config.first, config.last
90
- href = run_link_block(block, *args) or next
91
-
92
- prepare_link_for(href, options)
93
- end.compact # FIXME: make this less ugly.
94
- end
95
-
96
- def prepare_link_for(href, options)
97
- options = options.merge(href.is_a?(Hash) ? href : {:href => href})
98
- Hyperlink.new(options)
99
- end
100
-
101
- def run_link_block(block, *args)
102
- instance_exec(*args, &block)
103
- end
104
-
105
-
106
- class LinkCollection < Hash
107
- # DISCUSS: make Link#rel return string always.
108
- def [](rel)
109
- self.fetch(rel.to_s, nil)
110
- end
111
-
112
- def add(link)
113
- self[link.rel.to_s] = link
114
- end
115
- end
116
-
117
-
118
- module ClassMethods
119
- # Declares a hypermedia link in the document.
120
- #
121
- # Example:
122
- #
123
- # link :self do
124
- # "http://orders/#{id}"
125
- # end
126
- #
127
- # The block is executed in instance context, so you may call properties or other accessors.
128
- # Note that you're free to put decider logic into #link blocks, too.
129
- def link(options, &block)
130
- options = {:rel => options} unless options.is_a?(Hash)
131
- create_links_definition # this assures the links are rendered at the right position.
132
- link_configs << [options, block]
133
- end
134
-
135
- include LinkConfigsMethod
136
-
137
- private
138
- def create_links_definition
139
- return if representable_attrs.find { |d| d.is_a?(LinksDefinition) }
140
-
141
- options = links_definition_options # TODO: remove in 1.0.
142
- if Roar.representable_1_8?
143
- opt = options.last
144
- opt[:exec_context] = :decorator
145
- opt.delete(:decorator_scope)
146
- end
147
-
148
- representable_attrs << LinksDefinition.new(*options)
149
- end
150
- end
151
-
152
- class LinksDefinition < Representable::Definition
153
- end
154
-
155
-
156
- require "ostruct"
157
- # An abstract hypermedia link with arbitrary attributes.
158
- class Hyperlink < OpenStruct
159
- include Enumerable
160
-
161
- def each(*args, &block)
162
- marshal_dump.each(*args, &block)
163
- end
164
-
165
- # FIXME: do we need this method any longer?
166
- def replace(hash)
167
- # #marshal_load requires symbol keys: http://apidock.com/ruby/v1_9_3_125/OpenStruct/marshal_load
168
- marshal_load(hash.inject({}) { |h, (k,v)| h[k.to_sym] = v; h })
169
- self
170
- end
171
- end
172
- end
173
- end
174
- end
175
- end
@@ -1,67 +0,0 @@
1
- require 'roar/representer'
2
- require 'roar/representer/feature/hypermedia'
3
- require 'representable/json'
4
-
5
- module Roar
6
- require 'representable/version'
7
- def self.representable_1_8? # TODO: remove me in 1.0.
8
- Representable::VERSION =~ /^1.8/
9
- end
10
-
11
- module Representer
12
- module JSON
13
- def self.included(base)
14
- base.class_eval do
15
- include Representer
16
- include Representable::JSON
17
-
18
- extend ClassMethods
19
- include InstanceMethods # otherwise Representable overrides our #to_json.
20
- end
21
- end
22
-
23
- module InstanceMethods
24
- def to_hash(*args)
25
- before_serialize(*args)
26
- super
27
- end
28
-
29
- def from_json(document, options={})
30
- document = '{}' if document.nil? or document.empty?
31
-
32
- super
33
- end
34
-
35
- # Generic entry-point for rendering.
36
- def serialize(*args)
37
- to_json(*args)
38
- end
39
-
40
- def deserialize(*args)
41
- from_json(*args)
42
- end
43
- end
44
-
45
-
46
- module ClassMethods
47
- def deserialize(*args)
48
- from_json(*args)
49
- end
50
-
51
- # TODO: move to instance method, or remove?
52
- def links_definition_options
53
- # FIXME: this doesn't belong into the generic JSON representer.
54
- [:links_array, {:as => :links, :class => Feature::Hypermedia::Hyperlink, :extend => HyperlinkRepresenter, :collection => true,
55
- :decorator_scope => true}]
56
- end
57
- end
58
-
59
-
60
- require "representable/json/hash"
61
- # Represents a hyperlink in standard roar+json hash representation.
62
- module HyperlinkRepresenter
63
- include Representable::JSON::Hash
64
- end
65
- end
66
- end
67
- end
@@ -1,50 +0,0 @@
1
- require 'faraday'
2
-
3
- module Roar
4
- module Representer
5
- module Transport
6
- # Advanced implementation of the HTTP verbs with the Faraday HTTP library
7
- # (which can, in turn, use adapters based on Net::HTTP or libcurl)
8
- #
9
- # Depending on how the Faraday middleware stack is configured, this
10
- # Transport can support features such as HTTP error code handling,
11
- # redirects, etc.
12
- #
13
- # @see http://rubydoc.info/gems/faraday/file/README.md Faraday README
14
- class Faraday
15
-
16
- def get_uri(uri, as, *args)
17
- build_connection(uri, as).get
18
- end
19
-
20
- def post_uri(uri, body, as, *args)
21
- build_connection(uri, as).post(nil, body)
22
- end
23
-
24
- def put_uri(uri, body, as, *args)
25
- build_connection(uri, as).put(nil, body)
26
- end
27
-
28
- def patch_uri(uri, body, as, *args)
29
- build_connection(uri, as).patch(nil, body)
30
- end
31
-
32
- def delete_uri(uri, as, *args)
33
- build_connection(uri, as).delete
34
- end
35
-
36
- private
37
-
38
- def build_connection(uri, as)
39
- ::Faraday::Connection.new(
40
- :url => uri,
41
- :headers => { :accept => as, :content_type => as }
42
- ) do |builder|
43
- builder.use ::Faraday::Response::RaiseError
44
- builder.adapter ::Faraday.default_adapter
45
- end
46
- end
47
- end
48
- end
49
- end
50
- end