]> git.openstreetmap.org Git - rails.git/blobdiff - app/models/relation.rb
Added better error messages on 412 precondition failed.
[rails.git] / app / models / relation.rb
index 6be1061591dda7b665b20ceed5b5b81ae79e96d3..2032e677046684067287dddb14e0f7eddecf528c 100644 (file)
@@ -27,14 +27,13 @@ class Relation < ActiveRecord::Base
 
   def self.from_xml(xml, create=false)
     begin
 
   def self.from_xml(xml, create=false)
     begin
-      p = XML::Parser.new
-      p.string = xml
+      p = XML::Parser.string(xml)
       doc = p.parse
 
       doc.find('//osm/relation').each do |pt|
         return Relation.from_xml_node(pt, create)
       end
       doc = p.parse
 
       doc.find('//osm/relation').each do |pt|
         return Relation.from_xml_node(pt, create)
       end
-    rescue LibXML::XML::Error => ex
+    rescue LibXML::XML::Error, ArgumentError => ex
       raise OSM::APIBadXMLError.new("relation", xml, ex.message)
     end
   end
       raise OSM::APIBadXMLError.new("relation", xml, ex.message)
     end
   end
@@ -52,7 +51,7 @@ class Relation < ActiveRecord::Base
     # The follow block does not need to be executed because they are dealt with 
     # in create_with_history, update_from and delete_with_history
     if create
     # The follow block does not need to be executed because they are dealt with 
     # in create_with_history, update_from and delete_with_history
     if create
-      relation.timestamp = Time.now
+      relation.timestamp = Time.now.getutc
       relation.visible = true
       relation.version = 0
     else
       relation.visible = true
       relation.version = 0
     else
@@ -75,7 +74,7 @@ class Relation < ActiveRecord::Base
       #member_role
       member['role'] ||= "" # Allow  the upload to not include this, in which case we default to an empty string.
       logger.debug member['role']
       #member_role
       member['role'] ||= "" # Allow  the upload to not include this, in which case we default to an empty string.
       logger.debug member['role']
-      relation.add_member(member['type'], member['ref'], member['role'])
+      relation.add_member(member['type'].classify, member['ref'], member['role'])
     end
     raise OSM::APIBadUserInput.new("Some bad xml in relation") if relation.nil?
 
     end
     raise OSM::APIBadUserInput.new("Some bad xml in relation") if relation.nil?
 
@@ -126,7 +125,7 @@ class Relation < ActiveRecord::Base
       #end
       if p
         e = XML::Node.new 'member'
       #end
       if p
         e = XML::Node.new 'member'
-        e['type'] = member.member_type
+        e['type'] = member.member_type.downcase
         e['ref'] = member.member_id.to_s 
         e['role'] = member.member_role
         el1 << e
         e['ref'] = member.member_id.to_s 
         e['role'] = member.member_role
         el1 << e
@@ -146,7 +145,7 @@ class Relation < ActiveRecord::Base
     if ids.empty?
       return []
     else
     if ids.empty?
       return []
     else
-      self.with_scope(:find => { :joins => "INNER JOIN current_relation_members ON current_relation_members.id = current_relations.id", :conditions => "current_relation_members.member_type = 'node' AND current_relation_members.member_id IN (#{ids.join(',')})" }) do
+      self.with_scope(:find => { :joins => "INNER JOIN current_relation_members ON current_relation_members.id = current_relations.id", :conditions => "current_relation_members.member_type = 'Node' AND current_relation_members.member_id IN (#{ids.join(',')})" }) do
         return self.find(:all, options)
       end
     end
         return self.find(:all, options)
       end
     end
@@ -156,7 +155,7 @@ class Relation < ActiveRecord::Base
     if ids.empty?
       return []
     else
     if ids.empty?
       return []
     else
-      self.with_scope(:find => { :joins => "INNER JOIN current_relation_members ON current_relation_members.id = current_relations.id", :conditions => "current_relation_members.member_type = 'way' AND current_relation_members.member_id IN (#{ids.join(',')})" }) do
+      self.with_scope(:find => { :joins => "INNER JOIN current_relation_members ON current_relation_members.id = current_relations.id", :conditions => "current_relation_members.member_type = 'Way' AND current_relation_members.member_id IN (#{ids.join(',')})" }) do
         return self.find(:all, options)
       end
     end
         return self.find(:all, options)
       end
     end
@@ -166,7 +165,7 @@ class Relation < ActiveRecord::Base
     if ids.empty?
       return []
     else
     if ids.empty?
       return []
     else
-      self.with_scope(:find => { :joins => "INNER JOIN current_relation_members ON current_relation_members.id = current_relations.id", :conditions => "current_relation_members.member_type = 'relation' AND current_relation_members.member_id IN (#{ids.join(',')})" }) do
+      self.with_scope(:find => { :joins => "INNER JOIN current_relation_members ON current_relation_members.id = current_relations.id", :conditions => "current_relation_members.member_type = 'Relation' AND current_relation_members.member_id IN (#{ids.join(',')})" }) do
         return self.find(:all, options)
       end
     end
         return self.find(:all, options)
       end
     end
@@ -237,9 +236,9 @@ class Relation < ActiveRecord::Base
     Relation.transaction do
       check_consistency(self, new_relation, user)
       # This will check to see if this relation is used by another relation
     Relation.transaction do
       check_consistency(self, new_relation, user)
       # This will check to see if this relation is used by another relation
