wrest 2.1.9 → 4.0.0

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 (76) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG +6 -0
  3. data/LICENCE +1 -1
  4. data/README.md +47 -40
  5. data/bin/wrest +2 -1
  6. data/bin/wrest_shell.rb +10 -8
  7. data/lib/wrest/async_request/event_machine_backend.rb +3 -1
  8. data/lib/wrest/async_request/thread_backend.rb +5 -2
  9. data/lib/wrest/async_request/thread_pool.rb +4 -2
  10. data/lib/wrest/async_request.rb +7 -6
  11. data/lib/wrest/cache_proxy.rb +39 -28
  12. data/lib/wrest/caching/memcached.rb +21 -18
  13. data/lib/wrest/caching/redis.rb +22 -22
  14. data/lib/wrest/caching.rb +16 -14
  15. data/lib/wrest/callback.rb +19 -16
  16. data/lib/wrest/components/container/alias_accessors.rb +51 -47
  17. data/lib/wrest/components/container/typecaster.rb +146 -95
  18. data/lib/wrest/components/container.rb +171 -152
  19. data/lib/wrest/components/mutators/base.rb +43 -34
  20. data/lib/wrest/components/mutators/camel_to_snake_case.rb +7 -3
  21. data/lib/wrest/components/mutators/{xml_mini_type_caster.rb → xml_type_caster.rb} +29 -16
  22. data/lib/wrest/components/mutators.rb +21 -19
  23. data/lib/wrest/components/translators/content_types.rb +20 -16
  24. data/lib/wrest/components/translators/json.rb +19 -16
  25. data/lib/wrest/components/translators/txt.rb +19 -15
  26. data/lib/wrest/components/translators/xml/conversions.rb +56 -0
  27. data/lib/wrest/components/translators/xml.rb +60 -18
  28. data/lib/wrest/components/translators.rb +7 -6
  29. data/lib/wrest/components.rb +11 -8
  30. data/lib/wrest/core_ext/hash/conversions.rb +10 -10
  31. data/lib/wrest/core_ext/hash.rb +4 -2
  32. data/lib/wrest/core_ext/string/conversions.rb +14 -13
  33. data/lib/wrest/core_ext/string.rb +5 -3
  34. data/lib/wrest/exceptions.rb +4 -2
  35. data/lib/wrest/hash_with_case_insensitive_access.rb +8 -8
  36. data/lib/wrest/hash_with_indifferent_access.rb +442 -0
  37. data/lib/wrest/http_codes.rb +20 -19
  38. data/lib/wrest/http_shared/headers.rb +2 -0
  39. data/lib/wrest/http_shared/standard_headers.rb +2 -2
  40. data/lib/wrest/http_shared/standard_tokens.rb +8 -6
  41. data/lib/wrest/http_shared.rb +5 -3
  42. data/lib/wrest/multipart.rb +20 -11
  43. data/lib/wrest/native/connection_factory.rb +15 -11
  44. data/lib/wrest/native/delete.rb +15 -11
  45. data/lib/wrest/native/get.rb +60 -55
  46. data/lib/wrest/native/options.rb +15 -11
  47. data/lib/wrest/native/patch.rb +27 -0
  48. data/lib/wrest/native/post.rb +15 -11
  49. data/lib/wrest/native/post_multipart.rb +22 -18
  50. data/lib/wrest/native/put.rb +16 -12
  51. data/lib/wrest/native/put_multipart.rb +22 -18
  52. data/lib/wrest/native/redirection.rb +13 -12
  53. data/lib/wrest/native/request.rb +144 -106
  54. data/lib/wrest/native/response.rb +87 -78
  55. data/lib/wrest/native/session.rb +49 -40
  56. data/lib/wrest/native.rb +14 -11
  57. data/lib/wrest/test/request_patches.rb +10 -3
  58. data/lib/wrest/test.rb +3 -1
  59. data/lib/wrest/uri/builders.rb +14 -12
  60. data/lib/wrest/uri.rb +92 -50
  61. data/lib/wrest/uri_template.rb +11 -7
  62. data/lib/wrest/utils.rb +129 -0
  63. data/lib/wrest/version.rb +3 -1
  64. data/lib/wrest.rb +31 -33
  65. data/lib/wrest_no_ext.rb +2 -0
  66. metadata +91 -56
  67. data/lib/wrest/components/mutators/xml_simple_type_caster.rb +0 -37
  68. data/lib/wrest/xml_mini/jdom/xpath_filter.rb +0 -17
  69. data/lib/wrest/xml_mini/jdom.rb +0 -6
  70. data/lib/wrest/xml_mini/libxml/xpath_filter.rb +0 -12
  71. data/lib/wrest/xml_mini/libxml.rb +0 -8
  72. data/lib/wrest/xml_mini/nokogiri/xpath_filter.rb +0 -15
  73. data/lib/wrest/xml_mini/nokogiri.rb +0 -7
  74. data/lib/wrest/xml_mini/rexml/xpath_filter.rb +0 -15
  75. data/lib/wrest/xml_mini/rexml.rb +0 -8
  76. data/lib/wrest/xml_mini.rb +0 -8
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2009 Sidu Ponnappa
2
4
 
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,12 +14,13 @@ module Wrest
12
14
  attr_reader :callback_hash
