wrest 0.0.5-java

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 (45) hide show
  1. data/README.rdoc +101 -0
  2. data/Rakefile +243 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/jwrest +3 -0
  5. data/bin/wrest +3 -0
  6. data/bin/wrest_shell.rb +21 -0
  7. data/lib/wrest.rb +41 -0
  8. data/lib/wrest/components.rb +19 -0
  9. data/lib/wrest/components/attributes_container.rb +123 -0
  10. data/lib/wrest/components/mutators.rb +21 -0
  11. data/lib/wrest/components/mutators/base.rb +27 -0
  12. data/lib/wrest/components/mutators/xml_simple_type_caster.rb +31 -0
  13. data/lib/wrest/core_ext/hash.rb +5 -0
  14. data/lib/wrest/core_ext/hash/conversions.rb +25 -0
  15. data/lib/wrest/core_ext/string.rb +5 -0
  16. data/lib/wrest/core_ext/string/conversions.rb +23 -0
  17. data/lib/wrest/exceptions.rb +1 -0
  18. data/lib/wrest/exceptions/unsupported_content_type_exception.rb +15 -0
  19. data/lib/wrest/resource.rb +18 -0
  20. data/lib/wrest/resource/base.rb +69 -0
  21. data/lib/wrest/resource/collection.rb +12 -0
  22. data/lib/wrest/response.rb +38 -0
  23. data/lib/wrest/translators.rb +25 -0
  24. data/lib/wrest/translators/content_types.rb +20 -0
  25. data/lib/wrest/translators/json.rb +21 -0
  26. data/lib/wrest/translators/xml.rb +24 -0
  27. data/lib/wrest/uri.rb +74 -0
  28. data/lib/wrest/uri_template.rb +32 -0
  29. data/lib/wrest/version.rb +22 -0
  30. data/spec/custom_matchers/custom_matchers.rb +2 -0
  31. data/spec/rcov.opts +4 -0
  32. data/spec/spec.opts +6 -0
  33. data/spec/spec_helper.rb +18 -0
  34. data/spec/wrest/components/attributes_container_spec.rb +184 -0
  35. data/spec/wrest/components/mutators/base_spec.rb +18 -0
  36. data/spec/wrest/components/mutators/xml_simple_type_caster_spec.rb +41 -0
  37. data/spec/wrest/core_ext/hash/conversions_spec.rb +22 -0
  38. data/spec/wrest/core_ext/string/conversions_spec.rb +16 -0
  39. data/spec/wrest/resource/base_spec.rb +158 -0
  40. data/spec/wrest/response_spec.rb +21 -0
  41. data/spec/wrest/translators/xml_spec.rb +12 -0
  42. data/spec/wrest/translators_spec.rb +9 -0
  43. data/spec/wrest/uri_spec.rb +131 -0
  44. data/spec/wrest/uri_template_spec.rb +28 -0
  45. metadata +139 -0