-      if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = ? AND member_type='relation' and member_id=? ", true, self.id ])
-        raise OSM::APIPreconditionFailedError.new("The relation #{new_relation.id} is a used in another relation")
-      end
+      rel = RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = ? AND member_type='Relation' and member_id=? ", true, self.id ])
+      raise OSM::APIPreconditionFailedError.new("The relation #{new_relation.id} is a used in relation #{rel.id}.") unless rel.nil?
+
       self.changeset_id = new_relation.changeset_id
       self.tags = {}
       self.members = []
       self.changeset_id = new_relation.changeset_id
       self.tags = {}
       self.members = []
@@ -250,10 +249,11 @@ class Relation < ActiveRecord::Base
 
   def update_from(new_relation, user)
     check_consistency(self, new_relation, user)
 
   def update_from(new_relation, user)
     check_consistency(self, new_relation, user)
-    if !new_relation.preconditions_ok?
-      raise OSM::APIPreconditionFailedError.new
+    unless preconditions_ok?
+      raise OSM::APIPreconditionFailedError.new("Cannot update relation #{self.id}: data or member data is invalid.")
     end
     self.changeset_id = new_relation.changeset_id
     end
     self.changeset_id = new_relation.changeset_id
+    self.changeset = new_relation.changeset
     self.tags = new_relation.tags
     self.members = new_relation.members
     self.visible = true
     self.tags = new_relation.tags
     self.members = new_relation.members
     self.visible = true
@@ -262,8 +262,8 @@ class Relation < ActiveRecord::Base
   
   def create_with_history(user)
     check_create_consistency(self, user)
   
   def create_with_history(user)
     check_create_consistency(self, user)
-    if !self.preconditions_ok?
-      raise OSM::APIPreconditionFailedError.new
+    unless self.preconditions_ok?
+      raise OSM::APIPreconditionFailedError.new("Cannot create relation: data or member data is invalid.")
     end
     self.version = 0
     self.visible = true
     end
     self.version = 0
     self.visible = true
@@ -281,13 +281,12 @@ class Relation < ActiveRecord::Base
     elements = { :node => Hash.new, :way => Hash.new, :relation => Hash.new }
     self.members.each do |m|
       # find the hash for the element type or die
     elements = { :node => Hash.new, :way => Hash.new, :relation => Hash.new }
     self.members.each do |m|
       # find the hash for the element type or die
-      hash = elements[m[0].to_sym] or return false
-
+      logger.debug m[0]
+      hash = elements[m[0].downcase.to_sym] or return false
       # unless its in the cache already
       unless hash.key? m[1]
         # use reflection to look up the appropriate class
         model = Kernel.const_get(m[0].capitalize)
       # unless its in the cache already
       unless hash.key? m[1]
         # use reflection to look up the appropriate class
         model = Kernel.const_get(m[0].capitalize)
-
         # get the element with that ID
         element = model.find(m[1])
 
         # get the element with that ID
         element = model.find(m[1])
 
@@ -317,7 +316,7 @@ class Relation < ActiveRecord::Base
     self.members.map! do |type, id, role|
       old_id = id.to_i
       if old_id < 0
     self.members.map! do |type, id, role|
       old_id = id.to_i
       if old_id < 0
-        new_id = id_map[type.to_sym][old_id]
+        new_id = id_map[type.downcase.to_sym][old_id]
         raise "invalid placeholder" if new_id.nil?
         [type, new_id, role]
       else
         raise "invalid placeholder" if new_id.nil?
         [type, new_id, role]
       else
@@ -334,7 +333,7 @@ class Relation < ActiveRecord::Base
       # changed then we have to monitor their before and after state.
       tags_changed = false
 
       # changed then we have to monitor their before and after state.
       tags_changed = false
 
-      t = Time.now
+      t = Time.now.getutc
       self.version += 1
       self.timestamp = t
       self.save!
       self.version += 1
       self.timestamp = t
       self.save!
@@ -373,6 +372,10 @@ class Relation < ActiveRecord::Base
         tag.id = self.id
         tag.save!
       end
         tag.id = self.id
         tag.save!
       end
+      
+      # reload, so that all of the members are accessible in their
+      # new state.
+      self.reload
 
       # same pattern as before, but this time we're collecting the
       # changed members in an array, as the bounding box updates for
 
       # same pattern as before, but this time we're collecting the
       # changed members in an array, as the bounding box updates for
@@ -431,14 +434,16 @@ class Relation < ActiveRecord::Base
         # FIXME: check for tag changes along with element deletions and
         # make sure that the deleted element's bounding box is hit.
         self.members.each do |type, id, role|
         # FIXME: check for tag changes along with element deletions and
         # make sure that the deleted element's bounding box is hit.
         self.members.each do |type, id, role|
-          if type != "relation"
+          if type != "Relation"
             update_changeset_element(type, id)
           end
         end
       else
         # add only changed members to the changeset
         changed_members.each do |id, type|
             update_changeset_element(type, id)
           end
         end
       else
         # add only changed members to the changeset
         changed_members.each do |id, type|
-          update_changeset_element(type, id)
+          if type != "Relation"
+            update_changeset_element(type, id)
+          end
         end
       end
 
         end
       end