13
15
 
14
16
  def initialize(callable)
15
- if callable.is_a?(Hash)
17
+ case callable
18
+ when Hash
16
19
  @callback_hash = Callback.ensure_values_are_collections(callable)
17
- elsif callable.is_a?(Proc)
20
+ when Proc
18
21
  @callback_hash = {}
19
22
  callable.call(self)
20
- elsif callable.is_a?(Callback)
23
+ when Callback
21
24
  @callback_hash = callable.callback_hash.dup
22
25
  end
23
26
  end
@@ -34,12 +37,12 @@ module Wrest
34
37
 
35
38
  def execute(response)
36
39
  callback_hash.each do |code, callback_list|
37
- callback_list.each {|callback| callback.call(response)} if case code
38
- when Range
39
- code.include?(response.code.to_i)
40
- when Fixnum
41
- code == response.code.to_i
42
- end
40
+ callback_list.each { |callback| callback.call(response) } if case code
41
+ when Range
42
+ code.include?(response.code.to_i)
43
+ when Integer
44
+ code == response.code.to_i
45
+ end
43
46
  end
44
47
  end
45
48
 
@@ -47,14 +50,14 @@ module Wrest
47
50
  @callback_hash[code] ? @callback_hash[code] << block : @callback_hash[code] = [block]
48
51
  end
49
52
 
50
- {200 => "ok", 201 => "created", 202 => "accepted", 204 => "no_content", 301 => "moved_permanently", 302 => "found", 303 => "see_other", 304 => "not_modified",
51
- 307 => "temporary_redirect", 400 => "bad_request", 401 => "unauthorized", 403 => "forbidden", 404 => "not_found", 405 => "method_not_allowed",
52
- 406 => "not_acceptable", 422 => "unprocessable_entity", 500 => "internal_server_error"}.each do |code, method|
53
- method_name = "on_#{method}".to_sym
54
- define_method method_name do |&block|
55
- (@callback_hash[code] ? @callback_hash[code] << block : @callback_hash[code] = [block]) if block
56
- end
53
+ { 200 => 'ok', 201 => 'created', 202 => 'accepted', 204 => 'no_content', 301 => 'moved_permanently', 302 => 'found', 303 => 'see_other', 304 => 'not_modified',
54
+ 307 => 'temporary_redirect', 400 => 'bad_request', 401 => 'unauthorized', 403 => 'forbidden', 404 => 'not_found', 405 => 'method_not_allowed',
55
+ 406 => 'not_acceptable', 422 => 'unprocessable_entity', 500 => 'internal_server_error' }.each do |code, method|
56
+ method_name = "on_#{method}".to_sym
57
+ define_method method_name do |&block|
58
+ (@callback_hash[code] ? @callback_hash[code] << block : @callback_hash[code] = [block]) if block
57
59
  end
60
+ end
58
61
 
59
62
  def self.ensure_values_are_collections(hash)
60
63
  result = {}
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2009 Sidu Ponnappa
2
4
 
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -8,56 +10,58 @@
8
10
  # See the License for the specific language governing permissions and limitations under the License.
9
11
 
10
12
  module Wrest
11
- module Components::Container
12
- module AliasAccessors
13
- def self.included(klass) #:nodoc:
14
- klass.extend AliasAccessors::ClassMethods
15
- end
13
+ module Components
14
+ module Container
15
+ module AliasAccessors
16
+ def self.included(klass) # :nodoc:
17
+ klass.extend AliasAccessors::ClassMethods
18
+ end
16
19
 
17
- def self.build_aliased_attribute_getter(attribute_name, alias_name) #:nodoc:
18
- "def #{alias_name};#{attribute_name};end;"
19
- end
20
+ def self.build_aliased_attribute_getter(attribute_name, alias_name) # :nodoc:
21
+ "def #{alias_name};#{attribute_name};end;"
22
+ end
20
23
 
