simplemapper 0.0.1
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.
- 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
|