@@ -0,0 +1,123 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest::Components #:nodoc:
11
+
12
+ # Adds behaviour allowing a class to
13
+ # contain attributes and providing support
14
+ # for dynamic getters, setters and query methods.
15
+ # These methods are added at runtime, on the first
16
+ # invocation and on a per instance basis.
17
+ # <tt>respond_to?</tt> however will respond as though
18
+ # they are all already present.
19
+ # This means that two different instances of the same
20
+ # <tt>AttributesContainer</tt> could well have
21
+ # different attribute getters/setters/query methods.
22
+ #
23
+ # Note that this means the first call to a particular
24
+ # method will be slower because the method is defined
25
+ # at that point; subsequent calls will be much faster.
26
+ #
27
+ # If you're implementing your own initialize method
28
+ # remember to delegate to the default initialize
29
+ # of AttributesContainer by invoking <tt>super(attributes)</tt>
30
+ # Also keep in mind that attribute getter/setter/query methods
31
+ # will _not_ override any existing methods on the class.
32
+ #
33
+ # In situations where this is a problem, such as a client consuming Rails
34
+ # REST services where <tt>id</tt> is a common attribute and clashes with
35
+ # Object#id it is recommended to create getter/setter/query methods
36
+ # on the class (which affects all instances) using the <tt>has_attributes</tt> macro.
37
+ module AttributesContainer
38
+ def self.included(klass) #:nodoc:
39
+ klass.extend AttributesContainer::ClassMethods
40
+ klass.class_eval{ include AttributesContainer::InstanceMethods }
41
+ end
42
+
43
+ def self.build_attribute_getter(attribute_name) #:nodoc:
44
+ "def #{attribute_name};@attributes[:#{attribute_name}];end;"
45
+ end
46
+
47
+ def self.build_attribute_setter(attribute_name) #:nodoc:
48
+ "def #{attribute_name}=(value);@attributes[:#{attribute_name}] = value;end;"
49
+ end
50
+
51
+ def self.build_attribute_queryer(attribute_name) #:nodoc:
52
+ "def #{attribute_name}?;not @attributes[:#{attribute_name}].nil?;end;"
53
+ end
54
+
55
+ module ClassMethods
56
+ # This macro explicitly creates getter, setter and query methods on
57
+ # a class, overriding any exisiting methods with the same names.
58
+ # This can be used when attribute names clash with method names;
59
+ # an example would be Rails REST services which frequently make use
60
+ # an attribute named <tt>id</tt> which clashes with Object#id. Also,
61
+ # this can be used as a performance optimisation if the incoming
62
+ # attributes are known beforehand, as defining methods on the first
63
+ # invocation is no longer necessary.
64
+ def has_attributes(*attribute_names)
65
+ attribute_names.each do |attribute_name|
66
+ self.class_eval(
67
+ AttributesContainer.build_attribute_getter(attribute_name) +
68
+ AttributesContainer.build_attribute_setter(attribute_name) +
69
+ AttributesContainer.build_attribute_queryer(attribute_name)
70
+ )
71
+ end
72
+ end
73
+ end
74
+
75
+ module InstanceMethods
76
+ # Sets up any class to act like
77
+ # an attributes container by creating
78
+ # two variables, @attributes and @interface.
79
+ # Remember not to use these two variable names
80
+ # when using AttributesContainer in your
81
+ # own class.
82
+ def initialize(attributes = {})
83
+ @attributes = attributes.symbolize_keys
84
+ @interface = Module.new
85
+ self.extend @interface
86
+ end
87
+
88
+ def [](key)
89
+ @attributes[key.to_sym]
90
+ end
91
+
92
+ def []=(key, value)
93
+ @attributes[key.to_sym] = value
94
+ end
95
+
96
+ def respond_to?(method_name, include_private = false)
97
+ super(method_name, include_private) ? true : @attributes.include?(method_name.to_s.gsub(/(\?$)|(=$)/, '').to_sym)
98
+ end
99
+
100
+ # Creates getter, setter and query methods for
101
+ # attributes on the first call.
102
+ def method_missing(method_sym, *arguments)
103
+ method_name = method_sym.to_s
104
+ attribute_name = method_name.gsub(/(\?$)|(=$)/, '')
105
+
106
+ if @attributes.include?(attribute_name.to_sym) || method_name.last == '='
107
+ case method_name.last
108
+ when '='
109
+ @interface.module_eval AttributesContainer.build_attribute_setter(attribute_name)
110
+ when '?'
111
+ @interface.module_eval AttributesContainer.build_attribute_queryer(attribute_name)
112
+ else
113
+ @interface.module_eval AttributesContainer.build_attribute_getter(attribute_name)
114
+ end
115
+ send(method_sym, *arguments)
116
+ else
117
+ super(method_sym, *arguments)
118
+ end
119
+ end
120
+
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest #:nodoc:
11
+ module Components
12
+ # A mutator understands how to transform
13
+ # one tuple(key/value pair) from a hash
14
+ # into another
15
+ module Mutators
16
+ end
17
+ end
18
+ end
19
+
20
+ require "#{WREST_ROOT}/wrest/components/mutators/base"
21
+ require "#{WREST_ROOT}/wrest/components/mutators/xml_simple_type_caster"
@@ -0,0 +1,27 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ # This is a Null Object implementation of a
11
+ # hash mutator that will leave the contents
12
+ # of any hash it is applied to unchanged.
13
+ class Wrest::Components::Mutators::Base
14
+ # This method operates on a tuple (well, pair)
15
+ # from a hash map.
16
+ # Iterating over any hash using each injects
17
+ # each key/value pair from the hash in the
18
+ # form of an array.
19
+ # Thus the tuple this method expects
20
+ # is simply [:key, :value]
21
+ #
22
+ # Since this is a Null Object, this method
23
+ # simply returns the tuple unchanged
24
+ def mutate(tuple)
25
+ tuple
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ # This mutator undertands how do type casting
11
+ # using the type data embedded in a hash
12
+ # created by deserialising an xml using
13
+ # xml-simple
14
+ class Wrest::Components::Mutators::XmlSimpleTypeCaster
15
+ def mutate(tuple)
16
+ out_key = tuple.first.underscore
17
+ in_value = tuple.last[0]
18
+ out_value = in_value
19
+
20
+ case in_value
21
+ when Hash
22
+ if in_value["nil"] == "true"
23
+ out_value = nil
24
+ elsif in_value["type"] == "integer"
25
+ out_value = in_value["content"].to_i
26
+ end
27
+ end
28
+
29
+ [out_key, out_value]
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ require "#{WREST_ROOT}/wrest/core_ext/hash/conversions"
2
+
3
+ class Hash #:nodoc:
4
+ include Wrest::CoreExt::Hash::Conversions
5
+ end
@@ -0,0 +1,25 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest
11
+ module CoreExt #:nodoc:
12
+ module Hash #:nodoc:
13
+ # Makes it easier to build other objects from a String
14
+ module Conversions
15
+
16
+ # A convenience method equivalent to Wrest::Uri.new(string)
17
+ def to_mutated_hash(mutator)
18
+ returning({})do |mutated_hash|
19
+ self.each{|tuple| mutated_hash.store(*mutator.mutate(tuple))}
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ require "#{WREST_ROOT}/wrest/core_ext/string/conversions"
2
+
3
+ class String #:nodoc:
4
+ include Wrest::CoreExt::String::Conversions
5
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest
11
+ module CoreExt #:nodoc:
12
+ module String #:nodoc:
13
+ # Makes it easier to build other objects from a String
14
+ module Conversions
15
+
16
+ # A convenience method equivalent to Wrest::Uri.new(string)
17
+ def to_uri
18
+ Wrest::Uri.new(self)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1 @@
1
+ require "#{WREST_ROOT}/wrest/exceptions/unsupported_content_type_exception"
@@ -0,0 +1,15 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest
11
+ # Raised when a translator for an unregisterd response content type
12
+ # is requested. See Translators.
13
+ class UnsupportedContentTypeException < StandardError
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest
11
+ # Wrest::Resource is a re-implementation
12
+ # of ActiveResource using Wrest::Components
13
+ # and with some of the rough edges removed
14
+ module Resource
15
+ end
16
+ end
17
+
18
+ require "#{WREST_ROOT}/wrest/resource/base"
@@ -0,0 +1,69 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest::Resource #:nodoc:
11
+ # Resource::Base is the equivalent of ActiveResource::Base.
12
+ # It is a REST client targetted at Rails REST apps.
13
+ class Base
14
+ include Wrest::Components::AttributesContainer
15
+
16
+ has_attributes :id
17
+ attr_reader :attributes
18
+
19
+ class << self
20
+ def inherited(klass)
21
+ klass.set_resource_name klass.name
22
+ end
23
+
24
+ # Allows the resource name to be configured and creates
25
+ # a getter method for it.
26
+ # This is a useful feature when using anonymous classes like
27
+ # we often do while writing tests.
28
+ # By default, the resource name is set to the name of the class.
29
+ def set_resource_name(resource_name)
30
+ self.class_eval "def self.resource_name; '#{resource_name}';end"
31
+ end
32
+
33
+ # Allows the host url at which the resource is found to be configured
34
+ # and creates a getter method for it.
35
+ # For example in the url
36
+ # http://localhost:3000/users/1/settings
37
+ # you would set
38
+ # http://localhost:3000
39
+ # as the host url.
40
+ def set_host(host)
41
+ self.class_eval "def self.host; '#{host}';end"
42
+ end
43
+
44
+ def resource_path
45
+ @resource_path ||= "/#{resource_name.underscore.pluralize}"
46
+ end
47
+
48
+ def resource_url
49
+ "#{host}#{resource_path}"
50
+ end
51
+
52
+ def find_all
53
+ end
54
+
55
+ def find(id)
56
+ response_hash = "#{resource_url}/#{id}".to_uri.get.deserialise
57
+ resource_type = response_hash.keys.first
58
+ if(resource_type.underscore.camelize == self.name)
59
+ self.new(response_hash[resource_type].first)
60
+ else
61
+ response_hash
62
+ end
63
+ end
64
+
65
+ def objectify(hash)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,12 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ # Understands how to contain a collection of Wrest::Resources
11
+ class Wrest::Resource::Collection
12
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest #:nodoc:
11
+ # Decorates a response providing support for deserialisation.
12
+ #
13
+ # The following methods are also available (unlisted by rdoc because they're forwarded):
14
+ #
15
+ # <tt>:@http_response, :code, :message, :body, :http_version,
16
+ # :[], :content_length, :content_type, :each_header, :each_name, :each_value, :fetch,
17
+ # :get_fields, :key?, :type_params</tt>
18
+ #
19
+ # They behave exactly like their Net::HTTPResponse equivalents.
20
+ class Response
21
+ extend Forwardable
22
+ def_delegators :@http_response, :code, :message, :body, :http_version,
23
+ :[], :content_length, :content_type, :each_header, :each_name, :each_value, :fetch,
24
+ :get_fields, :key?, :type_params
25
+
26
+ def initialize(http_response)
27
+ @http_response = http_response
28
+ end
29
+
30
+ def deserialise
31
+ deserialise_using(Wrest::Translators.load(@http_response.content_type))
32
+ end
33
+
34
+ def deserialise_using(translator)
35
+ translator.call(@http_response)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ # Copyright 2009 Sidu Ponnappa
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
7
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+
10
+ module Wrest
11
+ # Contains strategies/lambdas which know how to deserialise
12
+ # different content types.
13
+ module Translators
14
+ # Loads the appropriate desirialisation strategy based on
15
+ # the content type
16
+ def self.load(content_type)
17
+ translator = CONTENT_TYPES[content_type]
18
+ translator || (raise UnsupportedContentTypeException.new("Unsupported content type #{content_type}"))
19
+ end
20
+ end
21
+ end
22
+
23
+ require "#{WREST_ROOT}/wrest/translators/xml"
24
+ require "#{WREST_ROOT}/wrest/translators/json"
25
+ require "#{WREST_ROOT}/wrest/translators/content_types"