21
- def self.build_aliased_attribute_setter(attribute_name, alias_name) #:nodoc:
22
- "def #{alias_name}=(value);self.#{attribute_name}=value;end;"
23
- end
24
+ def self.build_aliased_attribute_setter(attribute_name, alias_name) # :nodoc:
25
+ "def #{alias_name}=(value);self.#{attribute_name}=value;end;"
26
+ end
24
27
 
25
- def self.build_aliased_attribute_queryer(attribute_name, alias_name) #:nodoc:
26
- "def #{alias_name}?;self.#{attribute_name}?;end;"
27
- end
28
-
29
- module ClassMethods
30
- # Creates an alias set of getter, setter and query methods for
31
- # attributes that aren't quite the way you'd like them to be; this
32
- # is especially useful when you have no control over the source web
33
- # sevice/resource.
34
- #
35
- # For example, lets say that a particular resource exposes a
36
- # User's age as 'a' and sex as 's'. Typically, you'd have to access it as
37
- # user.a and user.s whereas you's like to access it as user.age and user.sex.
38
- # This is where alias_accessors comes into the picture. Your User class would
39
- # look somethig like this:
40
- #
41
- # class User
42
- # include Wrest::Components::Container
43
- #
44
- # alias_accessors :a => :age,
45
- # :s => :sex
46
- # end
47
- # This would create the methods user.age, user.age= and user.age? which delegates
48
- # to user.a, user.a= and user.a? respectively.
49
- #
50
- # See examples/wow_realm_status.rb for a working example.
51
- #
52
- # WARNING: If you try to create an alias with the same name as the attribute,
53
- # and then use it, you _will_ cause an infinite loop.
54
- def alias_accessors(alias_map)
55
- alias_map.each do |attribute_name, alias_name|
56
- self.class_eval(
57
- AliasAccessors.build_aliased_attribute_getter(attribute_name, alias_name) +
58
- AliasAccessors.build_aliased_attribute_setter(attribute_name, alias_name) +
59
- AliasAccessors.build_aliased_attribute_queryer(attribute_name, alias_name)
60
- )
28
+ def self.build_aliased_attribute_queryer(attribute_name, alias_name) # :nodoc:
29
+ "def #{alias_name}?;self.#{attribute_name}?;end;"
30
+ end
31
+
32
+ module ClassMethods
33
+ # Creates an alias set of getter, setter and query methods for
34
+ # attributes that aren't quite the way you'd like them to be; this
35
+ # is especially useful when you have no control over the source web
36
+ # sevice/resource.
37
+ #
38
+ # For example, lets say that a particular resource exposes a
39
+ # User's age as 'a' and sex as 's'. Typically, you'd have to access it as
40
+ # user.a and user.s whereas you's like to access it as user.age and user.sex.
41
+ # This is where alias_accessors comes into the picture. Your User class would
42
+ # look somethig like this:
43
+ #
44
+ # class User
45
+ # include Wrest::Components::Container
46
+ #
47
+ # alias_accessors :a => :age,
48
+ # :s => :sex
49
+ # end
50
+ # This would create the methods user.age, user.age= and user.age? which delegates
51
+ # to user.a, user.a= and user.a? respectively.
52
+ #
53
+ # See examples/wow_realm_status.rb for a working example.
54
+ #
55
+ # WARNING: If you try to create an alias with the same name as the attribute,
56
+ # and then use it, you _will_ cause an infinite loop.
57
+ def alias_accessors(alias_map)
58
+ alias_map.each do |attribute_name, alias_name|
59
+ class_eval(
60
+ AliasAccessors.build_aliased_attribute_getter(attribute_name, alias_name) +
61
+ AliasAccessors.build_aliased_attribute_setter(attribute_name, alias_name) +
62
+ AliasAccessors.build_aliased_attribute_queryer(attribute_name, alias_name)
63
+ )
64
+ end
61
65
  end
62
66
  end
63
67
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2009 Sidu Ponnappa
2
4
 
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -8,117 +10,166 @@
8
10
  # See the License for the specific language governing permissions and limitations under the License.
9
11
 
10
12
  module Wrest
