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 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 save place.
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
- { :site => 'http://www.openstreetmap.org',
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
@@ -19,5 +19,11 @@ task :c => :console
19
19
 
20
20
  task :default => :spec
21
21
 
22
+ require 'yard'
23
+ YARD::Rake::YardocTask.new do |t|
24
+ t.files = ['lib/**/*.rb'] # optional
25
+ # t.options = ['--any', '--extra', '--opts'] # optional
26
+ end
27
+
22
28
  require "bundler/gem_tasks"
23
29
 
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.save(@node)
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
- # call-seq: find_node(id) -> Rosemary::Node
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
- # call-seq: find_way(id) -> Rosemary::Way
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
- # call-seq: find_user -> Rosemary::User
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
- # Delete an element
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
- # Saves an element to the API.
99
- # If it has no id yet, the element will be created, otherwise updated.
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
- attr_reader :username, :password
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
@@ -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
- # is not updated by operations to this object)
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 (as read from file, it
13
- # is not updated by operations to this object)
14
- # API 0.6 and above only
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
- # True if this changeset is still open.
18
+ # @return [Boolean] is this changeset is still open.
18
19
  attr_accessor :open
19
20
 
20
- # Creation date of this changeset
21
+ # @return [Date] creation date of this changeset
21
22
  attr_accessor :created_at
22
23
 
23
- # When the changeset was closed
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'] = 'osm for ruby'
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
- # Returns a hash of all non-nil attributes of this object.
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
- # call-seq: attributes -> Hash
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]
@@ -0,0 +1,3 @@
1
+ # Superclass for all clients used to authenticate the user toward the OSM API.
2
+ class Rosemary::Client
3
+ end
@@ -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 object (as read from file, it
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
- # call-seq: is_tagged?
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
@@ -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
- class NotImplemented < Error; end # This method is not implemented yet.
57
+ # This method is not implemented yet.
58
+ class NotImplemented < Error; end
53
59
 
54
60
  end
data/lib/rosemary/node.rb CHANGED
@@ -28,6 +28,7 @@ module Rosemary
28
28
  super(attrs)
29
29
  end
30
30
 
31
+
31
32
  def type
32
33
  'Node'
33
34
  end
@@ -1,31 +1,50 @@
1
- module Rosemary
2
- class OauthClient
1
+ class Rosemary::OauthClient < Rosemary::Client
3
2
 
4
- attr_reader :access_token
3
+ # The access token to be used for all write access
4
+ # @return [OAuth::AccessToken]
5
+ attr_reader :access_token
5
6
 
6
- def initialize(access_token)
7
- @access_token = access_token
8
- end
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
- def put(url, options={}, header={})
15
- body = options[:body]
16
- access_token.put(url, body, {'Content-Type' => 'application/xml' })
17
- end
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
- def delete(url, options={}, header={})
20
- raise NotImplemented.new("Delete with Oauth and OSM is not supported")
21
- # body = options[:body]
22
- # access_token.delete(url, {'Content-Type' => 'application/xml' })
23
- end
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
- def post(url, options={}, header={})
26
- body = options[:body]
27
- access_token.post(url, body, {'Content-Type' => 'application/xml' })
28
- end
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
@@ -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
@@ -16,7 +16,7 @@ module Rosemary
16
16
 
17
17
  # Return string with comma separated key=value pairs.
18
18
  #
19
- # call-seq: to_s -> String
19
+ # @return [String] string representation
20
20
  #
21
21
  def to_s
22
22
  sort.collect{ |k, v| "#{k}=#{v}" }.join(', ')
data/lib/rosemary/user.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'builder'
2
2
  module Rosemary
3
+ # The user object representing a registered OSM user.
3
4
  class User
4
5
  # Unique ID
5
6
  attr_reader :id
@@ -1,3 +1,4 @@
1
1
  module Rosemary
2
- VERSION = "0.3.4"
2
+ # The current version of this gem.
3
+ VERSION = "0.3.5"
3
4
  end
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
- # call-seq: way << something -> Way
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
@@ -95,4 +95,9 @@ describe Node do
95
95
  subject.add_tags(:wheelchair => '')
96
96
  subject.to_xml.should_not match /k=\"wheelchair\"/
97
97
  end
98
+
99
+ it "should properly escape ampersands" do
100
+ subject.name = "foo & bar"
101
+ subject.to_xml.should match "foo &amp; bar"
102
+ end
98
103
  end
@@ -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
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.000000000Z
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: &70334409635220 !ruby/object:Gem::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: *70334409635220
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: &70334409634680 !ruby/object:Gem::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: *70334409634680
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: &70334409634120 !ruby/object:Gem::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: *70334409634120
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: &70334409633580 !ruby/object:Gem::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: *70334409633580
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: &70334409633020 !ruby/object:Gem::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: *70334409633020
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: &70334409632200 !ruby/object:Gem::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: *70334409632200
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: &70334409631420 !ruby/object:Gem::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: *70334409631420
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: &70334409630720 !ruby/object:Gem::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: *70334409630720
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: 4299720809747143469
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.10
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: