wrest 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +51 -19
- data/Rakefile +8 -4
- data/VERSION.yml +1 -1
- data/lib/wrest.rb +11 -2
- data/lib/wrest/components.rb +1 -2
- data/lib/wrest/components/attributes_container.rb +31 -69
- data/lib/wrest/components/attributes_container/typecaster.rb +121 -0
- data/lib/wrest/components/mutators.rb +18 -1
- data/lib/wrest/components/mutators/base.rb +52 -39
- data/lib/wrest/components/mutators/camel_to_snake_case.rb +7 -5
- data/lib/wrest/components/mutators/xml_mini_type_caster.rb +43 -0
- data/lib/wrest/components/mutators/xml_simple_type_caster.rb +22 -20
- data/lib/wrest/components/translators.rb +20 -17
- data/lib/wrest/components/translators/content_types.rb +2 -2
- data/lib/wrest/components/translators/json.rb +11 -8
- data/lib/wrest/components/translators/xml.rb +9 -12
- data/lib/wrest/core_ext/hash/conversions.rb +1 -1
- data/lib/wrest/resource/base.rb +25 -13
- data/lib/wrest/resource/state.rb +6 -0
- data/lib/wrest/response.rb +4 -0
- data/lib/wrest/uri.rb +5 -1
- data/lib/wrest/version.rb +1 -1
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +8 -1
- data/spec/wrest/components/attributes_container/typecaster_spec.rb +63 -0
- data/spec/wrest/components/attributes_container_spec.rb +6 -61
- data/spec/wrest/components/mutators/base_spec.rb +5 -1
- data/spec/wrest/components/mutators/xml_mini_type_caster_spec.rb +75 -0
- data/spec/wrest/components/mutators_spec.rb +21 -0
- data/spec/wrest/components/translators/xml_spec.rb +1 -1
- data/spec/wrest/components/translators_spec.rb +9 -0
- data/spec/wrest/uri_spec.rb +16 -4
- metadata +14 -15
- data/lib/wrest/components/typecast_helpers.rb +0 -41
data/README.rdoc
CHANGED
@@ -40,30 +40,56 @@ You can launch the interactive Wrest shell by running bin/wrest if you have the
|
|
40
40
|
)
|
41
41
|
=== Basic Http Calls
|
42
42
|
|
43
|
-
====
|
43
|
+
==== GET
|
44
44
|
|
45
|
-
A couple of ways to get the Yahoo news as hash map
|
45
|
+
A couple of ways to get the Yahoo news as hash map.
|
46
46
|
|
47
47
|
* This example simply does a get on a uri and figures out the appropriate deserialiser using the content-type (in this case 'text/javascript', which uses Wrest::Translators::Json). See content_types.rb under lib/wrest/mappers/translators.
|
48
48
|
"http://search.yahooapis.com/NewsSearchService/V1/newsSearch?appid=YahooDemo&output=json&query=India&results=3&start=1".to_uri.get.deserialise
|
49
49
|
|
50
50
|
* This example does a get on a base uri with several parameters passed to it, resulting in a uri essentially the same as the one above. It also shows how you can specify a custom deserialiser to produce a hash-map from the response, as well as a hash mutator to clean up the deserialised hash.
|
51
|
-
|
52
|
-
|
51
|
+
require 'rubygems'
|
52
|
+
require 'wrest'
|
53
|
+
include Wrest::Components
|
54
|
+
y "http://search.yahooapis.com/NewsSearchService/V1/newsSearch".to_uri.get(
|
55
|
+
:appid => 'YahooDemo',
|
56
|
+
:output => 'xml',
|
57
|
+
:query => 'India',
|
58
|
+
:results=> '3',
|
59
|
+
:start => '1'
|
60
|
+
).deserialise_using(
|
61
|
+
Translators::Xml
|
62
|
+
).mutate_using(
|
63
|
+
Mutators::XmlSimpleTypeCaster.new
|
64
|
+
)
|
65
|
+
|
66
|
+
==== OPTIONS
|
67
|
+
|
68
|
+
To find out what actions are permitted on a URI:
|
69
|
+
|
70
|
+
'http://www.yahoo.com'.to_uri.options.headers['allow']
|
71
|
+
|
72
|
+
|
73
|
+
=== Attributes Container
|
74
|
+
|
75
|
+
Allows any class to hold an attributes hash, somewhat like ActiveResource. It also supports several extensions to this base fuctionality such as support for typecasting attribute values.
|
76
|
+
|
77
|
+
Example:
|
78
|
+
|
79
|
+
class Demon
|
80
|
+
include Wrest::Components::AttributesContainer
|
81
|
+
include Wrest::Components::AttributesContainer::Typecaster
|
53
82
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
:query => 'India',
|
59
|
-
:results=> '3',
|
60
|
-
:start => '1'
|
61
|
-
).deserialise_using(
|
62
|
-
Translators::Xml
|
63
|
-
).mutate_using(
|
64
|
-
Mutators::XmlSimpleTypeCaster.new
|
65
|
-
)
|
83
|
+
always_has :id
|
84
|
+
typecast :age => as_integer,
|
85
|
+
:chi => lambda{|chi| Chi.new(chi)}
|
86
|
+
end
|
66
87
|
|
88
|
+
kai_wren = Demon.new('id => '1', 'age' => '1500', 'chi' => '1024', 'teacher' => 'Viss')
|
89
|
+
kai_wren.id # => '1'
|
90
|
+
kai_wren.age # => 1500
|
91
|
+
kai_wren.chi # => #<Chi:0x113af8c @count="1024">
|
92
|
+
kai_wren.teacher # => 'Viss'
|
67
93
|
|
68
94
|
=== Logging
|
69
95
|
|
@@ -80,9 +106,14 @@ Wrest RDocs can be found at http://wrest.rubyforge.org
|
|
80
106
|
|
81
107
|
== Wrest::Resource
|
82
108
|
|
83
|
-
Wrest::Resource is an alternative to ActiveResource. It targets Rails REST services and is currently under development.
|
109
|
+
Wrest::Resource is an alternative to ActiveResource. It targets Rails REST (well, POX - turns out Rails isn't really REST) services and is currently under development.
|
84
110
|
|
85
|
-
* No more pretending that REST resources are the same as database
|
111
|
+
* No more pretending that REST resources are the same as records in a database (yeah, no more freaking ActiveResource::Connection)
|
112
|
+
* Treat put as 'create or update,' not just 'update'
|
113
|
+
* Response codes result in user defined state transitions; favours state transitions based on response code over arbitrary ones
|
114
|
+
* Supports moving toward hypermedia links as opposed to client server collusion through URI templates
|
115
|
+
* The header is now exposed as metadata, rather being than something you have no control over
|
116
|
+
* Out of the box support for If-Unmodified-Since/If-Match+Etag
|
86
117
|
* Out of the box support for collections
|
87
118
|
* Out of the box support for collection pagination (including support for WillPaginate), both header based and xml attribute based
|
88
119
|
* Out of the box support for operations on all the records on the collection
|
@@ -92,14 +123,15 @@ Wrest::Resource is an alternative to ActiveResource. It targets Rails REST servi
|
|
92
123
|
* More natural mapping of deserialised entities to existing classes
|
93
124
|
* No communication via exceptions for http error status codes
|
94
125
|
* Better extensibility - allows access to request/response objects, avoids class variables, favours symbols over strings etc.
|
126
|
+
* Consider support for OPTIONS and response codes 100/417
|
95
127
|
|
96
128
|
== Dependencies
|
97
129
|
|
98
130
|
=== Source
|
99
131
|
* gems
|
100
|
-
* xmlsimple
|
101
132
|
* json (json-jruby on JRuby)
|
102
133
|
* active_support
|
134
|
+
* ruby-libxml (Recommended, will fall back to REXML if absent; be aware that Wrest uses ActiveSupport::XmlMini, so if you're using Wrest as a plugin in a Rails application, all xml parsing across the application will switch to using libxml if it's available. You're free to change this by hand by using the ActiveSupport::XmlMini.backend= method.)
|
103
135
|
|
104
136
|
=== Build
|
105
137
|
* rspec
|
data/Rakefile
CHANGED
@@ -10,7 +10,6 @@
|
|
10
10
|
require 'rubygems'
|
11
11
|
gem 'rspec'
|
12
12
|
require 'rake'
|
13
|
-
require 'hanna/rdoctask'
|
14
13
|
require 'spec'
|
15
14
|
require 'spec/rake/spectask'
|
16
15
|
|
@@ -25,6 +24,12 @@ Spec::Rake::SpecTask.new(:spec) do |task|
|
|
25
24
|
task.spec_opts = ['--options', 'spec/spec.opts']
|
26
25
|
end
|
27
26
|
|
27
|
+
begin
|
28
|
+
require 'hanna/rdoctask'
|
29
|
+
rescue LoadError
|
30
|
+
puts 'Hanna not available, using standard Rake rdoctask. Fix this by running gem install mislav-hanna.'
|
31
|
+
require 'rake/rdoctask'
|
32
|
+
end
|
28
33
|
desc 'Generate documentation for Wrest'
|
29
34
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
30
35
|
rdoc.rdoc_dir = 'rdoc'
|
@@ -62,12 +67,11 @@ begin
|
|
62
67
|
gemspec.homepage = "http://github.com/kaiwren/wrest"
|
63
68
|
gemspec.has_rdoc = true
|
64
69
|
gemspec.rubyforge_project = 'wrest'
|
65
|
-
gemspec.executables = ['wrest']
|
70
|
+
gemspec.executables = ['wrest', 'jwrest']
|
66
71
|
gemspec.require_path = "lib"
|
67
72
|
gemspec.files.exclude 'spec/wrest/meh_spec.rb'
|
68
73
|
gemspec.test_files.exclude 'spec/wrest/meh_spec.rb'
|
69
|
-
gemspec.add_dependency('activesupport', '>= 2.
|
70
|
-
gemspec.add_dependency('xml-simple', '>= 1.0.11')
|
74
|
+
gemspec.add_dependency('activesupport', '>= 2.3.2')
|
71
75
|
case RUBY_PLATFORM
|
72
76
|
when /java/
|
73
77
|
gemspec.add_dependency('json-jruby', '>= 1.1.3')
|
data/VERSION.yml
CHANGED
data/lib/wrest.rb
CHANGED
@@ -7,6 +7,8 @@
|
|
7
7
|
# See the License for the specific language governing permissions and limitations under the License.
|
8
8
|
|
9
9
|
require 'rubygems'
|
10
|
+
gem 'activesupport', '>= 2.3.2'
|
11
|
+
|
10
12
|
require 'net/http'
|
11
13
|
require 'net/https'
|
12
14
|
require 'forwardable'
|
@@ -19,7 +21,7 @@ require 'active_support'
|
|
19
21
|
|
20
22
|
WREST_ROOT = File.dirname(__FILE__)
|
21
23
|
|
22
|
-
module Wrest
|
24
|
+
module Wrest #:nodoc:
|
23
25
|
def self.logger=(logger)
|
24
26
|
@logger = logger
|
25
27
|
end
|
@@ -29,9 +31,16 @@ module Wrest
|
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
|
-
Wrest.logger =
|
34
|
+
Wrest.logger = ActiveSupport::BufferedLogger.new(STDOUT)
|
33
35
|
Wrest.logger.level = Logger::DEBUG
|
34
36
|
|
37
|
+
begin
|
38
|
+
gem 'libxml-ruby', '>= 1.1.3'
|
39
|
+
ActiveSupport::XmlMini.backend='LibXML'
|
40
|
+
rescue Gem::LoadError
|
41
|
+
Wrest.logger.warn "LibXML >= 1.1.3 not found, falling back to #{ActiveSupport::XmlMini.backend}. To install LibXML run `sudo gem install ruby-libxml`"
|
42
|
+
end
|
43
|
+
|
35
44
|
source_dirs = ["/wrest/core_ext/*.rb", "/wrest/*.rb"]
|
36
45
|
|
37
46
|
source_dirs.each{|directory|
|
data/lib/wrest/components.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
8
8
|
# See the License for the specific language governing permissions and limitations under the License.
|
9
9
|
|
10
|
-
module Wrest
|
10
|
+
module Wrest
|
11
11
|
# A component is a building block that can
|
12
12
|
# be used while building an object oriented wrapper
|
13
13
|
# around a REST service
|
@@ -15,7 +15,6 @@ module Wrest #:nodoc:
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
require "#{WREST_ROOT}/wrest/components/typecast_helpers"
|
19
18
|
require "#{WREST_ROOT}/wrest/components/attributes_container"
|
20
19
|
require "#{WREST_ROOT}/wrest/components/mutators"
|
21
20
|
require "#{WREST_ROOT}/wrest/components/translators"
|
@@ -7,16 +7,23 @@
|
|
7
7
|
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
8
8
|
# See the License for the specific language governing permissions and limitations under the License.
|
9
9
|
|
10
|
-
module Wrest
|
10
|
+
module Wrest
|
11
|
+
module Components::AttributesContainer
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require "#{WREST_ROOT}/wrest/components/attributes_container/typecaster"
|
16
|
+
|
17
|
+
module Wrest::Components
|
11
18
|
|
12
19
|
# Adds behaviour allowing a class to
|
13
20
|
# contain attributes and providing support
|
14
21
|
# for dynamic getters, setters and query methods.
|
15
22
|
# 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
|
23
|
+
# invocation and on a per instance basis.
|
24
|
+
# <tt>respond_to?</tt> however will respond as though
|
18
25
|
# they are all already present.
|
19
|
-
# This means that two different instances of the same
|
26
|
+
# This means that two different instances of the same
|
20
27
|
# AttributesContainer could well have
|
21
28
|
# different attribute getters/setters/query methods.
|
22
29
|
#
|
@@ -30,99 +37,60 @@ module Wrest::Components #:nodoc:
|
|
30
37
|
# In situations where this is a problem, such as a client consuming Rails
|
31
38
|
# REST services where <tt>id</tt> is a common attribute and clashes with
|
32
39
|
# Object#id, it is recommended to create getter/setter/query methods
|
33
|
-
# on the class (which affects all instances) using the +
|
40
|
+
# on the class (which affects all instances) using the +always_has+ macro.
|
34
41
|
#
|
35
42
|
# If you're implementing your own initialize method
|
36
|
-
# remember to delegate to the default initialize
|
43
|
+
# remember to delegate to the default initialize
|
37
44
|
# of AttributesContainer by invoking <tt>super(attributes)</tt>
|
38
45
|
#
|
39
46
|
# Example:
|
40
47
|
# class ShenCoin
|
41
48
|
# include Wrest::Components::AttributesContainer
|
49
|
+
# include Wrest::Components::AttributesContainer::Typecaster
|
42
50
|
#
|
43
|
-
#
|
44
|
-
# typecast
|
45
|
-
# end
|
51
|
+
# always_has :id
|
52
|
+
# typecast :id => as_integer
|
53
|
+
# end
|
46
54
|
# coin = ShenCoin.new(:id => '5', :chi_count => 500, :owner => 'Kai Wren')
|
47
55
|
# coin.id # => 5
|
48
56
|
# coin.owner # => 'Kai Wren'
|
49
57
|
module AttributesContainer
|
50
58
|
def self.included(klass) #:nodoc:
|
51
59
|
klass.extend AttributesContainer::ClassMethods
|
52
|
-
klass.extend TypecastHelpers
|
53
60
|
klass.class_eval{ include AttributesContainer::InstanceMethods }
|
54
61
|
end
|
55
|
-
|
62
|
+
|
56
63
|
def self.build_attribute_getter(attribute_name) #:nodoc:
|
57
64
|
"def #{attribute_name};@attributes[:#{attribute_name}];end;"
|
58
65
|
end
|
59
|
-
|
66
|
+
|
60
67
|
def self.build_attribute_setter(attribute_name) #:nodoc:
|
61
68
|
"def #{attribute_name}=(value);@attributes[:#{attribute_name}] = value;end;"
|
62
69
|
end
|
63
|
-
|
70
|
+
|
64
71
|
def self.build_attribute_queryer(attribute_name) #:nodoc:
|
65
72
|
"def #{attribute_name}?;not @attributes[:#{attribute_name}].nil?;end;"
|
66
73
|
end
|
67
|
-
|
74
|
+
|
68
75
|
module ClassMethods
|
69
76
|
# This macro explicitly creates getter, setter and query methods on
|
70
|
-
# a class, overriding any exisiting methods with the same names.
|
77
|
+
# a class, overriding any exisiting methods with the same names.
|
71
78
|
# This can be used when attribute names clash with method names;
|
72
79
|
# an example would be Rails REST services which frequently make use
|
73
80
|
# an attribute named <tt>id</tt> which clashes with Object#id. Also,
|
74
81
|
# this can be used as a performance optimisation if the incoming
|
75
82
|
# attributes are known beforehand.
|
76
|
-
def
|
83
|
+
def always_has(*attribute_names)
|
77
84
|
attribute_names.each do |attribute_name|
|
78
85
|
self.class_eval(
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
)
|
86
|
+
AttributesContainer.build_attribute_getter(attribute_name) +
|
87
|
+
AttributesContainer.build_attribute_setter(attribute_name) +
|
88
|
+
AttributesContainer.build_attribute_queryer(attribute_name)
|
89
|
+
)
|
83
90
|
end
|
84
91
|
end
|
85
|
-
|
86
|
-
# Accepts a set of attribute-name/lambda pairs which are used
|
87
|
-
# to typecast string values injected through the constructor.
|
88
|
-
# Typically needed when populating an +AttributesContainer+
|
89
|
-
# directly from request params. Typecasting kicks in for
|
90
|
-
# a given value _only_ if it is a string.
|
91
|
-
#
|
92
|
-
# Typcast information is inherited by subclasses; however be
|
93
|
-
# aware that explicitly invoking +typecast+ in a subclass will
|
94
|
-
# discard inherited typecast information leaving only the casts
|
95
|
-
# defined in the subclass.
|
96
|
-
#
|
97
|
-
# Common typecasts such as integer, float, datetime etc. are
|
98
|
-
# available through predefined helpers. See TypecastHelpers
|
99
|
-
# for a full list.
|
100
|
-
#
|
101
|
-
# Example:
|
102
|
-
#
|
103
|
-
# class Demon
|
104
|
-
# include Wrest::Components::AttributesContainer
|
105
|
-
# typecast :age => as_integer,
|
106
|
-
# :chi => lambda{|chi| Chi.new(chi)}
|
107
|
-
# end
|
108
|
-
# kai_wren = Demon.new('age' => '1500', 'chi' => '1024')
|
109
|
-
# kai_wren.age # => 1500
|
110
|
-
# kai_wren.chi # => #<Chi:0x113af8c @count="1024">
|
111
|
-
def typecast(cast_map)
|
112
|
-
@typecast_map = @typecast_map ? @typecast_map.merge(cast_map.symbolize_keys) : cast_map.symbolize_keys
|
113
|
-
end
|
114
|
-
|
115
|
-
def typecast_map #:nodoc:
|
116
|
-
if defined?(@typecast_map)
|
117
|
-
@typecast_map
|
118
|
-
elsif superclass != Object && superclass.respond_to?(:typecast_map)
|
119
|
-
superclass.typecast_map
|
120
|
-
else
|
121
|
-
{}
|
122
|
-
end
|
123
|
-
end
|
124
92
|
end
|
125
|
-
|
93
|
+
|
126
94
|
module InstanceMethods
|
127
95
|
# Sets up any class to act like
|
128
96
|
# an attributes container by creating
|
@@ -132,22 +100,18 @@ module Wrest::Components #:nodoc:
|
|
132
100
|
# own class.
|
133
101
|
def initialize(attributes = {})
|
134
102
|
@attributes = attributes.symbolize_keys
|
135
|
-
self.class.typecast_map.each do |key, typecaster|
|
136
|
-
value = @attributes[key]
|
137
|
-
@attributes[key] = typecaster.call(value) if value.is_a?(String)
|
138
|
-
end
|
139
103
|
@interface = Module.new
|
140
104
|
self.extend @interface
|
141
105
|
end
|
142
|
-
|
106
|
+
|
143
107
|
def [](key)
|
144
108
|
@attributes[key.to_sym]
|
145
109
|
end
|
146
|
-
|
110
|
+
|
147
111
|
def []=(key, value)
|
148
112
|
@attributes[key.to_sym] = value
|
149
113
|
end
|
150
|
-
|
114
|
+
|
151
115
|
def respond_to?(method_name, include_private = false)
|
152
116
|
super(method_name, include_private) ? true : @attributes.include?(method_name.to_s.gsub(/(\?$)|(=$)/, '').to_sym)
|
153
117
|
end
|
@@ -157,7 +121,6 @@ module Wrest::Components #:nodoc:
|
|
157
121
|
def method_missing(method_sym, *arguments)
|
158
122
|
method_name = method_sym.to_s
|
159
123
|
attribute_name = method_name.gsub(/(\?$)|(=$)/, '')
|
160
|
-
|
161
124
|
if @attributes.include?(attribute_name.to_sym) || method_name.last == '='
|
162
125
|
case method_name.last
|
163
126
|
when '='
|
@@ -172,7 +135,6 @@ module Wrest::Components #:nodoc:
|
|
172
135
|
super(method_sym, *arguments)
|
173
136
|
end
|
174
137
|
end
|
175
|
-
|
176
138
|
end
|
177
139
|
end
|
178
140
|
end
|
@@ -0,0 +1,121 @@
|
|
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 Components::AttributesContainer
|
12
|
+
# An extension to AttributesContainer that adds support for
|
13
|
+
# specifying 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
|
29
|
+
|
30
|
+
module ClassMethods
|
31
|
+
# Accepts a set of attribute-name/lambda pairs which are used
|
32
|
+
# to typecast string values injected through the constructor.
|
33
|
+
# Typically needed when populating an +AttributesContainer+
|
34
|
+
# directly from request params. Typecasting kicks in for
|
35
|
+
# a given value _only_ if it is a string.
|
36
|
+
#
|
37
|
+
# Typecast information is inherited by subclasses; however be
|
38
|
+
# aware that explicitly invoking +typecast+ in a subclass will
|
39
|
+
# discard inherited typecast information leaving only the casts
|
40
|
+
# defined in the subclass.
|
41
|
+
#
|
42
|
+
# Common typecasts such as integer, float, datetime etc. are
|
43
|
+
# available through predefined helpers. See TypecastHelpers
|
44
|
+
# for a full list.
|
45
|
+
#
|
46
|
+
# Example:
|
47
|
+
#
|
48
|
+
# class Demon
|
49
|
+
# include Wrest::Components::AttributesContainer
|
50
|
+
# include Wrest::Components::AttributesContainer::Typecaster
|
51
|
+
#
|
52
|
+
# typecast :age => as_integer,
|
53
|
+
# :chi => lambda{|chi| Chi.new(chi)}
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# kai_wren = Demon.new('age' => '1500', 'chi' => '1024')
|
57
|
+
# kai_wren.age # => 1500
|
58
|
+
# kai_wren.chi # => #<Chi:0x113af8c @count="1024">
|
59
|
+
def typecast(cast_map)
|
60
|
+
@typecast_map = @typecast_map ? @typecast_map.merge(cast_map.symbolize_keys) : cast_map.symbolize_keys
|
61
|
+
end
|
62
|
+
|
63
|
+
def typecast_map #:nodoc:
|
64
|
+
if defined?(@typecast_map)
|
65
|
+
@typecast_map
|
66
|
+
elsif superclass != Object && superclass.respond_to?(:typecast_map)
|
67
|
+
superclass.typecast_map
|
68
|
+
else
|
69
|
+
{}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def as_base64Binary
|
74
|
+
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['base64Binary']
|
75
|
+
end
|
76
|
+
|
77
|
+
def as_boolean
|
78
|
+
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['boolean']
|
79
|
+
end
|
80
|
+
|
81
|
+
def as_decimal
|
82
|
+
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['decimal']
|
83
|
+
end
|
84
|
+
|
85
|
+
def as_date
|
86
|
+
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['date']
|
87
|
+
end
|
88
|
+
|
89
|
+
def as_datetime
|
90
|
+
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['datetime']
|
91
|
+
end
|
92
|
+
|
93
|
+
def as_float
|
94
|
+
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['float']
|
95
|
+
end
|
96
|
+
|
97
|
+
def as_integer
|
98
|
+
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['integer']
|
99
|
+
end
|
100
|
+
|
101
|
+
def as_symbol
|
102
|
+
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['symbol']
|
103
|
+
end
|
104
|
+
|
105
|
+
def as_yaml
|
106
|
+
ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING['yaml']
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module InstanceMethods # :nodoc:
|
111
|
+
def initialize_with_typecasting(attributes = {}) # :nodoc:
|
112
|
+
initialize_without_typecasting(attributes)
|
113
|
+
self.class.typecast_map.each do |key, typecaster|
|
114
|
+
value = @attributes[key]
|
115
|
+
@attributes[key] = typecaster.call(value) if value.is_a?(String)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|