11
- module Components::Container
12
- # An extension to Container that adds support for specifying
13
- # how the values associated with certain attribute keys
14
- # should be typecast.
15
- #
16
- # This extension can be used in situations where the attributes
17
- # hash consists of just strings with no associated tup information.
18
- # For example, params recieved from a web browser may contain
19
- # attributes like
20
- # 'id' => '4', 'dateofbirth' => '1984-04-05'
21
- # and we'd like to have these cast to an integer and a date
22
- # respectively, rather than have to deal with them as strings.
23
- module Typecaster
24
- def self.included(klass) #:nodoc:
25
- klass.extend Typecaster::ClassMethods
26
- klass.class_eval{ include Typecaster::InstanceMethods }
27
- klass.alias_method_chain :initialize, :typecasting
28
- end
13
+ module Components
14
+ module Container
15
+ # An extension to Container that adds support for specifying
16
+ # how the values associated with certain attribute keys
17
+ # should be typecast.
18
+ #
19
+ # This extension can be used in situations where the attributes
20
+ # hash consists of just strings with no associated tup information.
21
+ # For example, params recieved from a web browser may contain
22
+ # attributes like
23
+ # 'id' => '4', 'dateofbirth' => '1984-04-05'
24
+ # and we'd like to have these cast to an integer and a date
25
+ # respectively, rather than have to deal with them as strings.
26
+ module Typecaster
27
+ PARSING = {
28
+ 'symbol' => proc { |symbol| symbol.to_s.to_sym },
29
+ 'date' => proc { |date| ::Date.parse(date) },
30
+ 'datetime' => proc { |time|
31
+ begin
32
+ Time.xmlschema(time).utc
33
+ rescue StandardError
34
+ ::DateTime.parse(time).utc
35
+ end
36
+ },
37
+ 'integer' => proc { |integer| integer.to_i },
38
+ 'float' => proc { |float| float.to_f },
39
+ 'decimal' => proc do |number|
40
+ if number.is_a?(String)
41
+ number.to_d
42
+ else
43
+ BigDecimal(number)
44
+ end
45
+ end,
46
+ 'boolean' => proc { |boolean| %w[1 true].include?(boolean.to_s.strip) },
47
+ 'string' => proc { |string| string.to_s },
48
+ 'yaml' => proc { |yaml|
49
+ begin
50
+ YAML.safe_load(yaml)
51
+ rescue StandardError
52
+ yaml
53
+ end
54
+ },
55
+ 'base64Binary' => proc { |bin| ::Base64.decode64(bin) },
56
+ 'binary' => proc { |bin, entity| _parse_binary(bin, entity) },
57
+ 'file' => proc { |file, entity| _parse_file(file, entity) },
58
+ 'double' => proc { |float| float.to_f },
59
+ 'dateTime' => proc { |time|
60
+ begin
61
+ Time.xmlschema(time).utc
62
+ rescue StandardError
63
+ ::DateTime.parse(time).utc
64
+ end
65
+ }
66
+ }.freeze
29
67
 
30
- module Helpers
31
- def as_base64Binary
32
- ActiveSupport::XmlMini::PARSING['base64Binary']
68
+ def self.included(klass)
69
+ # :nodoc:
70
+ klass.extend Typecaster::ClassMethods
71
+ klass.class_eval { include Typecaster::InstanceMethods }
72
+ klass.send(:alias_method, :initialize_without_typecasting, :initialize)
73
+ klass.send(:alias_method, :initialize, :initialize_with_typecasting)
33
74
  end
34
75
 
35
- def as_boolean
36
- ActiveSupport::XmlMini::PARSING['boolean']
37
- end
76
+ module Helpers
77
+ def as_base64_binary
78
+ PARSING['base64Binary']
79
+ end
38
80
 
39
- def as_decimal
40
- ActiveSupport::XmlMini::PARSING['decimal']
41
- end
81
+ def as_boolean
82
+ PARSING['boolean']
83
+ end
42
84
 
43
- def as_date
44
- ActiveSupport::XmlMini::PARSING['date']
45
- end
85
+ def as_decimal
86
+ PARSING['decimal']
87
+ end
46
88
 
47
- def as_datetime
48
- ActiveSupport::XmlMini::PARSING['datetime']
49
- end
89
+ def as_date
90
+ PARSING['date']
91
+ end
50
92
 
51
- def as_float
52
- ActiveSupport::XmlMini::PARSING['float']
53
- end
93
+ def as_datetime
94
+ PARSING['datetime']
95
+ end
54
96
 
55
- def as_integer
56
- ActiveSupport::XmlMini::PARSING['integer']
57
- end
97
+ def as_float
98
+ PARSING['float']
99
+ end
58
100
 
59
- def as_symbol
60
- ActiveSupport::XmlMini::PARSING['symbol']
61
- end
101
+ def as_integer
102
+ PARSING['integer']
103
+ end
62
104
 
63
- def as_yaml
64
- ActiveSupport::XmlMini::PARSING['yaml']
65
- end
66
- end
105
+ def as_symbol
106
+ PARSING['symbol']
107
+ end
67
108
 
