rosemary 0.3.4 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +19 -8
- data/Rakefile +6 -0
- data/lib/hash.rb +3 -0
- data/lib/rosemary.rb +2 -0
- data/lib/rosemary/api.rb +61 -16
- data/lib/rosemary/basic_auth_client.rb +11 -2
- data/lib/rosemary/changeset.rb +17 -12
- data/lib/rosemary/client.rb +3 -0
- data/lib/rosemary/element.rb +13 -2
- data/lib/rosemary/errors.rb +7 -1
- data/lib/rosemary/node.rb +1 -0
- data/lib/rosemary/oauth_client.rb +42 -23
- data/lib/rosemary/parser.rb +13 -1
- data/lib/rosemary/permissions.rb +19 -0
- data/lib/rosemary/tags.rb +1 -1
- data/lib/rosemary/user.rb +1 -0
- data/lib/rosemary/version.rb +2 -1
- data/lib/rosemary/way.rb +5 -1
- data/rosemary.gemspec +6 -0
- data/spec/models/node_spec.rb +5 -0
- data/spec/models/parser_spec.rb +31 -0
- metadata +95 -20
data/README.md
CHANGED
@@ -41,26 +41,37 @@ OK, gimme some code:
|
|
41
41
|
node = api.find_node(123)
|
42
42
|
=> #<Rosemary::Node:0x1019268d0 @changeset=7836598, @timestamp=Mon Apr 11 19:40:43 UTC 2011, @user="Turleder'n", @tags={}, @uid=289426, @version=4, @lat=59.9502252, @id=123, @lon=10.7899133>
|
43
43
|
|
44
|
+
## Testing your code
|
45
|
+
|
46
|
+
You should try your code on the OSM testing server first! You can change the url like this:
|
47
|
+
|
48
|
+
require 'rosemary'
|
49
|
+
Rosemary::Api.base_uri 'http://api06.dev.openstreetmap.org/'
|
50
|
+
api = Rosemary::Api.new
|
51
|
+
api.find_node(123)
|
52
|
+
|
44
53
|
Modification of data is supported too. According to the OSM license every modification to the data has to be done by a registered OSM user account. The user can be authenticated with username and password. But see yourself:
|
45
54
|
|
46
55
|
client = Rosemary::BasicAuthClient.new('osm_user_name', 'password')
|
56
|
+
|
47
57
|
api = Rosemary::Api.new(client)
|
58
|
+
changeset = api.create_changeset("Some meaningful comment")
|
48
59
|
node = Rosemary::Node.new(:lat => 52.0, :lon => 13.4)
|
49
|
-
api.save(node)
|
60
|
+
api.save(node, changeset)
|
61
|
+
api.close_changeset(changeset)
|
50
62
|
|
51
|
-
Yeah, i can hear you sayin: 'Seriously, do i have to provide username and password? Is that secure?' Providing username and password is prone to some security issues, especially because the OSM API does not provide an SSL service. But wait, there is some more in store for you: [OAuth](http://oauth.net/) It's much more secure for the user and your OSM app. But it comes with a price: You have to register an application on http://www.openstreetmap.org. After you have your app registered you get an app key and secret. Keep it in a
|
63
|
+
Yeah, i can hear you sayin: 'Seriously, do i have to provide username and password? Is that secure?' Providing username and password is prone to some security issues, especially because the OSM API does not provide an SSL service. But wait, there is some more in store for you: [OAuth](http://oauth.net/) It's much more secure for the user and your OSM app. But it comes with a price: You have to register an application on http://www.openstreetmap.org. After you have your app registered you get an app key and secret. Keep it in a safe place.
|
52
64
|
|
53
65
|
consumer = OAuth::Consumer.new( 'osm_app_key', 'osm_app_secret',
|
54
|
-
|
55
|
-
:request_token_path => '/oauth/request_token',
|
56
|
-
:access_token_path => '/oauth/access_token',
|
57
|
-
:authorize_path => '/oauth/authorize'
|
58
|
-
})
|
66
|
+
:site => 'http://www.openstreetmap.org')
|
59
67
|
access_token = OAuth::AccessToken.new(consumer, 'osm_user_token', 'osm_user_key')
|
60
68
|
client = Rosemary::OauthClient.new(access_token)
|
69
|
+
|
61
70
|
api = Rosemary::Api.new(client)
|
71
|
+
changeset = api.create_changeset("Some meaningful comment")
|
62
72
|
node = Rosemary::Node.new(:lat => 52.0, :lon => 13.4)
|
63
|
-
api.save(node)
|
73
|
+
api.save(node, changeset)
|
74
|
+
api.close_changeset(changeset)
|
64
75
|
|
65
76
|
Every request to the API is now handled by the OauthClient.
|
66
77
|
|
data/Rakefile
CHANGED
data/lib/hash.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# Standard ruby hash class extended with some additional behaviour.
|
1
2
|
class Hash
|
2
3
|
|
4
|
+
# symbolize_keys
|
3
5
|
define_method(:symbolize_keys!) do
|
4
6
|
self.each do |k,v|
|
5
7
|
self[k.to_sym] = v
|
@@ -7,6 +9,7 @@ class Hash
|
|
7
9
|
end
|
8
10
|
end unless method_defined? :symbolize_keys!
|
9
11
|
|
12
|
+
# stringify_keys
|
10
13
|
define_method(:stringify_keys!) do
|
11
14
|
temp_hash = {}
|
12
15
|
self.each do |k,v|
|
data/lib/rosemary.rb
CHANGED
@@ -10,7 +10,9 @@ require 'rosemary/changeset'
|
|
10
10
|
require 'rosemary/relation'
|
11
11
|
require 'rosemary/member'
|
12
12
|
require 'rosemary/user'
|
13
|
+
require 'rosemary/permissions'
|
13
14
|
require 'rosemary/errors'
|
15
|
+
require 'rosemary/client'
|
14
16
|
require 'rosemary/basic_auth_client'
|
15
17
|
require 'rosemary/oauth_client'
|
16
18
|
require 'rosemary/parser'
|
data/lib/rosemary/api.rb
CHANGED
@@ -9,38 +9,51 @@ module Rosemary
|
|
9
9
|
# api = Rosemary::Api.new(auth_client)
|
10
10
|
# @node = api.find_node(1234)
|
11
11
|
# @node.tags << {:wheelchair => 'no'}
|
12
|
-
# api.
|
12
|
+
# @changeset = api.create_changeset('Set the wheelchair tag')
|
13
|
+
# api.save(@node, @changeset)
|
13
14
|
class Api
|
15
|
+
# Provide basic HTTP client behaviour.
|
14
16
|
include HTTParty
|
17
|
+
|
18
|
+
# The OSM API version supported by this gem.
|
15
19
|
API_VERSION = "0.6".freeze
|
16
20
|
|
17
21
|
# the default base URI for the API
|
18
22
|
base_uri "http://www.openstreetmap.org"
|
19
23
|
#base_uri "http://api06.dev.openstreetmap.org/api/#{API_VERSION}"
|
20
24
|
|
25
|
+
# Make sure the request don't run forever
|
21
26
|
default_timeout 5
|
22
27
|
|
28
|
+
# Use a custom parser to handle the OSM XML format.
|
23
29
|
parser Parser
|
24
30
|
|
31
|
+
|
32
|
+
# @return [Rosemary::Client] the client to be used to authenticate the user towards the OSM API.
|
25
33
|
attr_accessor :client
|
26
34
|
|
35
|
+
# @return [Rosemary::Changeset] the current changeset to be used for all writing acces.
|
27
36
|
attr_accessor :changeset
|
28
37
|
|
38
|
+
# Creates an Rosemary::Api object with an optional client
|
39
|
+
# @param [Rosemary::Client] client the client to authenticate the user for write access.
|
29
40
|
def initialize(client=nil)
|
30
41
|
@client = client
|
31
42
|
end
|
32
43
|
|
33
44
|
# Get a Node with specified ID from API.
|
34
45
|
#
|
35
|
-
#
|
36
|
-
#
|
46
|
+
# @param [Fixnum] id the id of the node to find.
|
47
|
+
# @return [Rosemary::Node] the node with the given id.
|
48
|
+
# @raise [Rosemary::Gone] in case the node has been deleted.
|
37
49
|
def find_node(id)
|
38
50
|
find_element('node', id)
|
39
51
|
end
|
40
52
|
|
41
53
|
# Get a Way with specified ID from API.
|
42
54
|
#
|
43
|
-
#
|
55
|
+
# @param [Fixnum] id the id of the node to find.
|
56
|
+
# @return [Rosemary::Way] the way with the given id.
|
44
57
|
#
|
45
58
|
def find_way(id)
|
46
59
|
find_element('way', id)
|
@@ -54,14 +67,6 @@ module Rosemary
|
|
54
67
|
find_element('relation', id)
|
55
68
|
end
|
56
69
|
|
57
|
-
# Get a Changeset with specified ID from API.
|
58
|
-
#
|
59
|
-
# call-seq: find_changeset(id) -> Rosemary::Changeset
|
60
|
-
#
|
61
|
-
def find_changeset(id)
|
62
|
-
find_element('changeset', id)
|
63
|
-
end
|
64
|
-
|
65
70
|
# Get a Changeset with specified ID from API
|
66
71
|
# if that changeset is missing, id is nil, or the changeset is closed
|
67
72
|
# create a new one
|
@@ -79,7 +84,7 @@ module Rosemary
|
|
79
84
|
|
80
85
|
# Get the user which represented by the Rosemary::Client
|
81
86
|
#
|
82
|
-
#
|
87
|
+
# @return: [Rosemary::User] user the user authenticated using the current client
|
83
88
|
#
|
84
89
|
def find_user
|
85
90
|
raise CredentialsMissing if client.nil?
|
@@ -88,15 +93,29 @@ module Rosemary
|
|
88
93
|
resp
|
89
94
|
end
|
90
95
|
|
91
|
-
|
96
|
+
def permissions
|
97
|
+
if client.nil?
|
98
|
+
get("/permissions")
|
99
|
+
else
|
100
|
+
do_authenticated_request(:get, "/permissions")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Deletes the given element using API write access.
|
105
|
+
#
|
106
|
+
# @param [Rosemary::Element] element the element to be created
|
107
|
+
# @param [Rosemary::Changeset] changeset the changeset to be used to wrap the write access.
|
108
|
+
# @return [Fixnum] the new version of the deleted element.
|
92
109
|
def destroy(element, changeset)
|
93
110
|
element.changeset = changeset.id
|
94
111
|
response = delete("/#{element.type.downcase}/#{element.id}", :body => element.to_xml) unless element.id.nil?
|
95
112
|
response.to_i # New version number
|
96
113
|
end
|
97
114
|
|
98
|
-
#
|
99
|
-
#
|
115
|
+
# Creates or updates an element depending on the current state of persistance.
|
116
|
+
#
|
117
|
+
# @param [Rosemary::Element] element the element to be created
|
118
|
+
# @param [Rosemary::Changeset] changeset the changeset to be used to wrap the write access.
|
100
119
|
def save(element, changeset)
|
101
120
|
response = if element.id.nil?
|
102
121
|
create(element, changeset)
|
@@ -105,23 +124,49 @@ module Rosemary
|
|
105
124
|
end
|
106
125
|
end
|
107
126
|
|
127
|
+
# Create a new element using API write access.
|
128
|
+
#
|
129
|
+
# @param [Rosemary::Element] element the element to be created
|
130
|
+
# @param [Rosemary::Changeset] changeset the changeset to be used to wrap the write access.
|
131
|
+
# @return [Fixnum] the id of the newly created element.
|
108
132
|
def create(element, changeset)
|
109
133
|
element.changeset = changeset.id
|
110
134
|
put("/#{element.type.downcase}/create", :body => element.to_xml)
|
111
135
|
end
|
112
136
|
|
137
|
+
# Update an existing element using API write access.
|
138
|
+
#
|
139
|
+
# @param [Rosemary::Element] element the element to be created
|
140
|
+
# @param [Rosemary::Changeset] changeset the changeset to be used to wrap the write access.
|
141
|
+
# @return [Fixnum] the versiom of the updated element.
|
113
142
|
def update(element, changeset)
|
114
143
|
element.changeset = changeset.id
|
115
144
|
response = put("/#{element.type.downcase}/#{element.id}", :body => element.to_xml)
|
116
145
|
response.to_i # New Version number
|
117
146
|
end
|
118
147
|
|
148
|
+
# Create a new changeset with an optional comment
|
149
|
+
#
|
150
|
+
# @param [String] comment a meaningful comment for this changeset
|
151
|
+
# @return [Rosemary::Changeset] the changeset which was newly created
|
152
|
+
# @raise [Rosemary::NotFound] in case the changeset could not be found
|
119
153
|
def create_changeset(comment = nil)
|
120
154
|
changeset = Changeset.new(:tags => { :comment => comment })
|
121
155
|
changeset_id = put("/changeset/create", :body => changeset.to_xml).to_i
|
122
156
|
find_changeset(changeset_id) unless changeset_id == 0
|
123
157
|
end
|
124
158
|
|
159
|
+
# Get a Changeset with specified ID from API.
|
160
|
+
#
|
161
|
+
# @param [Integer] id the ID for the changeset you look for
|
162
|
+
# @return [Rosemary::Changeset] the changeset which was found with the id
|
163
|
+
def find_changeset(id)
|
164
|
+
find_element('changeset', id)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Closes the given changeset.
|
168
|
+
#
|
169
|
+
# @param [Rosemary::Changeset] changeset the changeset to be closed
|
125
170
|
def close_changeset(changeset)
|
126
171
|
put("/changeset/#{changeset.id}/close")
|
127
172
|
end
|
@@ -1,15 +1,24 @@
|
|
1
|
-
class Rosemary::BasicAuthClient
|
2
|
-
|
1
|
+
class Rosemary::BasicAuthClient < Rosemary::Client
|
2
|
+
|
3
|
+
# The username to be used to authenticate the user against the OMS API
|
4
|
+
attr_reader :username
|
5
|
+
|
6
|
+
# The password to be used to authenticate the user against the OMS API
|
7
|
+
attr_reader :password
|
3
8
|
|
4
9
|
def initialize(username, password)
|
5
10
|
@username = username
|
6
11
|
@password = password
|
7
12
|
end
|
8
13
|
|
14
|
+
# The username and password credentials as a Hash
|
15
|
+
# @return [Hash] the credential hash.
|
9
16
|
def credentials
|
10
17
|
{:username => username, :password => password}
|
11
18
|
end
|
12
19
|
|
20
|
+
# Override inspect message to keep the password from showing up
|
21
|
+
# in any logfile.
|
13
22
|
def inspect
|
14
23
|
"#<#{self.class.name}:#{self.object_id} @username='#{username}'>"
|
15
24
|
end
|
data/lib/rosemary/changeset.rb
CHANGED
@@ -3,30 +3,33 @@ module Rosemary
|
|
3
3
|
# Changeset is used in OpenStreetMap to bundle several changes into a kind of "commit"
|
4
4
|
class Changeset
|
5
5
|
# Unique ID
|
6
|
+
# @return [Fixnum]
|
6
7
|
attr_reader :id
|
7
8
|
|
8
|
-
# The user who last edited this object (as read from file, it
|
9
|
-
#
|
9
|
+
# The user who last edited this object (as read from file, it is not updated by operations to this object)
|
10
|
+
# @return [Rosemary::User] the user who last edited this object
|
10
11
|
attr_accessor :user
|
11
12
|
|
12
|
-
# The user id of the user who last edited this object
|
13
|
-
# is not updated by operations to this object)
|
14
|
-
#
|
13
|
+
# The user id of the user who last edited this object(as read from file, it
|
14
|
+
# is not updated by operations to this object) API 0.6 and above only
|
15
|
+
# @return [Fixnum] the user id of the user who last edited this object
|
15
16
|
attr_accessor :uid
|
16
17
|
|
17
|
-
#
|
18
|
+
# @return [Boolean] is this changeset is still open.
|
18
19
|
attr_accessor :open
|
19
20
|
|
20
|
-
#
|
21
|
+
# @return [Date] creation date of this changeset
|
21
22
|
attr_accessor :created_at
|
22
23
|
|
23
|
-
#
|
24
|
+
# @return [Date] when the changeset was closed
|
24
25
|
attr_accessor :closed_at
|
25
26
|
|
26
27
|
# Bounding box surrounding all changes made in this changeset
|
28
|
+
# @return [Float]
|
27
29
|
attr_accessor :min_lat, :min_lon, :max_lat, :max_lon
|
28
30
|
|
29
31
|
# Tags for this object
|
32
|
+
# @return [Hash]
|
30
33
|
attr_reader :tags
|
31
34
|
|
32
35
|
def initialize(attrs = {}) #:nodoc:
|
@@ -39,7 +42,7 @@ module Rosemary
|
|
39
42
|
@open = attrs['open']
|
40
43
|
tags = attrs['tags'] || {}
|
41
44
|
@tags = Tags.new.merge(tags.dup.stringify_keys!)
|
42
|
-
@tags['created_by'] =
|
45
|
+
@tags['created_by'] = "rosemary v#{Rosemary::VERSION}"
|
43
46
|
@min_lat = attrs['min_lat'].to_f
|
44
47
|
@min_lon = attrs['min_lon'].to_f
|
45
48
|
@max_lat = attrs['max_lat'].to_f
|
@@ -58,17 +61,17 @@ module Rosemary
|
|
58
61
|
end
|
59
62
|
|
60
63
|
# List of attributes for a Changeset
|
64
|
+
# @return [Array]
|
61
65
|
def attribute_list
|
62
66
|
[:id, :user, :uid, :open, :created_at, :closed_at, :min_lat, :max_lat, :min_lon, :max_lon]
|
63
67
|
end
|
64
68
|
|
65
|
-
#
|
66
|
-
#
|
69
|
+
# A hash of all non-nil attributes of this object.
|
67
70
|
# Keys of this hash are <tt>:id</tt>, <tt>:user</tt>,
|
68
71
|
# and <tt>:timestamp</tt>. For a Node also <tt>:lon</tt>
|
69
72
|
# and <tt>:lat</tt>.
|
70
73
|
#
|
71
|
-
#
|
74
|
+
# @return [Hash] a hash of all non-nil attributes of this object.
|
72
75
|
#
|
73
76
|
def attributes
|
74
77
|
attrs = Hash.new
|
@@ -79,6 +82,8 @@ module Rosemary
|
|
79
82
|
attrs
|
80
83
|
end
|
81
84
|
|
85
|
+
# Renders the object as an xml representation compatible to the OSM API
|
86
|
+
# @return [String] XML
|
82
87
|
def to_xml(options = {})
|
83
88
|
xml = options[:builder] ||= Builder::XmlMarkup.new
|
84
89
|
xml.instruct! unless options[:skip_instruct]
|
data/lib/rosemary/element.rb
CHANGED
@@ -4,15 +4,18 @@ module Rosemary
|
|
4
4
|
include ActiveModel::Validations
|
5
5
|
|
6
6
|
# Unique ID
|
7
|
+
# @return [Fixnum] id of this element
|
7
8
|
attr_reader :id
|
8
9
|
|
9
10
|
# The version of this object (as read from file, it
|
10
11
|
# is not updated by operations to this object)
|
11
12
|
# API 0.6 and above only
|
13
|
+
# @return [Fixnum] the current version
|
12
14
|
attr_accessor :version
|
13
15
|
|
14
|
-
# The user who last edited this
|
16
|
+
# The user who last edited this element (as read from file, it
|
15
17
|
# is not updated by operations to this object)
|
18
|
+
# @return [Rosemary::User] the user who last edititd this element
|
16
19
|
attr_accessor :user
|
17
20
|
|
18
21
|
# The user id of the user who last edited this object (as read from file, it
|
@@ -22,6 +25,7 @@ module Rosemary
|
|
22
25
|
|
23
26
|
# Last change of this object (as read from file, it is not
|
24
27
|
# updated by operations to this object)
|
28
|
+
# @return [Time] last change of this object.
|
25
29
|
attr_reader :timestamp
|
26
30
|
|
27
31
|
# The changeset the last change of this object was made with.
|
@@ -31,6 +35,8 @@ module Rosemary
|
|
31
35
|
attr_reader :tags
|
32
36
|
|
33
37
|
# Get Rosemary::Element from API
|
38
|
+
# @param [Fixnum] id the id of the element to load from the API
|
39
|
+
|
34
40
|
def self.from_api(id, api=Rosemary::API.new) #:nodoc:
|
35
41
|
raise NotImplementedError.new('Element is a virtual base class for the Node, Way, and Relation classes') if self.class == Rosemary::Element
|
36
42
|
api.get_object(type, id)
|
@@ -56,6 +62,7 @@ module Rosemary
|
|
56
62
|
end
|
57
63
|
|
58
64
|
# Set timestamp for this object.
|
65
|
+
# @param [Time] timestamp the time this object was created
|
59
66
|
def timestamp=(timestamp)
|
60
67
|
@timestamp = _check_timestamp(timestamp)
|
61
68
|
end
|
@@ -132,7 +139,7 @@ module Rosemary
|
|
132
139
|
|
133
140
|
# Has this object any tags?
|
134
141
|
#
|
135
|
-
#
|
142
|
+
# @return [Boolean] has any tags?
|
136
143
|
#
|
137
144
|
def is_tagged?
|
138
145
|
! @tags.empty?
|
@@ -228,6 +235,10 @@ module Rosemary
|
|
228
235
|
@tags = from.tags.dup
|
229
236
|
end
|
230
237
|
|
238
|
+
def self.from_xml(xml)
|
239
|
+
Parser.call(xml, :xml)
|
240
|
+
end
|
241
|
+
|
231
242
|
private
|
232
243
|
|
233
244
|
# Return next free ID
|
data/lib/rosemary/errors.rb
CHANGED
@@ -9,6 +9,9 @@ module Rosemary
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
# This error occurs when the request send to the server could not be parsed.
|
13
|
+
class ParseError < StandardError; end
|
14
|
+
|
12
15
|
# This error occurs when Rosemary is instantiated without a client
|
13
16
|
class CredentialsMissing < StandardError; end
|
14
17
|
|
@@ -22,6 +25,7 @@ module Rosemary
|
|
22
25
|
# password for a write operation.
|
23
26
|
class Unauthorized < Error; end # 401
|
24
27
|
|
28
|
+
# You don't have sufficient permissions to make that request.
|
25
29
|
class Forbidden < Error; end # 403
|
26
30
|
|
27
31
|
# The object was not found (HTTP 404). Generally means that the object doesn't exist
|
@@ -47,8 +51,10 @@ module Rosemary
|
|
47
51
|
# Unspecified API server error.
|
48
52
|
class ServerError < Error; end # 500
|
49
53
|
|
54
|
+
# When the API service times out or returns an HTTP 503 status.
|
50
55
|
class Unavailable < Error; end # 503
|
51
56
|
|
52
|
-
|
57
|
+
# This method is not implemented yet.
|
58
|
+
class NotImplemented < Error; end
|
53
59
|
|
54
60
|
end
|
data/lib/rosemary/node.rb
CHANGED
@@ -1,31 +1,50 @@
|
|
1
|
-
|
2
|
-
class OauthClient
|
1
|
+
class Rosemary::OauthClient < Rosemary::Client
|
3
2
|
|
4
|
-
|
3
|
+
# The access token to be used for all write access
|
4
|
+
# @return [OAuth::AccessToken]
|
5
|
+
attr_reader :access_token
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def get(url, header={})
|
11
|
-
access_token.get(url, {'Content-Type' => 'application/xml' })
|
12
|
-
end
|
7
|
+
# @param [OAuth::AccessToken] access_token the access token to be used for write access.
|
8
|
+
def initialize(access_token)
|
9
|
+
@access_token = access_token
|
10
|
+
end
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
# Execute a signed OAuth GET request.
|
13
|
+
# @param [String] url the url to be requested
|
14
|
+
# @param [Hash] header optional header attributes
|
15
|
+
def get(url, header={})
|
16
|
+
access_token.get(url, {'Content-Type' => 'application/xml' })
|
17
|
+
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
# Execute a signed OAuth PUT request.
|
20
|
+
# @param [String] url the url to be requested
|
21
|
+
# @param [Hash] options optional option attributes
|
22
|
+
# @param [Hash] header optional header attributes
|
23
|
+
def put(url, options={}, header={})
|
24
|
+
body = options[:body]
|
25
|
+
access_token.put(url, body, {'Content-Type' => 'application/xml' })
|
26
|
+
end
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
28
|
+
# Execute a signed OAuth DELETE request.
|
29
|
+
#
|
30
|
+
# Unfortunately the OSM API requires to send an XML
|
31
|
+
# representation of the Element to be delete in the body
|
32
|
+
# of the request. The OAuth library does not support sending
|
33
|
+
# any information in the request body.
|
34
|
+
# If you know a workaround please fork and improve.
|
35
|
+
def delete(url, options={}, header={})
|
36
|
+
raise NotImplemented.new("Delete with Oauth and OSM is not supported")
|
37
|
+
# body = options[:body]
|
38
|
+
# access_token.delete(url, {'Content-Type' => 'application/xml' })
|
39
|
+
end
|
29
40
|
|
41
|
+
# Execute a signed OAuth POST request.
|
42
|
+
# @param [String] url the url to be requested
|
43
|
+
# @param [Hash] options optional option attributes
|
44
|
+
# @param [Hash] header optional header attributes
|
45
|
+
def post(url, options={}, header={})
|
46
|
+
body = options[:body]
|
47
|
+
access_token.post(url, body, {'Content-Type' => 'application/xml' })
|
30
48
|
end
|
49
|
+
|
31
50
|
end
|
data/lib/rosemary/parser.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'httparty'
|
2
2
|
require 'xml/libxml'
|
3
3
|
|
4
|
+
# The XML parser capable of understanding the custom OSM XML format.
|
4
5
|
class Rosemary::Parser < HTTParty::Parser
|
5
6
|
include LibXML::XML::SaxParser::Callbacks
|
6
7
|
|
@@ -53,6 +54,8 @@ class Rosemary::Parser < HTTParty::Parser
|
|
53
54
|
when 'member' then _member(attr_hash)
|
54
55
|
when 'home' then _home(attr_hash)
|
55
56
|
when 'description' then @description = true
|
57
|
+
when 'permissions' then _start_permissions(attr_hash)
|
58
|
+
when 'permission' then _start_permission(attr_hash)
|
56
59
|
when 'lang' then @lang = true
|
57
60
|
end
|
58
61
|
end
|
@@ -77,7 +80,6 @@ class Rosemary::Parser < HTTParty::Parser
|
|
77
80
|
end
|
78
81
|
|
79
82
|
private
|
80
|
-
|
81
83
|
def _start_node(attr_hash)
|
82
84
|
@context = Rosemary::Node.new(attr_hash)
|
83
85
|
end
|
@@ -94,6 +96,16 @@ class Rosemary::Parser < HTTParty::Parser
|
|
94
96
|
@context = Rosemary::Changeset.new(attr_hash)
|
95
97
|
end
|
96
98
|
|
99
|
+
def _start_permissions(_)
|
100
|
+
# just a few sanity checks: we can only parse permissions as a top level elem
|
101
|
+
raise ParseError, "Unexpected <permissions> element" unless @context.nil?
|
102
|
+
@context = Rosemary::Permissions.new
|
103
|
+
end
|
104
|
+
|
105
|
+
def _start_permission(attr_hash)
|
106
|
+
@context << attr_hash['name']
|
107
|
+
end
|
108
|
+
|
97
109
|
def _end_changeset
|
98
110
|
@collection << @context
|
99
111
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# The permissions granted to the API user.
|
2
|
+
class Rosemary::Permissions
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :raw
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@raw = []
|
9
|
+
end
|
10
|
+
|
11
|
+
# make sure we can add permissions and are "Enumerable" via delegation to the permissions array
|
12
|
+
delegate :<<, :each, :to => :raw
|
13
|
+
|
14
|
+
# some convenience helpers for permissions we already know:
|
15
|
+
%w(allow_read_prefs allow_write_prefs allow_write_diary
|
16
|
+
allow_write_api allow_read_gpx allow_write_gpx).each do |name|
|
17
|
+
define_method("#{name}?") { raw.include?(name) }
|
18
|
+
end
|
19
|
+
end
|
data/lib/rosemary/tags.rb
CHANGED
data/lib/rosemary/user.rb
CHANGED
data/lib/rosemary/version.rb
CHANGED
data/lib/rosemary/way.rb
CHANGED
@@ -9,6 +9,7 @@ module Rosemary
|
|
9
9
|
#
|
10
10
|
class Way < Element
|
11
11
|
# Array of node IDs in this way.
|
12
|
+
# @return [Array]
|
12
13
|
attr_reader :nodes
|
13
14
|
|
14
15
|
# Create new Way object.
|
@@ -38,7 +39,7 @@ module Rosemary
|
|
38
39
|
#
|
39
40
|
# Returns the way to allow chaining.
|
40
41
|
#
|
41
|
-
#
|
42
|
+
# @return [Rosemary::Way] the way itself
|
42
43
|
#
|
43
44
|
def <<(stuff)
|
44
45
|
case stuff
|
@@ -64,6 +65,9 @@ module Rosemary
|
|
64
65
|
[:id, :version, :uid, :user, :timestamp, :changeset]
|
65
66
|
end
|
66
67
|
|
68
|
+
# Instantiate a way from an XML representation.
|
69
|
+
# @param [String] xml_string the xml representing a way.
|
70
|
+
# @return [Rosemary::Way] the way represented by the given XML string.
|
67
71
|
def self.from_xml(xml_string)
|
68
72
|
Parser.call(xml_string, :xml)
|
69
73
|
end
|
data/rosemary.gemspec
CHANGED
@@ -37,6 +37,8 @@ Gem::Specification.new do |s|
|
|
37
37
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
38
38
|
s.add_development_dependency(%q<webmock>, [">= 0"])
|
39
39
|
s.add_development_dependency(%q<rake>, [">= 0"])
|
40
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
41
|
+
s.add_development_dependency(%q<redcarpet>, [">= 0"])
|
40
42
|
else
|
41
43
|
s.add_dependency(%q<httparty>, [">= 0"])
|
42
44
|
s.add_dependency(%q<libxml-ruby>, [">= 0"])
|
@@ -45,6 +47,8 @@ Gem::Specification.new do |s|
|
|
45
47
|
s.add_dependency(%q<activemodel>, [">= 0"])
|
46
48
|
s.add_dependency(%q<rspec>, [">= 0"])
|
47
49
|
s.add_dependency(%q<webmock>, [">= 0"])
|
50
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
51
|
+
s.add_dependency(%q<redcarpet>, [">= 0"])
|
48
52
|
end
|
49
53
|
else
|
50
54
|
s.add_dependency(%q<httparty>, [">= 0"])
|
@@ -54,5 +58,7 @@ Gem::Specification.new do |s|
|
|
54
58
|
s.add_dependency(%q<activemodel>, [">= 0"])
|
55
59
|
s.add_dependency(%q<rspec>, [">= 0"])
|
56
60
|
s.add_dependency(%q<webmock>, [">= 0"])
|
61
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
62
|
+
s.add_dependency(%q<redcarpet>, [">= 0"])
|
57
63
|
end
|
58
64
|
end
|
data/spec/models/node_spec.rb
CHANGED
data/spec/models/parser_spec.rb
CHANGED
@@ -20,5 +20,36 @@ describe Parser do
|
|
20
20
|
|
21
21
|
|
22
22
|
end
|
23
|
+
|
24
|
+
it "parses empty set of permissions" do
|
25
|
+
permissions_xml =<<-EOF
|
26
|
+
<osm version="0.6" generator="OpenStreetMap Server">
|
27
|
+
<permissions>
|
28
|
+
</permissions>
|
29
|
+
</osm>
|
30
|
+
EOF
|
31
|
+
|
32
|
+
permissions = Parser.call(permissions_xml, :xml)
|
33
|
+
permissions.raw.should be_empty
|
34
|
+
end
|
35
|
+
|
36
|
+
it "parses permissions" do
|
37
|
+
permissions_xml =<<-EOF
|
38
|
+
<osm version="0.6" generator="OpenStreetMap Server">
|
39
|
+
<permissions>
|
40
|
+
<permission name="allow_read_prefs" />
|
41
|
+
<permission name="allow_write_api" />
|
42
|
+
</permissions>
|
43
|
+
</osm>
|
44
|
+
EOF
|
45
|
+
|
46
|
+
permissions = Parser.call(permissions_xml, :xml)
|
47
|
+
permissions.raw.sort.should == %w(allow_read_prefs allow_write_api)
|
48
|
+
|
49
|
+
permissions.allow_write_api?.should be_true
|
50
|
+
permissions.allow_read_prefs?.should be_true
|
51
|
+
permissions.allow_write_prefs?.should be_false
|
52
|
+
end
|
23
53
|
end
|
54
|
+
|
24
55
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rosemary
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-22 00:00:00.
|
12
|
+
date: 2012-03-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httparty
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: libxml-ruby
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: builder
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :runtime
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: oauth
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
@@ -54,10 +69,15 @@ dependencies:
|
|
54
69
|
version: '0'
|
55
70
|
type: :runtime
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: activemodel
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
83
|
- - ! '>='
|
@@ -65,10 +85,15 @@ dependencies:
|
|
65
85
|
version: '0'
|
66
86
|
type: :runtime
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
69
94
|
- !ruby/object:Gem::Dependency
|
70
95
|
name: rspec
|
71
|
-
requirement:
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
72
97
|
none: false
|
73
98
|
requirements:
|
74
99
|
- - ! '>='
|
@@ -76,10 +101,15 @@ dependencies:
|
|
76
101
|
version: '0'
|
77
102
|
type: :development
|
78
103
|
prerelease: false
|
79
|
-
version_requirements:
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
80
110
|
- !ruby/object:Gem::Dependency
|
81
111
|
name: webmock
|
82
|
-
requirement:
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
83
113
|
none: false
|
84
114
|
requirements:
|
85
115
|
- - ! '>='
|
@@ -87,10 +117,15 @@ dependencies:
|
|
87
117
|
version: '0'
|
88
118
|
type: :development
|
89
119
|
prerelease: false
|
90
|
-
version_requirements:
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
91
126
|
- !ruby/object:Gem::Dependency
|
92
127
|
name: rake
|
93
|
-
requirement:
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
94
129
|
none: false
|
95
130
|
requirements:
|
96
131
|
- - ! '>='
|
@@ -98,7 +133,44 @@ dependencies:
|
|
98
133
|
version: '0'
|
99
134
|
type: :development
|
100
135
|
prerelease: false
|
101
|
-
version_requirements:
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: yard
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: redcarpet
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
102
174
|
description: OpenStreetMap API client for ruby
|
103
175
|
email:
|
104
176
|
- info@christophbuente.de
|
@@ -125,12 +197,14 @@ files:
|
|
125
197
|
- lib/rosemary/api.rb
|
126
198
|
- lib/rosemary/basic_auth_client.rb
|
127
199
|
- lib/rosemary/changeset.rb
|
200
|
+
- lib/rosemary/client.rb
|
128
201
|
- lib/rosemary/element.rb
|
129
202
|
- lib/rosemary/errors.rb
|
130
203
|
- lib/rosemary/member.rb
|
131
204
|
- lib/rosemary/node.rb
|
132
205
|
- lib/rosemary/oauth_client.rb
|
133
206
|
- lib/rosemary/parser.rb
|
207
|
+
- lib/rosemary/permissions.rb
|
134
208
|
- lib/rosemary/relation.rb
|
135
209
|
- lib/rosemary/tags.rb
|
136
210
|
- lib/rosemary/user.rb
|
@@ -167,7 +241,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
167
241
|
version: '0'
|
168
242
|
segments:
|
169
243
|
- 0
|
170
|
-
hash:
|
244
|
+
hash: -896372154269482923
|
171
245
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
246
|
none: false
|
173
247
|
requirements:
|
@@ -176,7 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
250
|
version: '1.2'
|
177
251
|
requirements: []
|
178
252
|
rubyforge_project: rosemary
|
179
|
-
rubygems_version: 1.8.
|
253
|
+
rubygems_version: 1.8.22
|
180
254
|
signing_key:
|
181
255
|
specification_version: 3
|
182
256
|
summary: OpenStreetMap API client for ruby
|
@@ -191,3 +265,4 @@ test_files:
|
|
191
265
|
- spec/models/relation_spec.rb
|
192
266
|
- spec/models/way_spec.rb
|
193
267
|
- spec/spec_helper.rb
|
268
|
+
has_rdoc:
|