simplemapper 0.0.6 → 0.1.2
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/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.
|