68
- module ClassMethods
69
- # Accepts a set of attribute-name/lambda pairs which are used
70
- # to typecast string values injected through the constructor.
71
- # Typically needed when populating an +Container+
72
- # directly from request params. Typecasting kicks in for
73
- # a given value _only_ if it is a String, Hash or Array, the
74
- # three classes that deserilisation can produce.
75
- #
76
- # Typecast information is inherited by subclasses; however be
77
- # aware that explicitly invoking +typecast+ in a subclass will
78
- # discard inherited typecast information leaving only the casts
79
- # defined in the subclass.
80
- #
81
- # Note that this _will_ increase the time needed to initialize
82
- # instances.
83
- #
84
- # Common typecasts such as integer, float, datetime etc. are
85
- # available through predefined helpers. See TypecastHelpers
86
- # for a full list.
87
- #
88
- # Example:
89
- #
90
- # class Demon
91
- # include Wrest::Components::Container
92
- # include Wrest::Components::Container::Typecaster
93
- #
94
- # typecast :age => as_integer,
95
- # :chi => lambda{|chi| Chi.new(chi)}
96
- # end
97
- #
98
- # kai_wren = Demon.new('age' => '1500', 'chi' => '1024')
99
- # kai_wren.age # => 1500
100
- # kai_wren.chi # => #<Chi:0x113af8c @count="1024">
101
- def typecast(cast_map)
102
- @typecast_map = @typecast_map ? @typecast_map.merge(cast_map.symbolize_keys) : cast_map.symbolize_keys
109
+ def as_yaml
110
+ PARSING['yaml']
111
+ end
103
112
  end
104
113
 
105
- def typecast_map #:nodoc:
106
- if defined?(@typecast_map)
107
- @typecast_map
108
- elsif superclass != Object && superclass.respond_to?(:typecast_map)
109
- superclass.typecast_map
110
- else
111
- {}
114
+ module ClassMethods
115
+ # Accepts a set of attribute-name/lambda pairs which are used
116
+ # to typecast string values injected through the constructor.
117
+ # Typically needed when populating an +Container+
118
+ # directly from request params. Typecasting kicks in for
119
+ # a given value _only_ if it is a String, Hash or Array, the
120
+ # three classes that deserilisation can produce.
121
+ #
122
+ # Typecast information is inherited by subclasses; however be
123
+ # aware that explicitly invoking +typecast+ in a subclass will
124
+ # discard inherited typecast information leaving only the casts
125
+ # defined in the subclass.
126
+ #
127
+ # Note that this _will_ increase the time needed to initialize
128
+ # instances.
129
+ #
130
+ # Common typecasts such as integer, float, datetime etc. are
131
+ # available through predefined helpers. See TypecastHelpers
132
+ # for a full list.
133
+ #
134
+ # Example:
135
+ #
136
+ # class Demon
137
+ # include Wrest::Components::Container
138
+ # include Wrest::Components::Container::Typecaster
139
+ #
140
+ # typecast :age => as_integer,
141
+ # :chi => lambda{|chi| Chi.new(chi)}
142
+ # end
143
+ #
144
+ # kai_wren = Demon.new('age' => '1500', 'chi' => '1024')
145
+ # kai_wren.age # => 1500
146
+ # kai_wren.chi # => #<Chi:0x113af8c @count="1024">
147
+ def typecast(cast_map)
148
+ @typecast_map = @typecast_map ? @typecast_map.merge(cast_map.transform_keys(&:to_sym)) : cast_map.transform_keys(&:to_sym)
149
+ end
150
+
151
+ def typecast_map # :nodoc:
152
+ if defined?(@typecast_map)
153
+ @typecast_map
154
+ elsif superclass != Object && superclass.respond_to?(:typecast_map)
155
+ superclass.typecast_map
156
+ else
157
+ {}
158
+ end
112
159
  end
113
160
  end
114
- end
115
161
 
116
- module InstanceMethods # :nodoc:
117
- def initialize_with_typecasting(attributes = {}) # :nodoc:
118
- initialize_without_typecasting(attributes)
119
- self.class.typecast_map.each do |key, typecaster|
120
- value = @attributes[key]
121
- @attributes[key] = typecaster.call(value) if (value.is_a?(String) || value.is_a?(Hash) || value.is_a?(Array))
162
+ module InstanceMethods # :nodoc:
163
+ def initialize_with_typecasting(attributes = {})
164
+ # :nodoc:
165
+ initialize_without_typecasting(attributes)
166
+ self.class.typecast_map.each do |key, typecaster|
167
+ value = @attributes[key]
168
+ if value.is_a?(String) || value.is_a?(Hash) || value.is_a?(Array)
169
+ @attributes[key] =
170
+ typecaster.call(value)
171
+ end
172
+ end
122
173
  end
123
174
  end
124
175
  end