simplemapper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +16 -0
- data/README +0 -0
- data/Rakefile +78 -0
- data/doc/classes/Array.html +139 -0
- data/doc/classes/Array.src/M000001.html +34 -0
- data/doc/classes/Callbacks.html +161 -0
- data/doc/classes/Callbacks.src/M000037.html +18 -0
- data/doc/classes/Callbacks.src/M000038.html +18 -0
- data/doc/classes/Callbacks.src/M000039.html +19 -0
- data/doc/classes/Enumerable.html +131 -0
- data/doc/classes/Enumerable.src/M000040.html +21 -0
- data/doc/classes/Hash.html +154 -0
- data/doc/classes/Hash.src/M000002.html +18 -0
- data/doc/classes/Hash.src/M000003.html +34 -0
- data/doc/classes/Inflector/Inflections.html +323 -0
- data/doc/classes/Inflector/Inflections.src/M000031.html +18 -0
- data/doc/classes/Inflector/Inflections.src/M000032.html +18 -0
- data/doc/classes/Inflector/Inflections.src/M000033.html +18 -0
- data/doc/classes/Inflector/Inflections.src/M000034.html +19 -0
- data/doc/classes/Inflector/Inflections.src/M000035.html +18 -0
- data/doc/classes/Inflector/Inflections.src/M000036.html +23 -0
- data/doc/classes/Inflector.html +516 -0
- data/doc/classes/Inflector.src/M000017.html +22 -0
- data/doc/classes/Inflector.src/M000018.html +25 -0
- data/doc/classes/Inflector.src/M000019.html +25 -0
- data/doc/classes/Inflector.src/M000020.html +22 -0
- data/doc/classes/Inflector.src/M000021.html +18 -0
- data/doc/classes/Inflector.src/M000022.html +22 -0
- data/doc/classes/Inflector.src/M000023.html +18 -0
- data/doc/classes/Inflector.src/M000024.html +18 -0
- data/doc/classes/Inflector.src/M000025.html +18 -0
- data/doc/classes/Inflector.src/M000026.html +18 -0
- data/doc/classes/Inflector.src/M000027.html +19 -0
- data/doc/classes/Inflector.src/M000028.html +18 -0
- data/doc/classes/Inflector.src/M000029.html +22 -0
- data/doc/classes/Inflector.src/M000030.html +27 -0
- data/doc/classes/Merb/Request.html +139 -0
- data/doc/classes/Merb/Request.src/M000041.html +22 -0
- data/doc/classes/Merb.html +111 -0
- data/doc/classes/OAuth/RequestProxy/Base.html +139 -0
- data/doc/classes/OAuth/RequestProxy/Base.src/M000012.html +18 -0
- data/doc/classes/OAuth/RequestProxy.html +111 -0
- data/doc/classes/OAuth/Signature/Base.html +139 -0
- data/doc/classes/OAuth/Signature/Base.src/M000011.html +25 -0
- data/doc/classes/OAuth/Signature.html +111 -0
- data/doc/classes/OAuth.html +112 -0
- data/doc/classes/OAuthController.html +243 -0
- data/doc/classes/OAuthController.src/M000005.html +22 -0
- data/doc/classes/OAuthController.src/M000006.html +18 -0
- data/doc/classes/OAuthController.src/M000007.html +18 -0
- data/doc/classes/OAuthController.src/M000008.html +25 -0
- data/doc/classes/OAuthController.src/M000009.html +19 -0
- data/doc/classes/Object.html +142 -0
- data/doc/classes/Object.src/M000010.html +19 -0
- data/doc/classes/Proc.html +143 -0
- data/doc/classes/Proc.src/M000004.html +18 -0
- data/doc/classes/Serialize.html +189 -0
- data/doc/classes/Serialize.src/M000013.html +21 -0
- data/doc/classes/Serialize.src/M000014.html +39 -0
- data/doc/classes/Serialize.src/M000015.html +16 -0
- data/doc/classes/Serialize.src/M000016.html +46 -0
- data/doc/classes/SimpleMapper/Base.html +528 -0
- data/doc/classes/SimpleMapper/Base.src/M000064.html +16 -0
- data/doc/classes/SimpleMapper/Base.src/M000065.html +16 -0
- data/doc/classes/SimpleMapper/Base.src/M000066.html +22 -0
- data/doc/classes/SimpleMapper/Base.src/M000067.html +21 -0
- data/doc/classes/SimpleMapper/Base.src/M000068.html +26 -0
- data/doc/classes/SimpleMapper/Base.src/M000069.html +18 -0
- data/doc/classes/SimpleMapper/Base.src/M000070.html +18 -0
- data/doc/classes/SimpleMapper/Base.src/M000071.html +18 -0
- data/doc/classes/SimpleMapper/Base.src/M000072.html +19 -0
- data/doc/classes/SimpleMapper/Base.src/M000073.html +23 -0
- data/doc/classes/SimpleMapper/Base.src/M000074.html +18 -0
- data/doc/classes/SimpleMapper/Base.src/M000075.html +20 -0
- data/doc/classes/SimpleMapper/Base.src/M000076.html +18 -0
- data/doc/classes/SimpleMapper/Base.src/M000077.html +18 -0
- data/doc/classes/SimpleMapper/Base.src/M000078.html +19 -0
- data/doc/classes/SimpleMapper/Base.src/M000079.html +18 -0
- data/doc/classes/SimpleMapper/Base.src/M000080.html +18 -0
- data/doc/classes/SimpleMapper/Base.src/M000081.html +19 -0
- data/doc/classes/SimpleMapper/Base.src/M000082.html +20 -0
- data/doc/classes/SimpleMapper/Base.src/M000083.html +24 -0
- data/doc/classes/SimpleMapper/Base.src/M000084.html +18 -0
- data/doc/classes/SimpleMapper/HttpAdapter.html +280 -0
- data/doc/classes/SimpleMapper/HttpAdapter.src/M000056.html +18 -0
- data/doc/classes/SimpleMapper/HttpAdapter.src/M000057.html +18 -0
- data/doc/classes/SimpleMapper/HttpAdapter.src/M000058.html +19 -0
- data/doc/classes/SimpleMapper/HttpAdapter.src/M000060.html +18 -0
- data/doc/classes/SimpleMapper/HttpAdapter.src/M000061.html +18 -0
- data/doc/classes/SimpleMapper/HttpAdapter.src/M000062.html +18 -0
- data/doc/classes/SimpleMapper/HttpAdapter.src/M000063.html +18 -0
- data/doc/classes/SimpleMapper/HttpOAuthExtension.html +188 -0
- data/doc/classes/SimpleMapper/HttpOAuthExtension.src/M000048.html +49 -0
- data/doc/classes/SimpleMapper/HttpOAuthExtension.src/M000049.html +23 -0
- data/doc/classes/SimpleMapper/HttpOAuthExtension.src/M000050.html +18 -0
- data/doc/classes/SimpleMapper/HttpOAuthExtension.src/M000051.html +24 -0
- data/doc/classes/SimpleMapper/SimpleModel/ClassMethods.html +146 -0
- data/doc/classes/SimpleMapper/SimpleModel/ClassMethods.src/M000046.html +19 -0
- data/doc/classes/SimpleMapper/SimpleModel/ClassMethods.src/M000047.html +19 -0
- data/doc/classes/SimpleMapper/SimpleModel.html +184 -0
- data/doc/classes/SimpleMapper/SimpleModel.src/M000042.html +18 -0
- data/doc/classes/SimpleMapper/SimpleModel.src/M000043.html +18 -0
- data/doc/classes/SimpleMapper/SimpleModel.src/M000044.html +18 -0
- data/doc/classes/SimpleMapper/SimpleModel.src/M000045.html +24 -0
- data/doc/classes/SimpleMapper/XmlFormat/ClassMethods.html +152 -0
- data/doc/classes/SimpleMapper/XmlFormat/ClassMethods.src/M000055.html +30 -0
- data/doc/classes/SimpleMapper/XmlFormat.html +169 -0
- data/doc/classes/SimpleMapper/XmlFormat.src/M000052.html +18 -0
- data/doc/classes/SimpleMapper/XmlFormat.src/M000053.html +18 -0
- data/doc/classes/SimpleMapper/XmlFormat.src/M000054.html +18 -0
- data/doc/classes/SimpleMapper.html +146 -0
- data/doc/classes/String.html +120 -0
- data/doc/created.rid +1 -0
- data/doc/files/lib/simple_mapper/adapters/http_adapter_rb.html +110 -0
- data/doc/files/lib/simple_mapper/base_rb.html +108 -0
- data/doc/files/lib/simple_mapper/default_plugins/callbacks_rb.html +101 -0
- data/doc/files/lib/simple_mapper/default_plugins/oauth_rb.html +116 -0
- data/doc/files/lib/simple_mapper/default_plugins/simple_model_rb.html +101 -0
- data/doc/files/lib/simple_mapper/formats/xml_format_rb.html +108 -0
- data/doc/files/lib/simple_mapper/support/bliss_serializer_rb.html +109 -0
- data/doc/files/lib/simple_mapper/support/core_ext_rb.html +108 -0
- data/doc/files/lib/simple_mapper/support/inflections_rb.html +101 -0
- data/doc/files/lib/simple_mapper/support/inflector_rb.html +108 -0
- data/doc/files/lib/simple_mapper/support_rb.html +109 -0
- data/doc/files/lib/simple_mapper_rb.html +137 -0
- data/doc/fr_class_index.html +52 -0
- data/doc/fr_file_index.html +38 -0
- data/doc/fr_method_index.html +110 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/lib/simple_mapper/adapters/http_adapter.rb +64 -0
- data/lib/simple_mapper/base.rb +138 -0
- data/lib/simple_mapper/default_plugins/callbacks.rb +12 -0
- data/lib/simple_mapper/default_plugins/oauth.rb +167 -0
- data/lib/simple_mapper/default_plugins/simple_model.rb +36 -0
- data/lib/simple_mapper/formats/xml_format.rb +48 -0
- data/lib/simple_mapper/support/bliss_serializer.rb +168 -0
- data/lib/simple_mapper/support/core_ext.rb +73 -0
- data/lib/simple_mapper/support/inflections.rb +112 -0
- data/lib/simple_mapper/support/inflector.rb +275 -0
- data/lib/simple_mapper/support.rb +2 -0
- data/lib/simple_mapper.rb +16 -0
- metadata +236 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'simple_mapper/support'
|
2
|
+
|
3
|
+
module SimpleMapper
|
4
|
+
class Base
|
5
|
+
class << self
|
6
|
+
attr_reader :format
|
7
|
+
def debug?; @debug end
|
8
|
+
def debug!; @debug = true end
|
9
|
+
|
10
|
+
def connection_adapter=(adapter,debug=nil,&block)
|
11
|
+
# Should complain if the adapter doesn't exist.
|
12
|
+
@connection_adapter = adapter
|
13
|
+
require "#{File.dirname(__FILE__)}/adapters/#{@connection_adapter}_adapter"
|
14
|
+
@connection_init = block if block_given?
|
15
|
+
@connection_debug = debug
|
16
|
+
end
|
17
|
+
alias :set_connection_adapter :connection_adapter=
|
18
|
+
|
19
|
+
# set_format :xml
|
20
|
+
# self.format = :json
|
21
|
+
def format=(format)
|
22
|
+
@format_name = format.to_s
|
23
|
+
require "#{File.dirname(__FILE__)}/formats/#{@format_name}_format"
|
24
|
+
@format = Object.module_eval("::SimpleMapper::#{@format_name.camelize}Format", __FILE__, __LINE__)
|
25
|
+
include @format
|
26
|
+
end
|
27
|
+
alias :set_format :format=
|
28
|
+
attr_reader :format_name
|
29
|
+
|
30
|
+
def connection(refresh=false)
|
31
|
+
@connection = begin
|
32
|
+
# Initialize the connection with the connection adapter.
|
33
|
+
adapter = Object.module_eval("::SimpleMapper::#{@connection_adapter.to_s.camelize}Adapter", __FILE__, __LINE__).new
|
34
|
+
@connection_init.in_context(adapter).call if @connection_init.is_a?(Proc)
|
35
|
+
adapter.set_headers format.mime_type_headers
|
36
|
+
adapter.debug! if @connection_debug
|
37
|
+
adapter
|
38
|
+
end if !@connection || refresh
|
39
|
+
@connection
|
40
|
+
end
|
41
|
+
|
42
|
+
# get
|
43
|
+
def get(*args)
|
44
|
+
extract_from(connection.get(*args))
|
45
|
+
end
|
46
|
+
|
47
|
+
# new.save
|
48
|
+
def create(*args)
|
49
|
+
new(*args).save
|
50
|
+
end
|
51
|
+
|
52
|
+
def persistent?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def extract_from(formatted_data)
|
57
|
+
objs = send(:"from_#{format_name}", formatted_data)
|
58
|
+
objs.is_a?(Array) ? objs.collect {|e| e.extended {@persisted = true}} : objs.extended {@persisted = true}
|
59
|
+
end
|
60
|
+
|
61
|
+
def extract_one(formatted_data, identifier=nil)
|
62
|
+
objs = extract_from(formatted_data)
|
63
|
+
if objs.is_a?(Array)
|
64
|
+
identifier.nil? ? objs.first : objs.reject {|e| e.identifier != identifier}
|
65
|
+
else
|
66
|
+
identifier.nil? ? objs : (objs.identifier == identifier ? objs : nil)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize(data=nil)
|
72
|
+
self.data = data unless data.nil?
|
73
|
+
end
|
74
|
+
attr_reader :identifier
|
75
|
+
|
76
|
+
def original_data=(data)
|
77
|
+
@original_data = data.freeze
|
78
|
+
@original_attributes = data.keys
|
79
|
+
instantiate(@original_data)
|
80
|
+
end
|
81
|
+
attr_reader :original_data, :original_attributes
|
82
|
+
|
83
|
+
def data=(data)
|
84
|
+
instantiate(data)
|
85
|
+
end
|
86
|
+
def data
|
87
|
+
to_hash
|
88
|
+
end
|
89
|
+
|
90
|
+
# Sets the data into the object. This is provided as a default method, but your model can overwrite it any
|
91
|
+
# way you want. For example, you could set the data to some other object type, or to a Marshalled storage.
|
92
|
+
# The type of data you receive will depend on the format and parser you use. Of course you could make up
|
93
|
+
# your own spin-off of one of those, too.
|
94
|
+
def instantiate(data)
|
95
|
+
raise TypeError, "data must be a hash" unless data.is_a?(Hash)
|
96
|
+
data.each {|k,v| instance_variable_set(:"@#{k}", v)}
|
97
|
+
end
|
98
|
+
|
99
|
+
# Reads the data from the object for saving back to the persisted store. This is provided as a default
|
100
|
+
# method, but you can overwrite it in your model.
|
101
|
+
def formatted_data
|
102
|
+
send(:"to_#{self.class.format_name}")
|
103
|
+
end
|
104
|
+
|
105
|
+
# persisted? ? put : post
|
106
|
+
def save
|
107
|
+
persisted? ? put : post
|
108
|
+
end
|
109
|
+
|
110
|
+
# sends a put request with self.data
|
111
|
+
def put
|
112
|
+
self.data = self.class.extract_one(self.class.connection.put(identifier, formatted_data), identifier).to_hash
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
# sends a post request with self.data
|
117
|
+
def post
|
118
|
+
self.data = self.class.extract_one(self.class.connection.post(formatted_data)).to_hash
|
119
|
+
@persisted = true
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
# delete
|
124
|
+
def delete
|
125
|
+
if self.class.connection.delete(identifier)
|
126
|
+
@persisted = false
|
127
|
+
instance_variable_set('@'+self.class.identifier, nil)
|
128
|
+
true
|
129
|
+
else
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def persisted?
|
135
|
+
!!@persisted
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Callbacks
|
2
|
+
def callbacks
|
3
|
+
@callbacks ||= Hash.new {|h,k| h[k] = []}
|
4
|
+
end
|
5
|
+
def add_callback(name,&block)
|
6
|
+
callbacks[name] << block
|
7
|
+
end
|
8
|
+
def run_callback(name, *args)
|
9
|
+
args = args.first if args.length == 1
|
10
|
+
callbacks[name].inject(args) {|args,cb| cb.call(*args)}
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# gem 'oauth', '=0.2.2'
|
2
|
+
$:.unshift('gems/gems/oauth-0.2.2/lib')
|
3
|
+
require 'oauth'
|
4
|
+
require 'oauth/consumer'
|
5
|
+
require 'oauth/client/net_http'
|
6
|
+
module OAuth
|
7
|
+
module Signature
|
8
|
+
class Base
|
9
|
+
def initialize(request, options = {}, &block)
|
10
|
+
raise TypeError unless request.kind_of?(OAuth::RequestProxy::Base)
|
11
|
+
@request = request
|
12
|
+
if block_given?
|
13
|
+
@token_secret, @consumer_secret = yield block.arity == 1 ? token : [token, consumer_key,nonce,request.timestamp]
|
14
|
+
else
|
15
|
+
@consumer_secret = options[:consumer].respond_to?(:secret) ? options[:consumer].secret : options[:consumer]
|
16
|
+
@token_secret = options[:token].respond_to?(:secret) ? options[:token].secret : (options[:token] || '')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module RequestProxy
|
23
|
+
class Base
|
24
|
+
def inspect
|
25
|
+
"#<OAuth::RequestProxy::MerbRequest:#{object_id}\n\tconsumer_key: #{consumer_key}\n\ttoken: #{token}\n\tparameters: #{parameters.inspect}\n>"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module SimpleMapper
|
32
|
+
module HttpOAuthExtension
|
33
|
+
def requires_oauth(consumer_key, consumer_secret, options={})
|
34
|
+
@consumer_key = consumer_key
|
35
|
+
@consumer_secret = consumer_secret
|
36
|
+
@oauth_options = options
|
37
|
+
|
38
|
+
# Ingeniousity here... ;)
|
39
|
+
# Duplicates the class to give it a temporary session-attached oauth scope, sets oauth to the Model-Controller-OAuth class,
|
40
|
+
# then makes the class use the original class for all of its instantiation.
|
41
|
+
# NOTE: This only really makes the class methods use OAuth. Object methods, like associations, won't play the trick as well.
|
42
|
+
def self.with_oauth(controller)
|
43
|
+
self.element_name = self.element_name
|
44
|
+
self.collection_name = self.collection_name
|
45
|
+
duped = self.dup
|
46
|
+
duped.set_oauth(controller)
|
47
|
+
yield if block_given?
|
48
|
+
duped
|
49
|
+
end
|
50
|
+
|
51
|
+
def oauth
|
52
|
+
@oauth
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_oauth(controller)
|
56
|
+
@oauth = OAuthController.new(controller, self, @consumer_key, @consumer_secret, @oauth_options)
|
57
|
+
connection.add_callback('initialize_request') do |request|
|
58
|
+
@oauth.authenticate! if !@oauth.authorized? && @oauth.scriptable?
|
59
|
+
raise RuntimeError, "Must authorize OAuth before attempting to get data from the provider." unless @oauth.authorized?
|
60
|
+
@oauth.request_signed!(request)
|
61
|
+
end
|
62
|
+
@oauth
|
63
|
+
end
|
64
|
+
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# We'll have an instance of these for each controller-model pair.
|
71
|
+
class OAuthController
|
72
|
+
DEFAULT_OPTIONS = {
|
73
|
+
# Signature method used by server. Defaults to HMAC-SHA1
|
74
|
+
:signature_method=>'HMAC-SHA1',
|
75
|
+
|
76
|
+
# default paths on site. These are the same as the defaults set up by the generators
|
77
|
+
:request_token_path=>'/oauth/request_token',
|
78
|
+
:authorize_path=>'/oauth/authorize',
|
79
|
+
:access_token_path=>'/oauth/access_token',
|
80
|
+
|
81
|
+
# How do we send the oauth values to the server see
|
82
|
+
# http://oauth.googlecode.com/svn/spec/branches/1.0/drafts/6/spec.html#consumer_req_param for more info
|
83
|
+
#
|
84
|
+
# Possible values:
|
85
|
+
#
|
86
|
+
# :authorize - via the Authorize header (Default) ( option 1. in spec)
|
87
|
+
# :post - url form encoded in body of POST request ( option 2. in spec)
|
88
|
+
# :query - via the query part of the url ( option 3. in spec)
|
89
|
+
:auth_method=>:authorize,
|
90
|
+
|
91
|
+
# Default http method used for OAuth Token Requests (defaults to :post)
|
92
|
+
:http_method=>:post,
|
93
|
+
|
94
|
+
:version=>"1.0",
|
95
|
+
|
96
|
+
# Default authorization method: have the controller redirect to the authorize_url.
|
97
|
+
:authorization_method => lambda {|model| redirect(model.oauth.consumer.authorize_url)},
|
98
|
+
|
99
|
+
# Default session: grab session from the controller's session method -- session['Person_oauth'] for the Person ActiveResource model.
|
100
|
+
:session => lambda {|model| session[model.name.to_s + '_oauth'] ||= {} }
|
101
|
+
}
|
102
|
+
attr_accessor :options, :consumer
|
103
|
+
|
104
|
+
def initialize(controller, model, consumer_key, consumer_secret, options={})
|
105
|
+
@controller = controller
|
106
|
+
@model = model
|
107
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
108
|
+
@model = @options.delete(:model)
|
109
|
+
@consumer = OAuth::Consumer.new(consumer_key, consumer_secret, options)
|
110
|
+
end
|
111
|
+
|
112
|
+
def authorized?
|
113
|
+
!!session[:access_token]
|
114
|
+
end
|
115
|
+
|
116
|
+
def scriptable?
|
117
|
+
@options[:authorization_method] == :scriptable
|
118
|
+
end
|
119
|
+
|
120
|
+
# The session is what holds which models are authenticated with what tokens.
|
121
|
+
# We just need the controller to retreive the session and to send back redirects when necessary.
|
122
|
+
def authenticate!
|
123
|
+
# 1) If we have no tokens, get a request_token and run the authorization method.
|
124
|
+
# 2) If we have a request_token, assume the user has already answered the question, go ahead and try to get an access_token.
|
125
|
+
if access_token
|
126
|
+
return true
|
127
|
+
elsif request_token
|
128
|
+
return @controller.begin_pathway(@options[:authorization_method].in_context(controller).call(@model)) if @options[:authorization_method].is_a?(Proc)
|
129
|
+
return true if access_token # For scriptables
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def request_signed!(request)
|
134
|
+
@consumer.sign!(request, current_token)
|
135
|
+
request
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
# If none exist, go ahead and get one.
|
140
|
+
def request_token
|
141
|
+
session[:request_token] || begin
|
142
|
+
token = @consumer.get_request_token
|
143
|
+
session[:request_token] = token if token.token && token.secret
|
144
|
+
session[:request_token]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# If none exist but request_token exists, go ahead and request one.
|
149
|
+
def access_token
|
150
|
+
return nil if session[:request_token].nil?
|
151
|
+
session[:access_token] || begin
|
152
|
+
token = session[:request_token].get_access_token
|
153
|
+
session[:access_token] = token if token.token && token.secret
|
154
|
+
session[:access_token]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def current_token
|
159
|
+
access_token || request_token
|
160
|
+
end
|
161
|
+
|
162
|
+
def session
|
163
|
+
@session || begin
|
164
|
+
@session = @options[:session].in_context(@controller).call
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SimpleMapper
|
2
|
+
module SimpleModel
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_hash(options={})
|
8
|
+
self.class.properties.inject({}) {|h,k| h[k] = instance_variable_get("@#{k}"); h}
|
9
|
+
end
|
10
|
+
|
11
|
+
def identifier
|
12
|
+
instance_variable_get('@'+self.class.identifier)
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(method, *args)
|
16
|
+
if self.class.properties.include?(method.to_s)
|
17
|
+
instance_variable_get('@'+method.to_s)
|
18
|
+
elsif method.to_s =~ /=$/ && self.class.properties.include?(method.to_s.gsub(/=/, ''))
|
19
|
+
instance_variable_set('@'+method.to_s.gsub(/=/, ''), *args)
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
def properties(*properties)
|
27
|
+
@properties = properties.collect {|e| e.to_s} if properties.length > 0
|
28
|
+
@properties
|
29
|
+
end
|
30
|
+
def identifier(id=nil)
|
31
|
+
@identifier = id.to_s unless id.nil?
|
32
|
+
@identifier
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'simple_mapper/support/bliss_serializer'
|
2
|
+
|
3
|
+
module SimpleMapper
|
4
|
+
module XmlFormat
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.mime_type_headers
|
10
|
+
{'Accept' => 'application/xml', 'Content-type' => 'application/xml'}
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_xml
|
14
|
+
Serialize.object_to_xml(self, :key_name => 'self').to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# This assumes a standard xml format:
|
19
|
+
# <person attribute="">
|
20
|
+
# <another_att>value</another_att>
|
21
|
+
# </person>
|
22
|
+
# And for a collection of objects:
|
23
|
+
# <people>
|
24
|
+
# <person attribute="">
|
25
|
+
# <another_att>value 1</another_att>
|
26
|
+
# </person>
|
27
|
+
# <person attribute="">
|
28
|
+
# <another_att>value 2</another_att>
|
29
|
+
# </person>
|
30
|
+
# </people>
|
31
|
+
def from_xml(xml)
|
32
|
+
doc = Serialize.hash_from_xml(xml)
|
33
|
+
# doc could include a single 'model' element, or a 'models' wrapper around several.
|
34
|
+
puts "Top-level XML key(s): #{doc.keys.inspect}" if @debug
|
35
|
+
key = doc.keys.first
|
36
|
+
if doc[key].keys.uniq == [key.singularize] && doc[key][key.singularize].is_a?(Array)
|
37
|
+
puts "Several objects returned under key '#{key}'/'#{key.singularize}':" if @debug
|
38
|
+
doc[key][key.singularize].collect do |e|
|
39
|
+
puts "Obj: #{e.inspect}" if @debug
|
40
|
+
Object.module_eval("::#{key.singularize.camelize}", __FILE__, __LINE__).new(e)
|
41
|
+
end
|
42
|
+
else # top-level must be single object
|
43
|
+
Object.module_eval("::#{key.singularize.camelize}", __FILE__, __LINE__).new(Serialize.hash_from_xml(xml)[self.name.underscore])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'rexml/light/node'
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
# This is a slighly modified version of the XMLUtilityNode from
|
5
|
+
# http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com)
|
6
|
+
# It's mainly just adding vowels, as I ht cd wth n vwls :)
|
7
|
+
# This represents the hard part of the work, all I did was change the underlying
|
8
|
+
# parser
|
9
|
+
class REXMLUtilityNode # :nodoc:
|
10
|
+
attr_accessor :name, :attributes, :children
|
11
|
+
|
12
|
+
def initialize(name, attributes = {})
|
13
|
+
@name = name.tr("-", "_")
|
14
|
+
@attributes = undasherize_keys(attributes)
|
15
|
+
@children = []
|
16
|
+
@text = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_node(node)
|
20
|
+
@text = true if node.is_a? String
|
21
|
+
@children << node
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_hash
|
25
|
+
if @text
|
26
|
+
return { name => typecast_value( translate_xml_entities( inner_html ) ) }
|
27
|
+
else
|
28
|
+
#change repeating groups into an array
|
29
|
+
# group by the first key of each element of the array to find repeating groups
|
30
|
+
groups = @children.group_by{ |c| c.name }
|
31
|
+
|
32
|
+
hash = {}
|
33
|
+
groups.each do |key, values|
|
34
|
+
if values.size == 1
|
35
|
+
hash.merge! values.first
|
36
|
+
else
|
37
|
+
hash.merge! key => values.map { |element| element.to_hash[key] }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# merge the arrays, including attributes
|
42
|
+
hash.merge! attributes unless attributes.empty?
|
43
|
+
|
44
|
+
{ name => hash }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def typecast_value(value)
|
49
|
+
return value unless attributes["type"]
|
50
|
+
|
51
|
+
case attributes["type"]
|
52
|
+
when "integer" then value.to_i
|
53
|
+
when "boolean" then value.strip == "true"
|
54
|
+
when "datetime" then ::Time.parse(value).utc
|
55
|
+
when "date" then ::Date.parse(value)
|
56
|
+
else value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def translate_xml_entities(value)
|
61
|
+
value.gsub(/</, "<").
|
62
|
+
gsub(/>/, ">").
|
63
|
+
gsub(/"/, '"').
|
64
|
+
gsub(/'/, "'").
|
65
|
+
gsub(/&/, "&")
|
66
|
+
end
|
67
|
+
|
68
|
+
def undasherize_keys(params)
|
69
|
+
params.keys.each do |key, vvalue|
|
70
|
+
params[key.tr("-", "_")] = params.delete(key)
|
71
|
+
end
|
72
|
+
params
|
73
|
+
end
|
74
|
+
|
75
|
+
def inner_html
|
76
|
+
@children.join
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_html
|
80
|
+
"<#{name}#{attributes.to_xml_attributes}>#{inner_html}</#{name}>"
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_s
|
84
|
+
to_html
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
module Serialize
|
89
|
+
XML_OPTIONS = {
|
90
|
+
:include_key => :attribute, # Can be false, :element, or :attribute
|
91
|
+
:report_nil => true, # Sets an attribute nil="true" on elements that are nil, so that the reader doesn't read as an empty string
|
92
|
+
:key_name => 'id', # Default key name
|
93
|
+
}
|
94
|
+
def self.object_to_xml(obj, options={})
|
95
|
+
# Automatically set the key_name for DataMapper objects
|
96
|
+
options.merge!(:key_name => obj.class.table.key.name) if obj.class.respond_to?(:table) && obj.class.respond_to?(:persistent?) && obj.class.persistent?
|
97
|
+
# Should do the above also for ActiveRecord...
|
98
|
+
to_xml(obj.class.name.underscore, obj.to_hash(options), options)
|
99
|
+
end
|
100
|
+
def self.to_xml(root, attributes={}, options={})
|
101
|
+
options = XML_OPTIONS.merge(options)
|
102
|
+
attributes = attributes.dup.stringify_keys!
|
103
|
+
|
104
|
+
doc = REXML::Document.new
|
105
|
+
root_element = doc.add_element(root)
|
106
|
+
|
107
|
+
case options[:include_key]
|
108
|
+
when :attribute
|
109
|
+
root_element.add_attribute(options[:key_name], attributes.delete(options[:key_name].to_s).to_s).extended do
|
110
|
+
def self.to_string; %Q[#@expanded_name="#{to_s().gsub(/"/, '"')}"] end
|
111
|
+
end
|
112
|
+
when :element
|
113
|
+
root_element.add_element(options[:key_name]) << REXML::Text.new(attributes.delete(options[:key_name].to_s).to_s)
|
114
|
+
end
|
115
|
+
|
116
|
+
attributes.each do |key,value|
|
117
|
+
node = root_element.add_element(key)
|
118
|
+
node << REXML::Text.new(value.to_s) unless value.nil?
|
119
|
+
node.add_attribute('nil', 'true') if value.nil? && options[:report_nil]
|
120
|
+
end
|
121
|
+
|
122
|
+
doc
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.hash_from_xml(xml,options={})
|
126
|
+
options = XML_OPTIONS.merge(options)
|
127
|
+
|
128
|
+
stack = []
|
129
|
+
parser = REXML::Parsers::BaseParser.new(xml)
|
130
|
+
|
131
|
+
while true
|
132
|
+
event = parser.pull
|
133
|
+
case event[0]
|
134
|
+
when :end_document
|
135
|
+
break
|
136
|
+
when :end_doctype, :start_doctype
|
137
|
+
# do nothing
|
138
|
+
when :start_element
|
139
|
+
stack.push REXMLUtilityNode.new(event[1], event[2])
|
140
|
+
when :end_element
|
141
|
+
if stack.size > 1
|
142
|
+
temp = stack.pop
|
143
|
+
stack.last.add_node(temp)
|
144
|
+
end
|
145
|
+
when :text, :cdata
|
146
|
+
stack.last.add_node(event[1]) unless event[1].strip.length == 0
|
147
|
+
end
|
148
|
+
end
|
149
|
+
data = stack.pop.to_hash
|
150
|
+
|
151
|
+
# Turn any {"nil" => "true"} into just nil
|
152
|
+
data.crawl {|h,k,v| h[k] = nil if v == {}; v} unless options[:report_nil]
|
153
|
+
data.crawl {|h,k,v| h[k] = nil if v == {} || v == {'nil' => 'true'}; v} if options[:report_nil]
|
154
|
+
data
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
module Merb
|
159
|
+
class Request
|
160
|
+
def xml_params
|
161
|
+
@xml_params ||= begin
|
162
|
+
if Merb::Const::XML_MIME_TYPE_REGEXP.match(content_type)
|
163
|
+
Serialize.hash_from_xml(raw_post) rescue Mash.new
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|