simplemapper 0.0.6 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -1
- data/lib/simple_mapper/adapters/http_adapter.rb +25 -10
- data/lib/simple_mapper/default_plugins/callbacks.rb +1 -1
- data/lib/simple_mapper/default_plugins/oauth.rb +4 -3
- data/lib/simple_mapper/default_plugins/options_to_query.rb +1 -0
- data/lib/simple_mapper/formats/json_format.rb +26 -10
- data/lib/simple_mapper/formats/xml_format.rb +2 -1
- data/lib/simple_mapper/persistence.rb +45 -3
- data/lib/simple_mapper/support/bliss_serializer.rb +5 -15
- metadata +24 -5
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'rake/gempackagetask'
|
|
6
6
|
require 'rake/contrib/rubyforgepublisher'
|
7
7
|
|
8
8
|
PKG_NAME = 'simplemapper'
|
9
|
-
PKG_VERSION = "0.
|
9
|
+
PKG_VERSION = "0.1.2"
|
10
10
|
|
11
11
|
PKG_FILES = FileList[
|
12
12
|
"lib/**/*", "rspec/**/*", "[A-Z]*", "Rakefile", "doc/**/*"
|
@@ -57,6 +57,8 @@ spec = Gem::Specification.new do |s|
|
|
57
57
|
s.email = "gems@behindlogic.com"
|
58
58
|
s.homepage = "http://simplemapper.rubyforge.org"
|
59
59
|
s.rubyforge_project = 'simplemapper'
|
60
|
+
s.add_dependency 'hash_magic'
|
61
|
+
s.add_dependency 'formattedstring'
|
60
62
|
end
|
61
63
|
|
62
64
|
Rake::GemPackageTask.new(spec) do |pkg|
|
@@ -35,30 +35,41 @@ module SimpleMapper
|
|
35
35
|
raise TypeError, "options must be a hash!" unless options.is_a?(Hash)
|
36
36
|
@finder_options = options
|
37
37
|
end
|
38
|
+
alias :set_finder_options :finder_options=
|
39
|
+
def display_options
|
40
|
+
@display_options ||= {}
|
41
|
+
end
|
42
|
+
def display_options=(options)
|
43
|
+
raise TypeError, "options must be a hash!" unless options.is_a?(Hash)
|
44
|
+
@display_options = options
|
45
|
+
end
|
46
|
+
alias :set_display_options :display_options=
|
38
47
|
|
39
|
-
def get(
|
40
|
-
|
41
|
-
|
48
|
+
def get(options={})
|
49
|
+
raw_get(base_uri.path + query_string_from_options(finder_options.merge(display_options.merge(options))))
|
50
|
+
end
|
51
|
+
|
52
|
+
def raw_get(url)
|
42
53
|
begin
|
43
|
-
http.request(request('get',
|
54
|
+
http.request(request('get', url)).body
|
44
55
|
rescue => e
|
45
56
|
raise e if !!raise_http_errors
|
46
57
|
nil
|
47
58
|
end
|
48
59
|
end
|
49
60
|
|
50
|
-
def put(identifier,data)
|
61
|
+
def put(identifier,data,options={})
|
51
62
|
begin
|
52
|
-
http.request(request('put', URI.parse(identifier).path, data)).body
|
63
|
+
http.request(request('put', URI.parse(identifier).path + query_string_from_options(display_options.merge(options)), data)).body
|
53
64
|
rescue => e
|
54
65
|
raise e if !!raise_http_errors
|
55
66
|
nil
|
56
67
|
end
|
57
68
|
end
|
58
69
|
|
59
|
-
def post(data)
|
70
|
+
def post(data,options={})
|
60
71
|
begin
|
61
|
-
http.request(request('post', base_uri.path, data)).body
|
72
|
+
http.request(request('post', base_uri.path + query_string_from_options(display_options.merge(options)), data)).body
|
62
73
|
rescue => e
|
63
74
|
raise e if !!raise_http_errors
|
64
75
|
nil
|
@@ -66,9 +77,9 @@ module SimpleMapper
|
|
66
77
|
end
|
67
78
|
|
68
79
|
# In the http adapter, the identifier is a url.
|
69
|
-
def delete(identifier)
|
80
|
+
def delete(identifier,options={})
|
70
81
|
begin
|
71
|
-
http.request(request('delete', URI.parse(identifier).path)).body
|
82
|
+
http.request(request('delete', URI.parse(identifier).path + query_string_from_options(options))).body
|
72
83
|
rescue => e
|
73
84
|
raise e if !!raise_http_errors
|
74
85
|
nil
|
@@ -76,6 +87,10 @@ module SimpleMapper
|
|
76
87
|
end
|
77
88
|
|
78
89
|
private
|
90
|
+
def query_string_from_options(options={})
|
91
|
+
options.empty? ? '' : ('?' + options.to_query)
|
92
|
+
end
|
93
|
+
|
79
94
|
def http(refresh=false)
|
80
95
|
@http = Net::HTTP.new(base_uri.host, base_uri.port) if @http.nil? || refresh
|
81
96
|
@http
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'rubygems'
|
2
2
|
require 'oauth'
|
3
3
|
require 'oauth/consumer'
|
4
4
|
require 'oauth/client/net_http'
|
@@ -106,9 +106,8 @@ class OAuthController
|
|
106
106
|
|
107
107
|
def initialize(controller, model, consumer_key, consumer_secret, options={})
|
108
108
|
@controller = controller
|
109
|
-
@model = model
|
110
109
|
@options = DEFAULT_OPTIONS.merge(options)
|
111
|
-
@model = @options.delete(:model)
|
110
|
+
@model = @options.delete(:model) || model
|
112
111
|
@consumer = OAuth::Consumer.new(consumer_key, consumer_secret, options)
|
113
112
|
end
|
114
113
|
|
@@ -130,6 +129,8 @@ class OAuthController
|
|
130
129
|
elsif request_token
|
131
130
|
return @controller.begin_pathway(@options[:authorization_method].in_context(controller).call(@model)) if @options[:authorization_method].is_a?(Proc)
|
132
131
|
return true if access_token # For scriptables
|
132
|
+
else
|
133
|
+
raise RuntimeError, "It seems there is a problem between your OAuth client and the OAuth provider you are contacting. Inspect the naming of the token and token secret parameters being sent by the website."
|
133
134
|
end
|
134
135
|
end
|
135
136
|
|
@@ -19,6 +19,10 @@ module SimpleMapper
|
|
19
19
|
json
|
20
20
|
end
|
21
21
|
|
22
|
+
def meta
|
23
|
+
@meta
|
24
|
+
end
|
25
|
+
|
22
26
|
module ClassMethods
|
23
27
|
# This assumes a standard json format:
|
24
28
|
# {'person':{'attribute':'','another_att':'value'}}
|
@@ -27,20 +31,32 @@ module SimpleMapper
|
|
27
31
|
def from_json(json)
|
28
32
|
doc = Serialize.hash_from_json(json)
|
29
33
|
# doc could include a single 'model' element, or a 'models' wrapper around several.
|
30
|
-
puts "Top-level JSON key(s): #{doc.keys.inspect}" if @debug
|
31
34
|
if doc.is_a?(Hash)
|
32
|
-
key
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
# In contrast to XML, JSON is not restricted to one top-level key. We will assume the objects are in a hash/array
|
36
|
+
# referenced by either singular or plural of the klass.
|
37
|
+
# puts "Received #{doc.length} bytes of json. Top keys: #{doc.keys.join(', ')}. Looking for '#{self.entity_name.underscore}' or '#{self.entity_name.pluralize.underscore}'"
|
38
|
+
meta = doc.dup
|
39
|
+
key = if doc[self.entity_name.underscore]
|
40
|
+
self.entity_name.underscore
|
41
|
+
elsif doc[self.entity_name.pluralize.underscore]
|
42
|
+
self.entity_name.pluralize.underscore
|
43
|
+
end
|
44
|
+
return nil if key.nil?
|
45
|
+
# puts "JSON has #{key}"
|
46
|
+
meta.delete(key) # removing the data leaves us only the meta information
|
47
|
+
meta.freeze
|
48
|
+
ret = if doc[key].is_a?(Array)
|
49
|
+
doc[key].collect do |e|
|
50
|
+
obj = self.load(e)
|
51
|
+
obj.instance_variable_set(:@meta, meta)
|
52
|
+
obj
|
38
53
|
end
|
39
|
-
elsif doc[key] # top-level must be single object
|
40
|
-
Object.module_eval("::#{key.singularize.camelize}", __FILE__, __LINE__).load(doc[self.name.underscore])
|
41
54
|
else
|
42
|
-
|
55
|
+
obj = self.load(doc[key])
|
56
|
+
obj.instance_variable_set(:@meta, meta)
|
57
|
+
obj
|
43
58
|
end
|
59
|
+
# puts "Collected: #{(ret.is_a?(Array) ? ret : [ret]).length} objects"; ret
|
44
60
|
else # doc isn't a hash, probably nil
|
45
61
|
doc
|
46
62
|
end
|
@@ -38,6 +38,7 @@ module SimpleMapper
|
|
38
38
|
# doc could include a single 'model' element, or a 'models' wrapper around several.
|
39
39
|
puts "Top-level XML key(s): #{doc.keys.inspect}" if @debug
|
40
40
|
if doc.is_a?(Hash)
|
41
|
+
# By specification the doc should have only ONE top-level element (key)
|
41
42
|
key = doc.keys.first
|
42
43
|
if doc[key] && doc[key].keys.uniq == [key.singularize] && doc[key][key.singularize].is_a?(Array)
|
43
44
|
puts "Several objects returned under key '#{key}'/'#{key.singularize}':" if @debug
|
@@ -46,7 +47,7 @@ module SimpleMapper
|
|
46
47
|
Object.module_eval("::#{key.singularize.camelize}", __FILE__, __LINE__).load(e)
|
47
48
|
end
|
48
49
|
elsif doc[key] # top-level must be single object
|
49
|
-
Object.module_eval("::#{key.singularize.camelize}", __FILE__, __LINE__).load(doc[
|
50
|
+
Object.module_eval("::#{key.singularize.camelize}", __FILE__, __LINE__).load(doc[key])
|
50
51
|
else
|
51
52
|
nil
|
52
53
|
end
|
@@ -53,6 +53,12 @@ module SimpleMapper
|
|
53
53
|
alias :set_format :format=
|
54
54
|
attr_reader :format_name
|
55
55
|
|
56
|
+
def entity_name
|
57
|
+
@entity_name ||= name
|
58
|
+
end
|
59
|
+
attr_writer :entity_name
|
60
|
+
alias :set_entity_name :entity_name=
|
61
|
+
|
56
62
|
def connections
|
57
63
|
@connections ||= {}
|
58
64
|
end
|
@@ -70,10 +76,42 @@ module SimpleMapper
|
|
70
76
|
end
|
71
77
|
|
72
78
|
# get
|
79
|
+
# Works with pagination, provided the last object in the returned array responds to .meta and that meta information
|
80
|
+
# includes a total and a url for the next page of results.
|
73
81
|
def get(*args)
|
74
82
|
adapter = adapter_from_args(*args)
|
75
83
|
objs = extract_from(connection(adapter || :default).get(*args))
|
76
|
-
objs
|
84
|
+
# puts "#{objs ? objs.length : 0} objects."
|
85
|
+
# puts "Like #{objs[0].inspect}" if objs
|
86
|
+
if objs.is_a?(Array)
|
87
|
+
safe = 1
|
88
|
+
while(objs[-1].respond_to?(:meta) && objs[-1].meta['total'] > objs.length && objs[-1].meta['next'])
|
89
|
+
safe += 1
|
90
|
+
objs.concat(extract_from(connection(adapter || :default).raw_get(objs[-1].meta['next'])))
|
91
|
+
break if safe >= 50 # Safeguard: if we do 50 requests we're probably on a runaway. Paginating at 50/page would be 2500 records...
|
92
|
+
end
|
93
|
+
objs.each {|e| e.instance_variable_set(:@adapter, adapter)} if adapter
|
94
|
+
else
|
95
|
+
objs.instance_variable_set(:@adapter, adapter) if adapter
|
96
|
+
end
|
97
|
+
# puts "TOTAL #{objs ? objs.length : 0} objects in all."
|
98
|
+
# puts "Like #{objs[-1].inspect}" if objs
|
99
|
+
objs
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_from_url(url, adapter=:default)
|
103
|
+
objs = extract_from(connection(adapter).raw_get(url))
|
104
|
+
if objs.is_a?(Array)
|
105
|
+
safe = 1
|
106
|
+
while(objs[-1].respond_to?(:meta) && objs[-1].meta['total'] > objs.length && objs[-1].meta['next'])
|
107
|
+
safe += 1
|
108
|
+
objs.concat(extract_from(connection(adapter || :default).raw_get(objs[-1].meta['next'])))
|
109
|
+
break if safe >= 50 # Safeguard: if we do 50 requests we're probably on a runaway. Paginating at 50/page would be 2500 records...
|
110
|
+
end
|
111
|
+
objs.each {|e| e.instance_variable_set(:@adapter, adapter)} if adapter
|
112
|
+
else
|
113
|
+
objs.instance_variable_set(:@adapter, adapter) if adapter
|
114
|
+
end
|
77
115
|
objs
|
78
116
|
end
|
79
117
|
|
@@ -144,13 +182,17 @@ module SimpleMapper
|
|
144
182
|
|
145
183
|
# sends a put request with self.data
|
146
184
|
def put(*args)
|
147
|
-
|
185
|
+
new_rec = self.class.extract_one(self.class.connection(@adapter || :default).put(identifier, formatted_data, *args), identifier)
|
186
|
+
raise "Request did not return an object" if new_rec.nil?
|
187
|
+
self.data = new_rec.to_hash
|
148
188
|
self
|
149
189
|
end
|
150
190
|
|
151
191
|
# sends a post request with self.data
|
152
192
|
def post(*args)
|
153
|
-
|
193
|
+
new_rec = self.class.extract_one(self.class.connection(@adapter || :default).post(formatted_data, *args))
|
194
|
+
raise "Request did not return an object" if new_rec.nil?
|
195
|
+
self.data = new_rec.to_hash
|
154
196
|
@persisted = true
|
155
197
|
self
|
156
198
|
end
|
@@ -15,9 +15,6 @@ module Serialize
|
|
15
15
|
:default_root => 'xml',
|
16
16
|
}
|
17
17
|
def self.object_to_xml(obj, options={})
|
18
|
-
# Automatically set the key_name for DataMapper objects
|
19
|
-
options.merge!(:key_name => obj.class.table.key.name) if obj.class.respond_to?(:table) && obj.class.respond_to?(:persistent?) && obj.class.persistent?
|
20
|
-
# Should do the above also for ActiveRecord...
|
21
18
|
options = options.reverse_merge!(obj.class.xml_options) if obj.class.respond_to?(:xml_options)
|
22
19
|
options = options.reverse_merge!(obj.xml_options) if obj.respond_to?(:xml_options)
|
23
20
|
to_xml(obj.to_hash(options), {:root => Inflector.underscore(obj.class.name)}.merge(options))
|
@@ -25,7 +22,6 @@ module Serialize
|
|
25
22
|
def self.to_xml(attributes={}, options={})
|
26
23
|
options = XML_OPTIONS.merge(options)
|
27
24
|
root = options[:root]
|
28
|
-
options[:exclude] = [options[:exclude]].flatten.compact.collect {|e| e.to_s}
|
29
25
|
attributes.reject! {|k,v| options[:exclude].include?(k)}
|
30
26
|
|
31
27
|
doc = REXML::Document.new
|
@@ -68,21 +64,16 @@ module Serialize
|
|
68
64
|
:class_columns => :visible_properties,
|
69
65
|
}
|
70
66
|
def self.object_to_json(obj, options={})
|
71
|
-
# Automatically set the key_name for DataMapper objects
|
72
|
-
options.merge!(:key_name => obj.class.table.key.name) if obj.class.respond_to?(:table) && obj.class.respond_to?(:persistent?) && obj.class.persistent?
|
73
|
-
# Should do the above also for ActiveRecord...
|
74
67
|
options = options.reverse_merge!(obj.class.json_options) if obj.class.respond_to?(:json_options)
|
75
68
|
options = options.reverse_merge!(obj.json_options) if obj.respond_to?(:json_options)
|
76
|
-
to_json(obj.to_hash(options),
|
69
|
+
to_json(obj.to_hash(options), options)
|
77
70
|
end
|
78
71
|
def self.to_json(attributes={}, options={})
|
79
|
-
|
80
|
-
# start-index=26 || offset=26
|
81
|
-
raise NotImplemented
|
72
|
+
attributes = attributes.to_hash if attributes.respond_to?(:to_hash)
|
82
73
|
options = JSON_OPTIONS.merge(options)
|
83
|
-
root = options[:root]
|
84
74
|
options[:exclude] = [options[:exclude]].flatten.compact.collect {|e| e.to_s}
|
85
75
|
attributes.reject! {|k,v| options[:exclude].include?(k)}
|
76
|
+
options[:root] ? JSON.unparse({options[:root] => attributes}) : JSON.unparse(attributes)
|
86
77
|
end
|
87
78
|
def self.hash_from_json(json,options={})
|
88
79
|
json.to_s.formatted(:json).to_hash
|
@@ -91,9 +82,8 @@ end
|
|
91
82
|
|
92
83
|
class Hash
|
93
84
|
def to_xml(options={})
|
94
|
-
options[:root] = keys
|
95
|
-
options
|
96
|
-
Serialize.to_xml(self.slashed, options)
|
85
|
+
options[:root] = keys.length == 1 ? keys[0] : nil if !options.has_key?(:root)
|
86
|
+
Serialize.to_xml(self.slashed, options.merge(:root => (options.has_key?(:root) ? options[:root] : 'xml')))
|
97
87
|
end
|
98
88
|
end
|
99
89
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simplemapper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Parker
|
@@ -9,10 +9,29 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-06-01 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hash_magic
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: formattedstring
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
16
35
|
description: The lightest of ORMs excluding magic such as properties, primary keys, associations, validations.
|
17
36
|
email: gems@behindlogic.com
|
18
37
|
executables: []
|
@@ -70,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
89
|
requirements: []
|
71
90
|
|
72
91
|
rubyforge_project: simplemapper
|
73
|
-
rubygems_version: 1.
|
92
|
+
rubygems_version: 1.3.1
|
74
93
|
signing_key:
|
75
94
|
specification_version: 2
|
76
95
|
summary: The lightest of ORMs excluding magic such as properties, primary keys, associations, validations.
|