Merge rails_port as of r4613 & fix tests.
authorGabriel Ebner <gabriel@svn.openstreetmap.org>
Thu, 20 Sep 2007 14:37:29 +0000 (14:37 +0000)
committerGabriel Ebner <gabriel@svn.openstreetmap.org>
Thu, 20 Sep 2007 14:37:29 +0000 (14:37 +0000)
64 files changed:
app/controllers/amf_controller.rb
app/controllers/api_controller.rb
app/controllers/node_controller.rb
app/controllers/old_segment_controller.rb [deleted file]
app/controllers/old_way_node_controller.rb [new file with mode: 0644]
app/controllers/old_way_segment_controller.rb [deleted file]
app/controllers/relation_controller.rb [new file with mode: 0644]
app/controllers/relation_member_controller.rb [new file with mode: 0644]
app/controllers/relation_tag_controller.rb [new file with mode: 0644]
app/controllers/search_controller.rb
app/controllers/segment_controller.rb [deleted file]
app/controllers/site_controller.rb
app/controllers/swf_controller.rb
app/controllers/way_controller.rb
app/controllers/way_node_controller.rb [new file with mode: 0644]
app/controllers/way_segment_controller.rb [deleted file]
app/helpers/old_segment_helper.rb [deleted file]
app/helpers/old_way_node_helper.rb [new file with mode: 0644]
app/helpers/old_way_segment_helper.rb [deleted file]
app/helpers/segment_helper.rb [deleted file]
app/helpers/way_node_helper.rb [new file with mode: 0644]
app/helpers/way_segment_helper.rb [deleted file]
app/models/old_relation.rb [new file with mode: 0644]
app/models/old_relation_member.rb [new file with mode: 0644]
app/models/old_relation_tag.rb [new file with mode: 0644]
app/models/old_segment.rb [deleted file]
app/models/old_way.rb
app/models/old_way_node.rb [new file with mode: 0644]
app/models/old_way_segment.rb [deleted file]
app/models/relation.rb [new file with mode: 0644]
app/models/relation_member.rb [new file with mode: 0644]
app/models/relation_tag.rb [new file with mode: 0644]
app/models/segment.rb [deleted file]
app/models/way.rb
app/models/way_node.rb [new file with mode: 0644]
app/models/way_segment.rb [deleted file]
config/environment.rb
config/routes.rb
db/migrate/006_add_relations.rb [new file with mode: 0644]
db/migrate/007_remove_segments.rb [new file with mode: 0644]
db/migrate/007_remove_segments_helper.cc [new file with mode: 0644]
script/statistics
test/fixtures/current_relation_members.yml [new file with mode: 0644]
test/fixtures/current_relation_tags.yml [new file with mode: 0644]
test/fixtures/current_relations.yml [new file with mode: 0644]
test/fixtures/current_segments.yml [deleted file]
test/fixtures/current_way_nodes.yml [new file with mode: 0644]
test/fixtures/current_way_segments.yml [deleted file]
test/fixtures/current_way_tags.yml
test/fixtures/current_ways.yml
test/fixtures/relation_members.yml [new file with mode: 0644]
test/fixtures/relation_tags.yml [new file with mode: 0644]
test/fixtures/relations.yml [new file with mode: 0644]
test/fixtures/segments.yml [deleted file]
test/fixtures/way_nodes.yml [moved from test/fixtures/way_segments.yml with 50% similarity]
test/fixtures/way_tags.yml
test/fixtures/ways.yml
test/functional/api_controller_test.rb [new file with mode: 0644]
test/functional/relation_controller_test.rb [new file with mode: 0644]
test/functional/segment_controller_test.rb [deleted file]
test/functional/way_controller_test.rb
test/test_helper.rb
test/unit/node_test.rb
test/unit/segment_test.rb [deleted file]

index 3ba91fb..99c2a48 100644 (file)
@@ -221,12 +221,12 @@ EOF
 
     RAILS_DEFAULT_LOGGER.info("  Message: whichways, bbox=#{xmin},#{ymin},#{xmax},#{ymax}")
 
-    waylist=WaySegment.find_by_sql("SELECT DISTINCT current_way_segments.id AS wayid"+
-       "  FROM current_way_segments,current_segments,current_nodes,current_ways "+
+    waylist=WayNode.find_by_sql("SELECT DISTINCT current_way_nodes.id AS wayid"+
+       "  FROM current_way_nodes,current_segments,current_nodes,current_ways "+
        " WHERE segment_id=current_segments.id "+
        "   AND current_segments.visible=1 "+
        "   AND node_a=current_nodes.id "+
-          "   AND current_ways.id=current_way_segments.id "+
+          "   AND current_ways.id=current_way_nodes.id "+
           "   AND current_ways.visible=1 "+
        "   AND (latitude  BETWEEN "+ymin.to_s+" AND "+ymax.to_s+") "+
        "   AND (longitude BETWEEN "+xmin.to_s+" AND "+xmax.to_s+")")
@@ -429,7 +429,7 @@ EOF
     ActiveRecord::Base.connection.execute("DROP TABLE #{db_uqs}")
     ActiveRecord::Base.connection.execute("DROP TABLE #{db_uqn}")
 
-    #          insert new version of route into way_segments
+    #          insert new version of route into way_nodes
 
     insertsql =''
     currentsql=''
@@ -443,9 +443,9 @@ EOF
       sequence  +=1
     end
 
-    ActiveRecord::Base.connection.execute("DELETE FROM current_way_segments WHERE id=#{way}");
-    ActiveRecord::Base.connection.insert("INSERT INTO         way_segments (id,segment_id,version    ) VALUES #{insertsql}");
-    ActiveRecord::Base.connection.insert("INSERT INTO current_way_segments (id,segment_id,sequence_id) VALUES #{currentsql}");
+    ActiveRecord::Base.connection.execute("DELETE FROM current_way_nodes WHERE id=#{way}");
+    ActiveRecord::Base.connection.insert("INSERT INTO         way_nodes (id,segment_id,version    ) VALUES #{insertsql}");
+    ActiveRecord::Base.connection.insert("INSERT INTO current_way_nodes (id,segment_id,sequence_id) VALUES #{currentsql}");
 
     # -- 7. insert new way tags
 
@@ -572,7 +572,7 @@ EOF
        
        ActiveRecord::Base.connection.insert("INSERT INTO ways (id,user_id,timestamp,visible) VALUES (#{way},#{uid},#{db_now},0)")
        ActiveRecord::Base.connection.update("UPDATE current_ways SET user_id=#{uid},timestamp=#{db_now},visible=0 WHERE id=#{way}")
-       ActiveRecord::Base.connection.execute("DELETE FROM current_way_segments WHERE id=#{way}")
+       ActiveRecord::Base.connection.execute("DELETE FROM current_way_nodes WHERE id=#{way}")
        ActiveRecord::Base.connection.execute("DELETE FROM current_way_tags WHERE id=#{way}")
        
        way
@@ -606,7 +606,7 @@ def makeway(args)
                  FROM current_nodes AS cn1,
                       current_nodes AS cn2,
                       current_segments AS cs 
-                      LEFT OUTER JOIN current_way_segments ON segment_id=cs.id 
+                      LEFT OUTER JOIN current_way_nodes ON segment_id=cs.id 
                 WHERE (cn1.longitude BETWEEN #{xs1} AND #{xs2}) 
                   AND (cn1.latitude  BETWEEN #{ys1} AND #{ys2}) 
                   AND segment_id IS NULL 
@@ -672,7 +672,7 @@ def findconnect(id,nodesused,lookfor,toreverse,baselong,basey,masterscale)
                  FROM current_nodes AS cn1,
                       current_nodes AS cn2,
                       current_segments AS cs 
-                      LEFT OUTER JOIN current_way_segments ON segment_id=cs.id 
+                      LEFT OUTER JOIN current_way_nodes ON segment_id=cs.id 
                 WHERE segment_id IS NULL 
                    AND cs.visible=1
                   AND cn1.id=node_a AND cn1.visible=1 
@@ -684,7 +684,7 @@ def findconnect(id,nodesused,lookfor,toreverse,baselong,basey,masterscale)
                  FROM current_nodes AS cn1,
                       current_nodes AS cn2,
                       current_segments AS cs 
-                      LEFT OUTER JOIN current_way_segments ON segment_id=cs.id 
+                      LEFT OUTER JOIN current_way_nodes ON segment_id=cs.id 
                 WHERE segment_id IS NULL 
                    AND cs.visible=1
                   AND cn1.id=node_a AND cn1.visible=1 
@@ -732,8 +732,8 @@ end
 def readwayquery(id)
   ActiveRecord::Base.connection.select_all "SELECT n1.latitude AS lat1,n1.longitude AS long1,n1.id AS id1,n1.tags as tags1, "+
       "                  n2.latitude AS lat2,n2.longitude AS long2,n2.id AS id2,n2.tags as tags2,segment_id "+
-      "    FROM current_way_segments,current_segments,current_nodes AS n1,current_nodes AS n2 "+
-      "   WHERE current_way_segments.id=#{id} "+
+      "    FROM current_way_nodes,current_segments,current_nodes AS n1,current_nodes AS n2 "+
+      "   WHERE current_way_nodes.id=#{id} "+
       "     AND segment_id=current_segments.id "+
          "     AND current_segments.visible=1 "+
       "     AND n1.id=node_a and n2.id=node_b "+
@@ -746,9 +746,9 @@ def createuniquesegments(way,uqs_name,seglist)
   sql=<<-EOF
       CREATE TEMPORARY TABLE #{uqs_name}
               SELECT a.segment_id
-                FROM (SELECT DISTINCT segment_id FROM current_way_segment
+                FROM (SELECT DISTINCT segment_id FROM current_way_node
                   WHERE id = #{way}) a
-             LEFT JOIN current_way_segments b 
+             LEFT JOIN current_way_nodes b 
                 ON b.segment_id = a.segment_id
                  AND b.id != #{way}
                WHERE b.segment_id IS NULL
index b39cbdd..6e8e77c 100644 (file)
@@ -131,7 +131,8 @@ class ApiController < ApplicationController
     # check the bbox isn't too large
     requested_area = (max_lat-min_lat)*(max_lon-min_lon)
     if requested_area > MAX_REQUEST_AREA
-      report_error("The maximum bbox size is " + MAX_REQUEST_AREA.to_s + ", and your request was too large. Either request a smaller area, or use planet.osm")
+      report_error("The maximum bbox size is " + MAX_REQUEST_AREA.to_s + 
+        ", and your request was too large. Either request a smaller area, or use planet.osm")
       return
     end
 
@@ -146,68 +147,38 @@ class ApiController < ApplicationController
     end
 
     if node_ids.length == 0
-      render :text => "<osm version='0.4'></osm>", :content_type => "text/xml"
+      render :text => "<osm version='0.5'></osm>", :content_type => "text/xml"
       return
     end
 
-    # grab the segments
-    segments = Array.new
-    if node_ids.length > 0
-      node_ids_sql = "(#{node_ids.join(',')})"
-      # get the referenced segments
-      segments = Segment.find_by_sql "select * from current_segments where visible = 1 and (node_a in #{node_ids_sql} or node_b in #{node_ids_sql})"
-    end
-    # see if we have any missing nodes
-    segments_nodes = segments.collect {|segment| segment.node_a }
-    segments_nodes += segments.collect {|segment| segment.node_b }
-
-    segments_nodes.uniq!
-
-    missing_nodes = segments_nodes - node_ids
-
-    # get missing nodes if there are any
-    nodes += Node.find(missing_nodes) if missing_nodes.length > 0
+    relations = Array.new
 
     doc = OSM::API.new.get_xml_doc
 
     # get ways
     # find which ways are needed
-    segment_ids = segments.collect {|segment| segment.id }
     ways = Array.new
-    if segment_ids.length > 0
-      way_segments = WaySegment.find_all_by_segment_id(segment_ids)
-      way_ids = way_segments.collect {|way_segment| way_segment.id }
-      ways = Way.find(way_ids) # NB: doesn't pick up segments, tags from db until accessed via way.way_segments etc.
-
-      # seg_ids = way_segments.collect {|way_segment| way_segment.segment_id }
-
-      list_of_way_segs = ways.collect {|way| way.way_segments}
-      list_of_way_segs.flatten!
+    if node_ids.length > 0
+      way_nodes = WayNode.find_all_by_node_id(node_ids)
+      way_ids = way_nodes.collect {|way_node| way_node.id }
+      ways = Way.find(way_ids)
 
-      list_of_way_segments = list_of_way_segs.collect { |way_seg| way_seg.segment_id }
+      list_of_way_nodes = ways.collect { |way|
+       way.way_nodes.collect { |way_node| way_node.node_id }
+      }
+      list_of_way_nodes.flatten!
 
-       else
-         list_of_way_segments = Array.new
+    else
+      list_of_way_nodes = Array.new
     end
 
-    # - [0] in case some thing links to segment 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this
-    segments_to_fetch = (list_of_way_segments.uniq - segment_ids) - [0]
+    # - [0] in case some thing links to node 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this
+    nodes_to_fetch = (list_of_way_nodes.uniq - node_ids) - [0]
 
-    if segments_to_fetch.length > 0
-      segments += Segment.find(segments_to_fetch)
+    if nodes_to_fetch.length > 0
+      nodes += Node.find(nodes_to_fetch)
     end
 
-    # get more nodes
-    #
-
-    segments_nodes = segments.collect {|segment| segment.node_a }
-    segments_nodes += segments.collect {|segment| segment.node_b }
-
-    node_ids_a = nodes.collect {|node| node.id }
-
-    nodes_to_get = segments_nodes - node_ids_a
-    nodes += Node.find(nodes_to_get) if nodes_to_get.length > 0
-
     visible_nodes = {}
     user_display_name_cache = {}
 
@@ -218,19 +189,41 @@ class ApiController < ApplicationController
       end
     end
 
-    visible_segments = {}
-
-    segments.each do |segment|
-      if visible_nodes[segment.node_a] and visible_nodes[segment.node_b] and segment.visible?
-        doc.root << segment.to_xml_node(user_display_name_cache) 
-        visible_segments[segment.id] = segment
-      end
-    end
-
+    way_ids = Array.new
     ways.each do |way|
-      doc.root << way.to_xml_node(visible_segments, user_display_name_cache) if way.visible?
+      if way.visible?
+        doc.root << way.to_xml_node(visible_nodes, user_display_name_cache)
+        way_ids << way.id
+      end
     end 
 
+    # collect relationships. currently done in one big block at the end;
+    # may need to move this upwards if people want automatic completion of
+    # relationships, i.e. deliver referenced objects like we do with ways...
+    relations = Relation.find_by_sql("select e.* from current_relations e,current_relation_members em where " +
+        "e.visible=1 and " +
+        "em.id = e.id and em.member_type='node' and em.member_id in (#{visible_nodes.keys.join(',')})")
+    relations += Relation.find_by_sql("select e.* from current_relations e,current_relation_members em where " +
+        "e.visible=1 and " +
+        "em.id = e.id and em.member_type='way' and em.member_id in (#{way_ids.join(',')})")
+    # we do not normally return the "other" partners referenced by an relation, 
+    # e.g. if we return a way A that is referenced by relation X, and there's 
+    # another way B also referenced, that is not returned. But we do make 
+    # an exception for cases where an relation references another *relation*; 
+    # in that case we return that as well (but we don't go recursive here)
+    relation_ids = relations.collect { |relation| relation.id }
+    if relation_ids.length > 0
+        relations += Relation.find_by_sql("select e.* from current_relations e,current_relation_members em where " +
+            "e.visible=1 and " +
+            "em.id = e.id and em.member_type='relation' and em.member_id in (#{relation_ids.join(',')})")
+    end
+
+    # this "uniq" may be slightly inefficient; it may be better to first collect and output
+    # all node-related relations, then find the *not yet covered* way-related ones etc.
+    relations.uniq.each do |relation|
+      doc.root << relation.to_xml_node(user_display_name_cache)
+    end
+
     render :text => doc.to_s, :content_type => "text/xml"
     
     #exit when we have too many requests
@@ -246,8 +239,8 @@ class ApiController < ApplicationController
 
     api = XML::Node.new 'api'
     version = XML::Node.new 'version'
-    version['minimum'] = '0.4';
-    version['maximum'] = '0.4';
+    version['minimum'] = '0.5';
+    version['maximum'] = '0.5';
     api << version
     area = XML::Node.new 'area'
     area['maximum'] = MAX_REQUEST_AREA.to_s;
index 4e2a9b7..7c7e3e6 100644 (file)
@@ -69,7 +69,9 @@ class NodeController < ApplicationController
       node = Node.find(params[:id])
 
       if node.visible
-        if Segment.find(:first, :conditions => [ "visible = 1 and (node_a = ? or node_b = ?)", node.id, node.id])
+        if WayNode.find(:first, :joins => "INNER JOIN current_ways ON current_ways.id = current_way_nodes.id", :conditions => [ "current_ways.visible = 1 AND current_way_nodes.node_id = ?", node.id ])
+          render :nothing => true, :status => :precondition_failed
+        elsif RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='node' and member_id=?", params[:id]])
           render :nothing => true, :status => :precondition_failed
         else
           node.user_id = @user.id
diff --git a/app/controllers/old_segment_controller.rb b/app/controllers/old_segment_controller.rb
deleted file mode 100644 (file)
index 2d48fb5..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-class OldSegmentController < ApplicationController
-  require 'xml/libxml'
-
-  session :off
-  after_filter :compress_output
-
-  def history
-    begin
-      segment = Segment.find(params[:id])
-
-      doc = OSM::API.new.get_xml_doc
-
-      segment.old_segments.each do |old_segment|
-        doc.root << old_segment.to_xml_node
-      end
-
-     render :text => doc.to_s, :content_type => "text/xml"
-    rescue ActiveRecord::RecordNotFound
-      render :nothing => true, :status => :not_found
-    rescue
-      render :nothing => true, :status => :internal_server_error
-    end
-  end
-end
diff --git a/app/controllers/old_way_node_controller.rb b/app/controllers/old_way_node_controller.rb
new file mode 100644 (file)
index 0000000..69fa970
--- /dev/null
@@ -0,0 +1,2 @@
+class OldWayNodeController < ApplicationController
+end
diff --git a/app/controllers/old_way_segment_controller.rb b/app/controllers/old_way_segment_controller.rb
deleted file mode 100644 (file)
index 4e9404c..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-class OldWaySegmentController < ApplicationController
-end
diff --git a/app/controllers/relation_controller.rb b/app/controllers/relation_controller.rb
new file mode 100644 (file)
index 0000000..d00e9e0
--- /dev/null
@@ -0,0 +1,218 @@
+class RelationController < ApplicationController
+  require 'xml/libxml'
+
+  before_filter :authorize, :only => [:create, :update, :delete]
+  before_filter :check_availability, :only => [:create, :update, :delete]
+
+  after_filter :compress_output
+
+  def create
+    if request.put?
+      relation = Relation.from_xml(request.raw_post, true)
+
+      if relation
+        if !relation.preconditions_ok?
+          render :nothing => true, :status => :precondition_failed
+        else
+          relation.user_id = @user.id
+          relation.save_with_history!
+
+         render :text => relation.id.to_s, :content_type => "text/plain"
+        end
+      else
+        render :nothing => true, :status => :bad_request
+      end
+    else
+      render :nothing => true, :status => :method_not_allowed
+    end
+  end
+
+  def read
+    begin
+      relation = Relation.find(params[:id])
+
+      if relation.visible
+        render :text => relation.to_xml.to_s, :content_type => "text/xml"
+      else
+        render :nothing => true, :status => :gone
+      end
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+    rescue
+      render :nothing => true, :status => :internal_server_error
+    end
+  end
+
+  def update
+    begin
+      relation = Relation.find(params[:id])
+
+      if relation.visible
+        new_relation = Relation.from_xml(request.raw_post)
+
+        if new_relation and new_relation.id == relation.id
+          if !new_relation.preconditions_ok?
+            render :nothing => true, :status => :precondition_failed
+          else
+            relation.user_id = @user.id
+            relation.tags = new_relation.tags
+            relation.members = new_relation.members
+            relation.visible = true
+            relation.save_with_history!
+
+            render :nothing => true
+          end
+        else
+          render :nothing => true, :status => :bad_request
+        end
+      else
+        render :nothing => true, :status => :gone
+      end
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+    rescue
+      render :nothing => true, :status => :internal_server_error
+    end
+  end
+
+  def delete
+#XXX check if member somewhere!
+    begin
+      relation = Relation.find(params[:id])
+
+      if relation.visible
+        if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='relation' and member_id=?", params[:id]])
+          render :nothing => true, :status => :precondition_failed
+        else
+          relation.user_id = @user.id
+          relation.tags = []
+          relation.members = []
+          relation.visible = false
+          relation.save_with_history!
+
+          render :nothing => true
+        end
+      else
+        render :nothing => true, :status => :gone
+      end
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+    rescue
+      render :nothing => true, :status => :internal_server_error
+    end
+  end
+
+  # -----------------------------------------------------------------
+  # full
+  # 
+  # input parameters: id
+  #
+  # returns XML representation of one relation object plus all its
+  # members, plus all nodes part of member ways
+  # -----------------------------------------------------------------
+  def full
+    begin
+      relation = Relation.find(params[:id])
+
+      if relation.visible
+
+        # first collect nodes, ways, and relations referenced by this relation.
+        
+        ways = Way.find_by_sql("select w.* from current_ways w,current_relation_members rm where "+
+            "rm.member_type='way' and rm.member_id=w.id and rm.id=#{relation.id}");
+        nodes = Node.find_by_sql("select n.* from current_nodes n,current_relation_members rm where "+
+            "rm.member_type='node' and rm.member_id=n.id and rm.id=#{relation.id}");
+        # note query is built to exclude self just in case.
+        relations = Relation.find_by_sql("select r.* from current_relations r,current_relation_members rm where "+
+            "rm.member_type='relation' and rm.member_id=r.id and rm.id=#{relation.id} and r.id<>rm.id");
+
+        # now additionally collect nodes referenced by ways. Note how we recursively 
+        # evaluate ways but NOT relations.
+
+        node_ids = nodes.collect {|node| node.id }
+        way_node_ids = ways.collect { |way|
+           way.way_nodes.collect { |way_node| way_node.node_id }
+        }
+        way_node_ids.flatten!
+        way_node_ids.uniq
+        way_node_ids -= node_ids
+        nodes += Node.find(way_node_ids)
+    
+        # create XML.
+        doc = OSM::API.new.get_xml_doc
+        user_display_name_cache = {}
+
+        nodes.each do |node|
+          if node.visible? # should be unnecessary if data is consistent.
+            doc.root << node.to_xml_node(user_display_name_cache)
+          end
+        end
+        ways.each do |way|
+          if way.visible? # should be unnecessary if data is consistent.
+            doc.root << way.to_xml_node(user_display_name_cache)
+          end
+        end
+        relations.each do |rel|
+          if rel.visible? # should be unnecessary if data is consistent.
+            doc.root << rel.to_xml_node(user_display_name_cache)
+          end
+        end
+        # finally add self and output
+        doc.root << relation.to_xml_node(user_display_name_cache)
+        render :text => doc.to_s, :content_type => "text/xml"
+
+      else
+
+        render :nothing => true, :status => :gone
+      end
+
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+
+    rescue
+      render :nothing => true, :status => :internal_server_error
+    end
+  end
+
+  def relations
+    ids = params['relations'].split(',').collect { |w| w.to_i }
+
+    if ids.length > 0
+      doc = OSM::API.new.get_xml_doc
+
+      Relation.find(ids).each do |relation|
+        doc.root << relation.to_xml_node
+      end
+
+      render :text => doc.to_s, :content_type => "text/xml"
+    else
+      render :nothing => true, :status => :bad_request
+    end
+  end
+
+  def relations_for_way
+    relations_for_object("way")
+  end
+  def relations_for_node
+    relations_for_object("node")
+  end
+  def relations_for_relation
+    relations_for_object("relation")
+  end
+
+  def relations_for_object(objtype)
+    relationids = RelationMember.find(:all, :conditions => ['member_type=? and member_id=?', objtype, params[:id]]).collect { |ws| ws.id }.uniq
+
+    if relationids.length > 0
+      doc = OSM::API.new.get_xml_doc
+
+      Relation.find(relationids).each do |relation|
+        doc.root << relation.to_xml_node
+      end
+
+      render :text => doc.to_s, :content_type => "text/xml"
+    else
+      render :nothing => true, :status => :not_found
+    end
+  end
+end
diff --git a/app/controllers/relation_member_controller.rb b/app/controllers/relation_member_controller.rb
new file mode 100644 (file)
index 0000000..3eb7fca
--- /dev/null
@@ -0,0 +1,5 @@
+class RelationMemberController < ApplicationController
+
+
+
+end
diff --git a/app/controllers/relation_tag_controller.rb b/app/controllers/relation_tag_controller.rb
new file mode 100644 (file)
index 0000000..c58364c
--- /dev/null
@@ -0,0 +1,9 @@
+class RelationTagController < ApplicationController
+  layout 'site'
+
+  def search
+    @tags = RelationTag.find(:all, :limit => 11, :conditions => ["match(v) against (?)", params[:query][:query].to_s] )
+  end
+
+
+end
index de305fc..b3e7ca9 100644 (file)
@@ -1,26 +1,23 @@
 class SearchController < ApplicationController
-  # Support searching for nodes, segments, ways, or all
+  # Support searching for nodes, ways, or all
   # Can search by tag k, v, or both (type->k,value->v)
   # Can search by name (k=name,v=....)
 
   after_filter :compress_output
 
   def search_all
-    do_search(true,true,true)
+    do_search(true,true)
   end
 
   def search_ways
-    do_search(true,false,false)
-  end
-  def search_segments
-    do_search(false,true,false)
+    do_search(true,false)
   end
   def search_nodes
-    do_search(false,false,true)
+    do_search(false,true)
   end
 
 
-  def do_search(do_ways,do_segments,do_nodes)
+  def do_search(do_ways,do_nodes)
     type = params['type']
     value = params['value']
     unless type or value
@@ -33,7 +30,6 @@ class SearchController < ApplicationController
 
     way_ids = Array.new
     ways = Array.new
-    segments = Array.new
     nodes = Array.new
 
     # Matching for tags table
@@ -75,21 +71,13 @@ class SearchController < ApplicationController
       ways = Way.find(:all, :conditions => cond_tbl, :limit => 100)
     end
 
-    # Now, segments matching
-    if do_segments
-      segments = Segment.find(:all, :conditions => cond_tags, :limit => 500)
-    end
-
     # Now, nodes
     if do_nodes
       nodes = Node.find(:all, :conditions => cond_tags, :limit => 2000)
     end
 
-    # Fetch any segments needed for our ways (only have matching segments so far)
-    segments += Segment.find(ways.collect { |w| w.segs }.uniq)
-
-    # Fetch any nodes needed for our segments (only have matching nodes so far)
-    nodes += Node.find(segments.collect { |s| [s.node_a, s.node_b] }.flatten.uniq)
+    # Fetch any node needed for our ways (only have matching nodes so far)
+    nodes += Node.find(ways.collect { |w| w.nds }.uniq)
 
     # Print
     user_display_name_cache = {}
@@ -98,10 +86,6 @@ class SearchController < ApplicationController
       doc.root << node.to_xml_node(user_display_name_cache)
     end
 
-    segments.each do |segment|
-      doc.root << segment.to_xml_node(user_display_name_cache)
-    end 
-
     ways.each do |way|
       doc.root << way.to_xml_node(user_display_name_cache)
     end 
diff --git a/app/controllers/segment_controller.rb b/app/controllers/segment_controller.rb
deleted file mode 100644 (file)
index b99f746..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-class SegmentController < ApplicationController
-  require 'xml/libxml'
-
-  session :off
-  before_filter :authorize, :only => [:create, :update, :delete]
-  before_filter :check_availability, :only => [:create, :update, :delete]
-  after_filter :compress_output
-
-  def create
-    if request.put?
-      segment = Segment.from_xml(request.raw_post, true)
-
-      if segment
-        if segment.node_a == segment.node_b
-          render :nothing => true, :status => :expectation_failed
-        elsif !segment.preconditions_ok?
-          render :nothing => true, :status => :precondition_failed
-        else
-          segment.user_id = @user.id
-          segment.from_node = Node.find(segment.node_a.to_i)
-          segment.to_node = Node.find(segment.node_b.to_i)
-          segment.save_with_history!
-
-          render :text => segment.id.to_s, :content_type => "text/plain"
-        end
-      else
-        render :nothing => true, :status => :bad_request
-      end
-    else
-      render :nothing => true, :status => :method_not_allowed
-    end
-  end
-
-  def read
-    begin
-      segment = Segment.find(params[:id])
-
-      if segment.visible
-        render :text => segment.to_xml.to_s, :content_type => "text/xml"
-      else
-        render :nothing => true, :status => :gone
-      end
-    rescue ActiveRecord::RecordNotFound
-      render :nothing => true, :status => :not_found
-    end
-  end
-
-  def update
-    begin
-      segment = Segment.find(params[:id])
-
-      if segment.visible
-        new_segment = Segment.from_xml(request.raw_post)
-
-        if new_segment and new_segment.id == segment.id
-          if new_segment.node_a == new_segment.node_b
-            render :nothing => true, :status => :expectation_failed
-          elsif !new_segment.preconditions_ok?
-            render :nothing => true, :status => :precondition_failed
-          else
-            segment.user_id = @user.id
-            segment.node_a = new_segment.node_a
-            segment.node_b = new_segment.node_b
-            segment.tags = new_segment.tags
-            segment.visible = new_segment.visible
-            segment.save_with_history!
-
-            render :nothing => true
-          end
-        else
-          render :nothing => true, :status => :bad_request
-        end
-      else
-        render :nothing => true, :status => :gone
-      end
-    rescue ActiveRecord::RecordNotFound
-      render :nothing => true, :status => :not_found
-    end
-  end
-
-  def delete
-    begin
-      segment = Segment.find(params[:id])
-
-      if segment.visible
-        if WaySegment.find(:first, :joins => "INNER JOIN current_ways ON current_ways.id = current_way_segments.id", :conditions => [ "current_ways.visible = 1 AND current_way_segments.segment_id = ?", segment.id ])
-          render :nothing => true, :status => :precondition_failed
-        else
-          segment.user_id = @user.id
-          segment.visible = 0
-          segment.save_with_history!
-
-          render :nothing => true
-        end
-      else
-        render :nothing => true, :status => :gone
-      end
-    rescue ActiveRecord::RecordNotFound
-      render :nothing => true, :status => :not_found
-    end
-  end
-
-  def segments
-    ids = params['segments'].split(',').collect { |s| s.to_i }
-
-    if ids.length > 0
-      doc = OSM::API.new.get_xml_doc
-
-      Segment.find(ids).each do |segment|
-        doc.root << segment.to_xml_node
-      end 
-
-      render :text => doc.to_s, :content_type => "text/xml"
-    else
-      render :nothing => true, :status => :bad_request
-    end
-  end
-
-  def segments_for_node
-    segmentids = Segment.find(:all, :conditions => ['node_a = ? OR node_b = ?', params[:id], params[:id]]).collect { |s| s.id }.uniq
-
-    if segmentids.length > 0
-      doc = OSM::API.new.get_xml_doc
-
-      Segment.find(segmentids).each do |segment|
-        doc.root << segment.to_xml_node
-      end
-
-      render :text => doc.to_s, :content_type => "text/xml"
-    else
-      render :nothing => true, :status => :bad_request
-    end
-  end
-end
index 7482008..767220c 100644 (file)
@@ -6,7 +6,7 @@ class SiteController < ApplicationController
     way = Way.find(params[:id])
 
     begin
-      node = way.way_segments.first.segment.from_node
+      node = way.way_nodes.first.node
       redirect_to :controller => 'site', :action => 'index', :lat => node.latitude, :lon => node.longitude, :zoom => 6
     rescue
       redirect_to :back
index 2200ab6..6b29d15 100644 (file)
@@ -92,7 +92,7 @@ class SwfController < ApplicationController
                        sql="SELECT cn1.latitude AS lat1,cn1.longitude AS lon1,"+
                                "               cn2.latitude AS lat2,cn2.longitude AS lon2 "+
                                "  FROM current_segments "+
-                               "       LEFT OUTER JOIN current_way_segments"+
+                               "       LEFT OUTER JOIN current_way_nodes"+
                                "       ON segment_id=current_segments.id,"+
                                "       current_nodes AS cn1,current_nodes AS cn2"+
                                " WHERE (cn1.longitude BETWEEN #{xmin} AND #{xmax})"+
index 935ff3a..2e0623d 100644 (file)
@@ -54,7 +54,7 @@ class WayController < ApplicationController
           else
             way.user_id = @user.id
             way.tags = new_way.tags
-            way.segs = new_way.segs
+            way.nds = new_way.nds
             way.visible = true
             way.save_with_history!
 
@@ -76,18 +76,24 @@ class WayController < ApplicationController
       way = Way.find(params[:id])
 
       if way.visible
-        way.user_id = @user.id
-        way.tags = []
-        way.segs = []
-        way.visible = false
-        way.save_with_history!
+        if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='way' and member_id=?", params[:id]])
+          render :nothing => true, :status => :precondition_failed
+        else
+          way.user_id = @user.id
+          way.tags = []
+          way.nds = []
+          way.visible = false
+         way.save_with_history!
 
-        render :nothing => true
+         render :nothing => true
+        end
       else
         render :nothing => true, :status => :gone
       end
     rescue ActiveRecord::RecordNotFound
       render :nothing => true, :status => :not_found
+    rescue => ex
+      puts ex
     end
   end
 
@@ -96,23 +102,14 @@ class WayController < ApplicationController
       way = Way.find(params[:id])
 
       if way.visible
-        # In future, we might want to do all the data fetch in one step
-        seg_ids = way.segs + [-1]
-        segments = Segment.find_by_sql "select * from current_segments where visible = 1 and id IN (#{seg_ids.join(',')})"
-
-        node_ids = segments.collect {|segment| segment.node_a }
-        node_ids += segments.collect {|segment| segment.node_b }
-        node_ids += [-1]
-        nodes = Node.find(:all, :conditions => "visible = 1 AND id IN (#{node_ids.join(',')})")
+        nd_ids = way.nds + [-1]
+        nodes = Node.find(:all, :conditions => "visible = 1 AND id IN (#{nd_ids.join(',')})")
 
         # Render
         doc = OSM::API.new.get_xml_doc
         nodes.each do |node|
           doc.root << node.to_xml_node()
         end
-        segments.each do |segment|
-          doc.root << segment.to_xml_node()
-        end
         doc.root << way.to_xml_node()
 
         render :text => doc.to_s, :content_type => "text/xml"
@@ -144,8 +141,8 @@ class WayController < ApplicationController
     end
   end
 
-  def ways_for_segment
-    wayids = WaySegment.find(:all, :conditions => ['segment_id = ?', params[:id]]).collect { |ws| ws.id }.uniq
+  def ways_for_node
+    wayids = WayNode.find(:all, :conditions => ['node_id = ?', params[:id]]).collect { |ws| ws.id }.uniq
 
     if wayids.length > 0
       doc = OSM::API.new.get_xml_doc
diff --git a/app/controllers/way_node_controller.rb b/app/controllers/way_node_controller.rb
new file mode 100644 (file)
index 0000000..41549ed
--- /dev/null
@@ -0,0 +1,5 @@
+class WayNodeController < ApplicationController
+
+
+
+end
diff --git a/app/controllers/way_segment_controller.rb b/app/controllers/way_segment_controller.rb
deleted file mode 100644 (file)
index e37303f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-class WaySegmentController < ApplicationController
-
-
-
-end
diff --git a/app/helpers/old_segment_helper.rb b/app/helpers/old_segment_helper.rb
deleted file mode 100644 (file)
index 3650f0a..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-module OldSegmentHelper
-end
diff --git a/app/helpers/old_way_node_helper.rb b/app/helpers/old_way_node_helper.rb
new file mode 100644 (file)
index 0000000..933d661
--- /dev/null
@@ -0,0 +1,2 @@
+module OldWayNodeHelper
+end
diff --git a/app/helpers/old_way_segment_helper.rb b/app/helpers/old_way_segment_helper.rb
deleted file mode 100644 (file)
index 8299bdd..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-module OldWaySegmentHelper
-end
diff --git a/app/helpers/segment_helper.rb b/app/helpers/segment_helper.rb
deleted file mode 100644 (file)
index d6be137..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-module SegmentHelper
-end
diff --git a/app/helpers/way_node_helper.rb b/app/helpers/way_node_helper.rb
new file mode 100644 (file)
index 0000000..884dbcc
--- /dev/null
@@ -0,0 +1,2 @@
+module WayNodeHelper
+end
diff --git a/app/helpers/way_segment_helper.rb b/app/helpers/way_segment_helper.rb
deleted file mode 100644 (file)
index f2ebd39..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-module WaySegmentHelper
-end
diff --git a/app/models/old_relation.rb b/app/models/old_relation.rb
new file mode 100644 (file)
index 0000000..6da7814
--- /dev/null
@@ -0,0 +1,111 @@
+class OldRelation < ActiveRecord::Base
+  set_table_name 'relations'
+
+  belongs_to :user
+
+  def self.from_relation(relation)
+    old_relation = OldRelation.new
+    old_relation.visible = relation.visible
+    old_relation.user_id = relation.user_id
+    old_relation.timestamp = relation.timestamp
+    old_relation.id = relation.id
+    old_relation.members = relation.members
+    old_relation.tags = relation.tags
+    return old_relation
+  end
+
+  def save_with_dependencies!
+
+    # see comment in old_way.rb ;-)
+    save!
+    clear_aggregation_cache
+    clear_association_cache
+    @attributes.update(OldRelation.find(:first, :conditions => ['id = ? AND timestamp = ?', self.id, self.timestamp]).instance_variable_get('@attributes'))
+
+    # ok, you can touch from here on
+
+    self.tags.each do |k,v|
+      tag = OldRelationTag.new
+      tag.k = k
+      tag.v = v
+      tag.id = self.id
+      tag.version = self.version
+      tag.save!
+    end
+
+    i = 1
+    self.members.each do |m|
+      member = OldRelationMember.new
+      member.id = self.id
+      member.member_type = m[0]
+      member.member_id = m[1]
+      member.member_role = m[2]
+      member.version = self.version
+      member.save!
+    end
+  end
+
+  def members
+    unless @members
+        @members = Array.new
+        OldRelationMember.find(:all, :conditions => ["id = ? AND version = ?", self.id, self.version]).each do |m|
+            @members += [[m.type,m.id,m.role]]
+        end
+    end
+    @members
+  end
+
+  def tags
+    unless @tags
+        @tags = Hash.new
+        OldRelationTag.find(:all, :conditions => ["id = ? AND version = ?", self.id, self.version]).each do |tag|
+            @tags[tag.k] = tag.v
+        end
+    end
+    @tags = Hash.new unless @tags
+    @tags
+  end
+
+  def members=(s)
+    @members = s
+  end
+
+  def tags=(t)
+    @tags = t
+  end
+
+#  has_many :relation_segments, :class_name => 'OldRelationSegment', :foreign_key => 'id'
+#  has_many :relation_tags, :class_name => 'OldRelationTag', :foreign_key => 'id'
+
+  def old_members
+    OldRelationMember.find(:all, :conditions => ['id = ? AND version = ?', self.id, self.version])    
+  end
+
+  def old_tags
+    OldRelationTag.find(:all, :conditions => ['id = ? AND version = ?', self.id, self.version])    
+  end
+
+  def to_xml_node
+    el1 = XML::Node.new 'relation'
+    el1['id'] = self.id.to_s
+    el1['visible'] = self.visible.to_s
+    el1['timestamp'] = self.timestamp.xmlschema
+    el1['user'] = self.user.display_name if self.user.data_public?
+    
+    self.old_members.each do |member|
+      e = XML::Node.new 'member'
+      e['type'] = member.member_type.to_s
+      e['ref'] = member.member_id.to_s # "id" is considered uncool here as it should be unique in XML
+      e['role'] = member.member_role.to_s
+      el1 << e
+    end
+    self.old_tags.each do |tag|
+      e = XML::Node.new 'tag'
+      e['k'] = tag.k
+      e['v'] = tag.v
+      el1 << e
+    end
+    return el1
+  end 
+end
diff --git a/app/models/old_relation_member.rb b/app/models/old_relation_member.rb
new file mode 100644 (file)
index 0000000..9e2cce4
--- /dev/null
@@ -0,0 +1,6 @@
+class OldRelationMember < ActiveRecord::Base
+  belongs_to :user
+
+  set_table_name 'relation_members'
+
+end
diff --git a/app/models/old_relation_tag.rb b/app/models/old_relation_tag.rb
new file mode 100644 (file)
index 0000000..6ad4b45
--- /dev/null
@@ -0,0 +1,6 @@
+class OldRelationTag < ActiveRecord::Base
+  belongs_to :user
+
+  set_table_name 'relation_tags'
+
+end
diff --git a/app/models/old_segment.rb b/app/models/old_segment.rb
deleted file mode 100644 (file)
index c243c52..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-class OldSegment < ActiveRecord::Base
-  set_table_name 'segments'
-
-  validates_presence_of :user_id, :timestamp
-  validates_inclusion_of :visible, :in => [ true, false ]
-  validates_numericality_of :node_a, :node_b
-
-  belongs_to :user
-
-  def self.from_segment(segment)
-    old_segment = OldSegment.new
-    old_segment.node_a = segment.node_a
-    old_segment.node_b = segment.node_b
-    old_segment.visible = segment.visible
-    old_segment.tags = segment.tags
-    old_segment.timestamp = segment.timestamp
-    old_segment.user_id = segment.user_id
-    old_segment.id = segment.id
-    return old_segment
-  end
-
-  def to_xml_node
-    el1 = XML::Node.new 'segment'
-    el1['id'] = self.id.to_s
-    el1['from'] = self.node_a.to_s
-    el1['to'] = self.node_b.to_s
-    el1['user'] = self.user.display_name if self.user.data_public?
-    Segment.split_tags(el1, self.tags)
-    el1['visible'] = self.visible.to_s
-    el1['timestamp'] = self.timestamp.xmlschema
-    return el1
-  end
-end
index e4df258..bb267df 100644 (file)
@@ -9,7 +9,7 @@ class OldWay < ActiveRecord::Base
     old_way.user_id = way.user_id
     old_way.timestamp = way.timestamp
     old_way.id = way.id
-    old_way.segs = way.segs
+    old_way.nds = way.nds
     old_way.tags = way.tags
     return old_way
   end
@@ -39,23 +39,25 @@ class OldWay < ActiveRecord::Base
     end
 
     i = 1
-    self.segs.each do |n|
-      seg = OldWaySegment.new
-      seg.id = self.id
-      seg.segment_id = n
-      seg.version = self.version
-      seg.save!
+    self.nds.each do |n|
+      nd = OldWayNode.new
+      nd.id = self.id
+      nd.node_id = n
+      nd.sequence_id = i
+      nd.version = self.version
+      nd.save!
+      i += 1
     end
   end
 
-  def segs
-    unless @segs
-        @segs = Array.new
-        OldWaySegment.find(:all, :conditions => ["id = ? AND version = ?", self.id, self.version], :order => "sequence_id").each do |seg|
-            @segs += [seg.segment_id]
+  def nds
+    unless @nds
+        @nds = Array.new
+        OldWayNode.find(:all, :conditions => ["id = ? AND version = ?", self.id, self.version], :order => "sequence_id").each do |nd|
+            @nds += [nd.node_id]
         end
     end
-    @segs
+    @nds
   end
 
   def tags
@@ -69,19 +71,19 @@ class OldWay < ActiveRecord::Base
     @tags
   end
 
-  def segs=(s)
-    @segs = s
+  def nds=(s)
+    @nds = s
   end
 
   def tags=(t)
     @tags = t
   end
 
-#  has_many :way_segments, :class_name => 'OldWaySegment', :foreign_key => 'id'
+#  has_many :way_nodes, :class_name => 'OldWayNode', :foreign_key => 'id'
 #  has_many :way_tags, :class_name => 'OldWayTag', :foreign_key => 'id'
 
-  def old_segments
-    OldWaySegment.find(:all, :conditions => ['id = ? AND version = ?', self.id, self.version])    
+  def old_nodes
+    OldWayNode.find(:all, :conditions => ['id = ? AND version = ?', self.id, self.version])    
   end
 
   def old_tags
@@ -95,9 +97,9 @@ class OldWay < ActiveRecord::Base
     el1['timestamp'] = self.timestamp.xmlschema
     el1['user'] = self.user.display_name if self.user.data_public?
     
-    self.old_segments.each do |seg| # FIXME need to make sure they come back in the right order
-      e = XML::Node.new 'seg'
-      e['id'] = seg.segment_id.to_s
+    self.old_nodes.each do |nd| # FIXME need to make sure they come back in the right order
+      e = XML::Node.new 'nd'
+      e['id'] = nd.node_id.to_s
       el1 << e
     end
  
diff --git a/app/models/old_way_node.rb b/app/models/old_way_node.rb
new file mode 100644 (file)
index 0000000..5260ee6
--- /dev/null
@@ -0,0 +1,6 @@
+class OldWayNode < ActiveRecord::Base
+  belongs_to :user
+
+  set_table_name 'way_nodes'
+
+end
diff --git a/app/models/old_way_segment.rb b/app/models/old_way_segment.rb
deleted file mode 100644 (file)
index 45d1ce0..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-class OldWaySegment < ActiveRecord::Base
-  belongs_to :user
-
-  set_table_name 'way_segments'
-
-end
diff --git a/app/models/relation.rb b/app/models/relation.rb
new file mode 100644 (file)
index 0000000..a5d463f
--- /dev/null
@@ -0,0 +1,207 @@
+class Relation < ActiveRecord::Base
+  require 'xml/libxml'
+  
+  belongs_to :user
+
+  has_many :relation_members, :foreign_key => 'id'
+  has_many :relation_tags, :foreign_key => 'id'
+
+  has_many :old_relations, :foreign_key => 'id', :order => 'version'
+
+  set_table_name 'current_relations'
+
+  def self.from_xml(xml, create=false)
+    begin
+      p = XML::Parser.new
+      p.string = xml
+      doc = p.parse
+
+      relation = Relation.new
+
+      doc.find('//osm/relation').each do |pt|
+        if !create and pt['id'] != '0'
+          relation.id = pt['id'].to_i
+        end
+
+        if create
+          relation.timestamp = Time.now
+          relation.visible = true
+        else
+          if pt['timestamp']
+            relation.timestamp = Time.parse(pt['timestamp'])
+          end
+        end
+
+        pt.find('tag').each do |tag|
+          relation.add_tag_keyval(tag['k'], tag['v'])
+        end
+
+        pt.find('member').each do |member|
+          relation.add_member(member['type'], member['ref'], member['role'])
+        end
+      end
+    rescue
+      relation = nil
+    end
+
+    return relation
+  end
+
+  def to_xml
+    doc = OSM::API.new.get_xml_doc
+    doc.root << to_xml_node()
+    return doc
+  end
+
+  def to_xml_node(user_display_name_cache = nil)
+    el1 = XML::Node.new 'relation'
+    el1['id'] = self.id.to_s
+    el1['visible'] = self.visible.to_s
+    el1['timestamp'] = self.timestamp.xmlschema
+
+    user_display_name_cache = {} if user_display_name_cache.nil?
+    
+    if user_display_name_cache and user_display_name_cache.key?(self.user_id)
+      # use the cache if available
+    elsif self.user.data_public?
+      user_display_name_cache[self.user_id] = self.user.display_name
+    else
+      user_display_name_cache[self.user_id] = nil
+    end
+
+    el1['user'] = user_display_name_cache[self.user_id] unless user_display_name_cache[self.user_id].nil?
+
+    self.relation_members.each do |member|
+      p=0
+      #if visible_members
+      #  # if there is a list of visible members then use that to weed out deleted segments
+      #  if visible_members[member.member_type][member.member_id]
+      #    p=1
+      #  end
+      #else
+        # otherwise, manually go to the db to check things
+        if member.member.visible?
+          p=1
+        end
+      #end
+      if p
+        e = XML::Node.new 'member'
+        e['type'] = member.member_type
+        e['ref'] = member.member_id.to_s 
+        e['role'] = member.member_role
+        el1 << e
+       end
+    end
+
+    self.relation_tags.each do |tag|
+      e = XML::Node.new 'tag'
+      e['k'] = tag.k
+      e['v'] = tag.v
+      el1 << e
+    end
+    return el1
+  end 
+
+  # FIXME is this really needed?
+  def members
+    unless @members
+        @members = Array.new
+        self.relation_members.each do |member|
+            @members += [[member.member_type,member.member_id,member.member_role]]
+        end
+    end
+    @members
+  end
+
+  def tags
+    unless @tags
+        @tags = Hash.new
+        self.relation_tags.each do |tag|
+            @tags[tag.k] = tag.v
+        end
+    end
+    @tags
+  end
+
+  def members=(m)
+    @members = m
+  end
+
+  def tags=(t)
+    @tags = t
+  end
+
+  def add_member(type,id,role)
+    @members = Array.new unless @members
+    @members += [[type,id,role]]
+  end
+
+  def add_tag_keyval(k, v)
+    @tags = Hash.new unless @tags
+    @tags[k] = v
+  end
+
+  def save_with_history!
+    Relation.transaction do
+      t = Time.now
+      self.timestamp = t
+      self.save!
+
+      tags = self.tags
+
+      RelationTag.delete_all(['id = ?', self.id])
+
+      tags.each do |k,v|
+       tag = RelationTag.new
+       tag.k = k
+       tag.v = v
+       tag.id = self.id
+       tag.save!
+      end
+
+      members = self.members
+
+      RelationMember.delete_all(['id = ?', self.id])
+
+      members.each do |n|
+       mem = RelationMember.new
+       mem.id = self.id
+       mem.member_type = n[0];
+       mem.member_id = n[1];
+       mem.member_role = n[2];
+       mem.save!
+      end
+
+      old_relation = OldRelation.from_relation(self)
+      old_relation.timestamp = t
+      old_relation.save_with_dependencies!
+    end
+  end
+
+  def preconditions_ok?
+    self.members.each do |m|
+      if (m[0] == "node")
+        n = Node.find(:first, :conditions => ["id = ?", m[1]])
+        unless n and n.visible 
+          return false
+        end
+      elsif (m[0] == "way")
+        w = Way.find(:first, :conditions => ["id = ?", m[1]])
+        unless w and w.visible and w.preconditions_ok?
+          return false
+        end
+      elsif (m[0] == "relation")
+        e = Relation.find(:first, :conditions => ["id = ?", m[1]])
+        unless e and e.visible and e.preconditions_ok?
+          return false
+        end
+      else
+        return false
+      end
+    end
+    return true
+  rescue
+    return false
+  end
+
+end
diff --git a/app/models/relation_member.rb b/app/models/relation_member.rb
new file mode 100644 (file)
index 0000000..7910285
--- /dev/null
@@ -0,0 +1,30 @@
+class RelationMember < ActiveRecord::Base
+  set_table_name 'current_relation_members'
+
+  # problem with RelationMember is that it may link to any one 
+  # object (a node, a way, another relation), and belongs_to is
+  # not flexible enough for that. So we do this, which is ugly,
+  # but fortunately rails won't actually run the SQL behind that
+  # unless someone really accesses .node, .way, or
+  # .relation - which is what we do below based on member_type.
+  # (and no: the :condition on belongs_to doesn't work here as
+  # it is a condition on the *referenced* object not the 
+  # *referencing* object!)
+  
+  belongs_to :node, :foreign_key => "member_id"
+  belongs_to :way, :foreign_key => "member_id"
+  belongs_to :relation, :foreign_key => "member_id"
+
+  # so we define this "member" function that returns whatever it
+  # is.
+  def member()
+    return (member_type == "node") ? node : (member_type == "way") ? way : relation
+  end
+
+  # NOTE - relations are SUBJECTS of memberships. The fact that nodes, 
+  # ways, and relations can be the OBJECT of a membership,
+  # i.e. a node/way/relation can be referenced throgh a
+  # RelationMember object, is NOT modelled in rails, i.e. these links
+  # have to be resolved manually, on demand. 
+end
diff --git a/app/models/relation_tag.rb b/app/models/relation_tag.rb
new file mode 100644 (file)
index 0000000..939165e
--- /dev/null
@@ -0,0 +1,6 @@
+class RelationTag < ActiveRecord::Base
+  set_table_name 'current_relation_tags'
+
+  belongs_to :relation, :foreign_key => 'id'
+
+end
diff --git a/app/models/segment.rb b/app/models/segment.rb
deleted file mode 100644 (file)
index 6916b79..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-class Segment < ActiveRecord::Base
-  require 'xml/libxml'
-  set_table_name 'current_segments'
-
-  validates_presence_of :user_id, :timestamp
-  validates_inclusion_of :visible, :in => [ true, false ]
-  validates_numericality_of :node_a, :node_b
-
-  has_many :old_segments, :foreign_key => :id
-  belongs_to :user
-
-  # using belongs_to :foreign_key = 'node_*', since if use has_one :foreign_key = 'id', segment preconditions? fails checking for segment id in node table
-  belongs_to :from_node, :class_name => 'Node', :foreign_key => 'node_a'
-  belongs_to :to_node, :class_name => 'Node', :foreign_key => 'node_b'
-
-  def self.from_xml(xml, create=false)
-    begin
-      p = XML::Parser.new
-      p.string = xml
-      doc = p.parse
-
-      segment = Segment.new
-
-      doc.find('//osm/segment').each do |pt|
-        segment.node_a = pt['from'].to_i
-        segment.node_b = pt['to'].to_i
-
-        unless create
-          if pt['id'] != '0'
-            segment.id = pt['id'].to_i
-          end
-        end
-
-        segment.visible = true
-
-        if create
-          segment.timestamp = Time.now
-        else
-          if pt['timestamp']
-            segment.timestamp = Time.parse(pt['timestamp'])
-          end
-        end
-
-        tags = []
-
-        pt.find('tag').each do |tag|
-          tags << [tag['k'],tag['v']]
-        end
-
-        tags = tags.collect { |k,v| "#{k}=#{v}" }.join(';')
-        tags = '' if tags.nil?
-
-        segment.tags = tags
-      end
-    rescue
-      segment = nil
-    end
-
-    return segment
-  end
-
-  def save_with_history!
-    Segment.transaction do
-      self.timestamp = Time.now
-      self.save!
-      old_segment = OldSegment.from_segment(self)
-      old_segment.save!
-    end
-  end
-
-  def to_xml
-    doc = OSM::API.new.get_xml_doc
-    doc.root << to_xml_node()
-    return doc
-  end
-
-  def to_xml_node(user_display_name_cache  = nil)
-    el1 = XML::Node.new 'segment'
-    el1['id'] = self.id.to_s
-    el1['from'] = self.node_a.to_s
-    el1['to'] = self.node_b.to_s
-
-    user_display_name_cache = {} if user_display_name_cache.nil?
-
-    if user_display_name_cache and user_display_name_cache.key?(self.user_id)
-      # use the cache if available
-    elsif self.user.data_public?
-      user_display_name_cache[self.user_id] = self.user.display_name
-    else
-      user_display_name_cache[self.user_id] = nil
-    end
-
-    el1['user'] = user_display_name_cache[self.user_id] unless user_display_name_cache[self.user_id].nil?
-
-    Segment.split_tags(el1, self.tags)
-    el1['visible'] = self.visible.to_s
-    el1['timestamp'] = self.timestamp.xmlschema
-    return el1
-  end
-
-  def self.split_tags(el, tags)
-    tags.split(';').each do |tag|
-      parts = tag.split('=')
-      key = ''
-      val = ''
-      key = parts[0].strip unless parts[0].nil?
-      val = parts[1].strip unless parts[1].nil?
-      if key != '' && val != ''
-        el2 = XML::Node.new('tag')
-        el2['k'] = key.to_s
-        el2['v'] = val.to_s
-        el << el2
-      end
-    end
-  end
-
-  def preconditions_ok?
-    from_node and from_node.visible and to_node and to_node.visible
-  end
-
-end
index c651192..e0a4450 100644 (file)
@@ -3,7 +3,7 @@ class Way < ActiveRecord::Base
   
   belongs_to :user
 
-  has_many :way_segments, :foreign_key => 'id', :order => 'sequence_id'
+  has_many :way_nodes, :foreign_key => 'id', :order => 'sequence_id'
   has_many :way_tags, :foreign_key => 'id'
 
   has_many :old_ways, :foreign_key => 'id', :order => 'version'
@@ -36,8 +36,8 @@ class Way < ActiveRecord::Base
           way.add_tag_keyval(tag['k'], tag['v'])
         end
 
-        pt.find('seg').each do |seg|
-          way.add_seg_num(seg['id'])
+        pt.find('nd').each do |nd|
+          way.add_nd_num(nd['ref'])
         end
       end
     rescue
@@ -53,7 +53,7 @@ class Way < ActiveRecord::Base
     return doc
   end
 
-  def to_xml_node(visible_segments = nil, user_display_name_cache = nil)
+  def to_xml_node(visible_nodes = nil, user_display_name_cache = nil)
     el1 = XML::Node.new 'way'
     el1['id'] = self.id.to_s
     el1['visible'] = self.visible.to_s
@@ -71,26 +71,26 @@ class Way < ActiveRecord::Base
 
     el1['user'] = user_display_name_cache[self.user_id] unless user_display_name_cache[self.user_id].nil?
 
-    # make sure segments are output in sequence_id order
-    ordered_segments = []
-    self.way_segments.each do |seg|
-      if visible_segments
-        # if there is a list of visible segments then use that to weed out deleted segments
-        if visible_segments[seg.segment_id]
-          ordered_segments[seg.sequence_id] = seg.segment_id.to_s
+    # make sure nodes are output in sequence_id order
+    ordered_nodes = []
+    self.way_nodes.each do |nd|
+      if visible_nodes
+        # if there is a list of visible nodes then use that to weed out deleted nodes
+        if visible_nodes[nd.node_id]
+          ordered_nodes[nd.sequence_id] = nd.node_id.to_s
         end
       else
         # otherwise, manually go to the db to check things
-        if seg.segment.visible? and seg.segment.from_node.visible? and seg.segment.to_node.visible?
-          ordered_segments[seg.sequence_id] = seg.segment_id.to_s
+        if nd.node.visible? and nd.node.visible?
+          ordered_nodes[nd.sequence_id] = nd.node_id.to_s
         end
       end
     end
 
-    ordered_segments.each do |seg_id|
-      if seg_id and seg_id != '0'
-        e = XML::Node.new 'seg'
-        e['id'] = seg_id
+    ordered_nodes.each do |nd_id|
+      if nd_id and nd_id != '0'
+        e = XML::Node.new 'nd'
+        e['ref'] = nd_id
         el1 << e
       end
     end
@@ -104,14 +104,14 @@ class Way < ActiveRecord::Base
     return el1
   end 
 
-  def segs
-    unless @segs
-        @segs = Array.new
-        self.way_segments.each do |seg|
-            @segs += [seg.segment_id]
+  def nds
+    unless @nds
+        @nds = Array.new
+        self.way_nodes.each do |nd|
+            @nds += [nd.node_id]
         end
     end
-    @segs
+    @nds
   end
 
   def tags
@@ -124,17 +124,17 @@ class Way < ActiveRecord::Base
     @tags
   end
 
-  def segs=(s)
-    @segs = s
+  def nds=(s)
+    @nds = s
   end
 
   def tags=(t)
     @tags = t
   end
 
-  def add_seg_num(n)
-    @segs = Array.new unless @segs
-    @segs << n.to_i
+  def add_nd_num(n)
+    @nds = Array.new unless @nds
+    @nds << n.to_i
   end
 
   def add_tag_keyval(k, v)
@@ -164,18 +164,18 @@ class Way < ActiveRecord::Base
       end
     end
 
-    WaySegment.transaction do
-      segs = self.segs
+    WayNode.transaction do
+      nds = self.nds
 
-      WaySegment.delete_all(['id = ?', self.id])
+      WayNode.delete_all(['id = ?', self.id])
 
       i = 1
-      segs.each do |n|
-        seg = WaySegment.new
-        seg.id = self.id
-        seg.segment_id = n
-        seg.sequence_id = i
-        seg.save!
+      nds.each do |n|
+        nd = WayNode.new
+        nd.id = self.id
+        nd.node_id = n
+        nd.sequence_id = i
+        nd.save!
         i += 1
       end
     end
@@ -186,10 +186,10 @@ class Way < ActiveRecord::Base
   end
 
   def preconditions_ok?
-    return false if self.segs.empty?
-    self.segs.each do |n|
-      segment = Segment.find(:first, :conditions => ["id = ?", n])
-      unless segment and segment.visible and segment.preconditions_ok?
+    return false if self.nds.empty?
+    self.nds.each do |n|
+      node = Node.find(:first, :conditions => ["id = ?", n])
+      unless node and node.visible
         return false
       end
     end
diff --git a/app/models/way_node.rb b/app/models/way_node.rb
new file mode 100644 (file)
index 0000000..06515fc
--- /dev/null
@@ -0,0 +1,5 @@
+class WayNode < ActiveRecord::Base
+  set_table_name 'current_way_nodes'
+
+  belongs_to :node
+end
diff --git a/app/models/way_segment.rb b/app/models/way_segment.rb
deleted file mode 100644 (file)
index bc81f6b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-class WaySegment < ActiveRecord::Base
-  set_table_name 'current_way_segments'
-
-  belongs_to :segment
-end
index 003a7b8..3820ed2 100644 (file)
@@ -17,7 +17,7 @@ RAILS_GEM_VERSION = '1.2.3'
 require File.join(File.dirname(__FILE__), 'boot')
 
 # Application constants needed for routes.rb - must go before Initializer call
-API_VERSION = ENV['OSM_API_VERSION'] || '0.4'
+API_VERSION = ENV['OSM_API_VERSION'] || '0.5'
 
 # Custom logger class to format messages sensibly
 class OSMLogger < Logger
index b9d5d41..d8fb6b9 100644 (file)
@@ -2,21 +2,13 @@ ActionController::Routing::Routes.draw do |map|
 
   # API
   map.connect "api/#{API_VERSION}/node/create", :controller => 'node', :action => 'create'
-  map.connect "api/#{API_VERSION}/node/:id/segments", :controller => 'segment', :action => 'segments_for_node', :id => /\d+/
+  map.connect "api/#{API_VERSION}/node/:id/ways", :controller => 'way', :action => 'ways_for_node', :id => /\d+/
   map.connect "api/#{API_VERSION}/node/:id/history", :controller => 'old_node', :action => 'history', :id => /\d+/
   map.connect "api/#{API_VERSION}/node/:id", :controller => 'node', :action => 'read', :id => /\d+/, :conditions => { :method => :get }
   map.connect "api/#{API_VERSION}/node/:id", :controller => 'node', :action => 'update', :id => /\d+/, :conditions => { :method => :put }
   map.connect "api/#{API_VERSION}/node/:id", :controller => 'node', :action => 'delete', :id => /\d+/, :conditions => { :method => :delete }
   map.connect "api/#{API_VERSION}/nodes", :controller => 'node', :action => 'nodes', :id => nil
   
-  map.connect "api/#{API_VERSION}/segment/create", :controller => 'segment', :action => 'create'
-  map.connect "api/#{API_VERSION}/segment/:id/ways", :controller => 'way', :action => 'ways_for_segment', :id => /\d+/
-  map.connect "api/#{API_VERSION}/segment/:id/history", :controller => 'old_segment', :action => 'history', :id => /\d+/
-  map.connect "api/#{API_VERSION}/segment/:id", :controller => 'segment', :action => 'read', :id => /\d+/, :conditions => { :method => :get }
-  map.connect "api/#{API_VERSION}/segment/:id", :controller => 'segment', :action => 'update', :id => /\d+/, :conditions => { :method => :put }
-  map.connect "api/#{API_VERSION}/segment/:id", :controller => 'segment', :action => 'delete', :id => /\d+/, :conditions => { :method => :delete }
-  map.connect "api/#{API_VERSION}/segments", :controller => 'segment', :action => 'segments', :id => nil
-  
   map.connect "api/#{API_VERSION}/way/create", :controller => 'way', :action => 'create'
   map.connect "api/#{API_VERSION}/way/:id/history", :controller => 'old_way', :action => 'history', :id => /\d+/
   map.connect "api/#{API_VERSION}/way/:id/full", :controller => 'way', :action => 'full', :id => /\d+/
@@ -26,6 +18,13 @@ ActionController::Routing::Routes.draw do |map|
   map.connect "api/#{API_VERSION}/ways", :controller => 'way', :action => 'ways', :id => nil
 
   map.connect "api/#{API_VERSION}/capabilities", :controller => 'api', :action => 'capabilities'
+  map.connect "api/#{API_VERSION}/relation/create", :controller => 'relation', :action => 'create'
+  map.connect "api/#{API_VERSION}/relation/:id/history", :controller => 'old_relation', :action => 'history', :id => /\d+/
+  map.connect "api/#{API_VERSION}/relation/:id/full", :controller => 'relation', :action => 'full', :id => /\d+/
+  map.connect "api/#{API_VERSION}/relation/:id", :controller => 'relation', :action => 'read', :id => /\d+/, :conditions => { :method => :get }
+  map.connect "api/#{API_VERSION}/relation/:id", :controller => 'relation', :action => 'update', :id => /\d+/, :conditions => { :method => :put }
+  map.connect "api/#{API_VERSION}/relation/:id", :controller => 'relation', :action => 'delete', :id => /\d+/, :conditions => { :method => :delete }
+  map.connect "api/#{API_VERSION}/relations", :controller => 'relation', :action => 'relations', :id => nil
 
   map.connect "api/#{API_VERSION}/map", :controller => 'api', :action => 'map'
   
@@ -33,7 +32,6 @@ ActionController::Routing::Routes.draw do |map|
   
   map.connect "api/#{API_VERSION}/search", :controller => 'search', :action => 'search_all'
   map.connect "api/#{API_VERSION}/ways/search", :controller => 'search', :action => 'search_ways'
-  map.connect "api/#{API_VERSION}/segments/search", :controller => 'search', :action => 'search_segments'
   map.connect "api/#{API_VERSION}/nodes/search", :controller => 'search', :action => 'search_nodes'
   
   map.connect "api/#{API_VERSION}/user/details", :controller => 'user', :action => 'api_details'
diff --git a/db/migrate/006_add_relations.rb b/db/migrate/006_add_relations.rb
new file mode 100644 (file)
index 0000000..a30642e
--- /dev/null
@@ -0,0 +1,83 @@
+class AddRelations < ActiveRecord::Migration
+  def self.up
+    # a relation can have members much like a way can have nodes.
+    # differences:
+    # way: only nodes / relation: any kind of member
+    # way: ordered sequence of nodes / relation: free-form "role" string
+    create_table "current_relation_members", innodb_table do |t|
+      t.column "id",          :bigint, :limit => 64, :null => false
+      t.column "member_type", :string, :limit => 11, :null => false
+      t.column "member_id",   :bigint, :limit => 11, :null => false
+      t.column "member_role", :string
+    end
+    # enums work like strings but are more efficient
+    execute "alter table current_relation_members change column member_type member_type enum('node','way','relation');"
+
+    add_primary_key "current_relation_members", ["id", "member_type", "member_id", "member_role"]
+    add_index "current_relation_members", ["member_type", "member_id"], :name => "current_relation_members_member_idx"
+    # the following is obsolete given the primary key, is it not?
+    # add_index "current_relation_members", ["id"], :name => "current_relation_members_id_idx"
+    create_table "current_relation_tags", myisam_table do |t|
+      t.column "id", :bigint, :limit => 64, :null => false
+      t.column "k",  :string, :default => "", :null => false
+      t.column "v",  :string, :default => "", :null => false
+    end
+
+    add_index "current_relation_tags", ["id"], :name => "current_relation_tags_id_idx"
+    execute "CREATE FULLTEXT INDEX `current_relation_tags_v_idx` ON `current_relation_tags` (`v`)"
+
+    create_table "current_relations", innodb_table do |t|
+      t.column "id",        :bigint,   :limit => 64, :null => false
+      t.column "user_id",   :bigint,   :limit => 20, :null => false
+      t.column "timestamp", :datetime, :null => false
+      t.column "visible",   :boolean,  :null => false
+    end
+
+    add_primary_key "current_relations", ["id"]
+    change_column "current_relations", "id", :bigint, :limit => 64, :null => false, :options => "AUTO_INCREMENT"
+
+    create_table "relation_members", myisam_table do |t|
+      t.column "id",          :bigint,  :limit => 64, :default => 0, :null => false
+      t.column "member_type", :string, :limit => 11, :null => false
+      t.column "member_id",   :bigint, :limit => 11, :null => false
+      t.column "member_role", :string
+      t.column "version",     :bigint,  :limit => 20, :default => 0, :null => false
+    end
+
+    execute "alter table relation_members change column member_type member_type enum('node','way','relation');" 
+    add_primary_key "relation_members", ["id", "version", "member_type", "member_id", "member_role"]
+    add_index "relation_members", ["member_type", "member_id"], :name => "relation_members_member_idx"
+
+    create_table "relation_tags", myisam_table do |t|
+      t.column "id",      :bigint,  :limit => 64, :default => 0, :null => false
+      t.column "k",       :string, :null => false, :default => ""
+      t.column "v",       :string, :null => false, :default => ""
+      t.column "version", :bigint,  :limit => 20, :null => false
+    end
+
+    add_index "relation_tags", ["id", "version"], :name => "relation_tags_id_version_idx"
+
+    create_table "relations", myisam_table do |t|
+      t.column "id",        :bigint,   :limit => 64, :null => false, :default => 0
+      t.column "user_id",   :bigint,   :limit => 20, :null => false
+      t.column "timestamp", :datetime,               :null => false
+      t.column "version",   :bigint,   :limit => 20, :null => false
+      t.column "visible",   :boolean,                :null => false, :default => true
+    end
+
+    add_primary_key "relations", ["id", "version"]
+    add_index "relations", ["timestamp"], :name => "relations_timestamp_idx"
+    
+    change_column "relations", "version", :bigint, :limit => 20, :null => false, :options => "AUTO_INCREMENT"
+  end
+
+
+  def self.down
+    drop_table :relations
+    drop_table :current_relations
+    drop_table :relation_tags
+    drop_table :current_relation_tags
+    drop_table :relation_members
+    drop_table :current_relation_members
+  end
+end
diff --git a/db/migrate/007_remove_segments.rb b/db/migrate/007_remove_segments.rb
new file mode 100644 (file)
index 0000000..492beda
--- /dev/null
@@ -0,0 +1,89 @@
+require 'lib/migrate'
+
+class RemoveSegments < ActiveRecord::Migration
+  def self.up
+    have_segs = select_value("SELECT count(*) FROM current_segments").to_i != 0
+
+    if have_segs
+      prefix = File.join Dir.tmpdir, "007_remove_segments.#{$$}."
+
+      cmd = "db/migrate/007_remove_segments_helper"
+      src = "#{cmd}.cc"
+      if not File.exists? cmd or File.mtime(cmd) < File.mtime(src) then 
+       system 'c++ -O3 -Wall `mysql_config --cflags --libs` ' +
+         "#{src} -o #{cmd}" or fail
+      end
+
+      conn_opts = ActiveRecord::Base.connection.
+       instance_eval { @connection_options }
+      args = conn_opts.map { |arg| arg.to_s } + [prefix]
+      fail "#{cmd} failed" unless system cmd, *args
+
+      tempfiles = ['ways', 'way_nodes', 'way_tags',
+       'relations', 'relation_members', 'relation_tags'].
+       map { |base| prefix + base }
+      ways, way_nodes, way_tags,
+       relations, relation_members, relation_tags = tempfiles
+    end
+
+    drop_table :segments
+    drop_table :way_segments
+    create_table :way_nodes, myisam_table do |t|
+      t.column :id,          :bigint, :limit => 64, :null => false
+      t.column :node_id,     :bigint, :limit => 64, :null => false
+      t.column :version,     :bigint, :limit => 20, :null => false
+      t.column :sequence_id, :bigint, :limit => 11, :null => false
+    end
+    add_primary_key :way_nodes, [:id, :version, :sequence_id]
+
+    drop_table :current_segments
+    drop_table :current_way_segments
+    create_table :current_way_nodes, innodb_table do |t|
+      t.column :id,          :bigint, :limit => 64, :null => false
+      t.column :node_id,     :bigint, :limit => 64, :null => false
+      t.column :sequence_id, :bigint, :limit => 11, :null => false
+    end
+    add_primary_key :current_way_nodes, [:id, :sequence_id]
+
+    execute "TRUNCATE way_tags"
+    execute "TRUNCATE ways"
+    execute "TRUNCATE current_way_tags"
+    execute "TRUNCATE current_ways"
+
+    # now get the data back
+    csvopts = "FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\"' LINES TERMINATED BY '\\n'"
+
+    tempfiles.each { |fn| File.chmod 0644, fn } if have_segs
+
+    if have_segs
+      execute "LOAD DATA LOCAL INFILE '#{ways}' INTO TABLE ways #{csvopts} (id, user_id, timestamp) SET visible = 1, version = 1"
+      execute "LOAD DATA LOCAL INFILE '#{way_nodes}' INTO TABLE way_nodes #{csvopts} (id, node_id, sequence_id) SET version = 1"
+      execute "LOAD DATA LOCAL INFILE '#{way_tags}' INTO TABLE way_tags #{csvopts} (id, k, v) SET version = 1"
+
+      execute "INSERT INTO current_ways SELECT id, user_id, timestamp, visible FROM ways"
+      execute "INSERT INTO current_way_nodes SELECT id, node_id, sequence_id FROM way_nodes"
+      execute "INSERT INTO current_way_tags SELECT id, k, v FROM way_tags"
+    end
+
+    # and then readd the index
+    add_index :current_way_nodes, [:node_id], :name => "current_way_nodes_node_idx"
+
+    if have_segs
+      execute "LOAD DATA LOCAL INFILE '#{relations}' INTO TABLE relations #{csvopts} (id, user_id, timestamp) SET visible = 1, version = 1"
+      execute "LOAD DATA LOCAL INFILE '#{relation_members}' INTO TABLE relation_members #{csvopts} (id, member_type, member_id, member_role) SET version = 1"
+      execute "LOAD DATA LOCAL INFILE '#{relation_tags}' INTO TABLE relation_tags #{csvopts} (id, k, v) SET version = 1"
+
+      # FIXME: This will only work if there were no relations before the
+      # migration!
+      execute "INSERT INTO current_relations SELECT id, user_id, timestamp, visible FROM relations"
+      execute "INSERT INTO current_relation_members SELECT id, member_type, member_id, member_role FROM relation_members"
+      execute "INSERT INTO current_relation_tags SELECT id, k, v FROM relation_tags"
+    end
+
+    tempfiles.each { |fn| File.unlink fn } if have_segs
+  end
+
+  def self.down
+    raise IrreversibleMigration.new
+  end
+end
diff --git a/db/migrate/007_remove_segments_helper.cc b/db/migrate/007_remove_segments_helper.cc
new file mode 100644 (file)
index 0000000..2234a3b
--- /dev/null
@@ -0,0 +1,591 @@
+#include <mysql.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vector>
+#include <list>
+#include <sstream>
+
+#ifdef __amd64__
+
+#define F_U64 "%lu"
+#define F_U32 "%u"
+
+#else
+
+#define F_U64 "%Lu"
+#define F_U32 "%u"
+
+#endif
+
+using namespace std;
+
+template <typename T>
+static T parse(const char *str) {
+  istringstream in(str);
+  T t;
+  in >> t;
+  return t;
+}
+
+static void exit_mysql_err(MYSQL *mysql) {
+  const char *err = mysql_error(mysql);
+  if (err) {
+    fprintf(stderr, "007_remove_segments_helper: MySQL error: %s\n", err);
+  } else {
+    fprintf(stderr, "007_remove_segments_helper: MySQL error\n");
+  }
+  abort();
+  exit(EXIT_FAILURE);
+}
+
+static void exit_stmt_err(MYSQL_STMT *stmt) {
+  const char *err = mysql_stmt_error(stmt);
+  if (err) {
+    fprintf(stderr, "007_remove_segments_helper: MySQL stmt error: %s\n", err);
+  } else {
+    fprintf(stderr, "007_remove_segments_helper: MySQL stmt error\n");
+  }
+  abort();
+  exit(EXIT_FAILURE);
+}
+
+struct segment {
+  uint32_t from, to;
+};
+
+struct data {
+  MYSQL *mysql, *mysql2;
+
+  uint64_t seg_maxid, way_maxid;
+  uint64_t new_way_id;
+  uint64_t new_relation_id;
+
+  size_t segs_len;
+  struct segment *segs;
+  unsigned char *rem_segs;
+
+  FILE *ways, *way_nodes, *way_tags,
+    *relations, *relation_members, *relation_tags;
+};
+
+static uint64_t select_u64(MYSQL *mysql, const char *q) {
+  MYSQL_RES *res;
+  MYSQL_ROW row;
+  uint64_t ret;
+
+  if (mysql_query(mysql, q))
+    exit_mysql_err(mysql);
+
+  res = mysql_store_result(mysql);
+  if (!res) exit_mysql_err(mysql);
+
+  row = mysql_fetch_row(res);
+  if (!row) exit_mysql_err(mysql);
+
+  if (row[0]) {
+    ret = parse<uint64_t>(row[0]);
+  } else {
+    ret = 0;
+  }
+
+  mysql_free_result(res);
+
+  return ret;
+}
+
+static void find_maxids(struct data *d) {
+  d->seg_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_segments");
+  d->segs_len = d->seg_maxid + 1;
+  d->way_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_ways");
+  d->new_way_id = d->way_maxid + 1;
+  d->new_relation_id = select_u64(d->mysql, "SELECT max(id) FROM current_relations") + 1;
+}
+
+static void populate_segs(struct data *d) {
+  MYSQL_RES *res;
+  MYSQL_ROW row;
+  size_t id;
+
+  d->segs = (segment *) malloc(sizeof(struct segment) * d->segs_len);
+  memset(d->segs, 0, sizeof(struct segment) * d->segs_len);
+
+  d->rem_segs = (unsigned char *) malloc(d->segs_len);
+  memset(d->rem_segs, 0, d->segs_len);
+
+  if (mysql_query(d->mysql, "SELECT id, node_a, node_b "
+      "FROM current_segments WHERE visible"))
+    exit_mysql_err(d->mysql);
+
+  res = mysql_use_result(d->mysql);
+  if (!res) exit_mysql_err(d->mysql);
+
+  while ((row = mysql_fetch_row(res))) {
+    id = parse<size_t>(row[0]);
+    if (id >= d->segs_len) continue;
+    d->segs[id].from = parse<uint32_t>(row[1]);
+    d->segs[id].to   = parse<uint32_t>(row[2]);
+    d->rem_segs[id] = 1;
+  }
+  if (mysql_errno(d->mysql)) exit_mysql_err(d->mysql);
+
+  mysql_free_result(res);
+}
+
+static void write_csv_col(FILE *f, const char *str, char end) {
+  char *out = (char *) malloc(2 * strlen(str) + 4);
+  char *o = out;
+  size_t len;
+
+  *(o++) = '\"';
+  for (; *str; str++) {
+    if (*str == '\0') {
+      break;
+    } else if (*str == '\"') {
+      *(o++) = '\"';
+      *(o++) = '\"';
+    } else {
+      *(o++) = *str;
+    }
+  }
+  *(o++) = '\"';
+  *(o++) = end;
+  *(o++) = '\0';
+
+  len = strlen(out);
+  if (fwrite(out, len, 1, f) != 1) {
+    perror("fwrite");
+    exit(EXIT_FAILURE);
+  }
+
+  free(out);
+}
+
+static void convert_ways(struct data *d) {
+  MYSQL_RES *res;
+  MYSQL_ROW row;
+  MYSQL_STMT *load_segs, *load_tags;
+  const char
+    load_segs_stmt[] = "SELECT segment_id FROM current_way_segments "
+      "WHERE id = ? ORDER BY sequence_id",
+    load_tags_stmt[] = "SELECT k, v FROM current_way_tags WHERE id = ?";
+  char *k, *v;
+  const size_t max_tag_len = 1 << 16;
+  long long mysql_id, mysql_seg_id;
+  unsigned long res_len;
+  my_bool res_error;
+  MYSQL_BIND bind[1], seg_bind[1], tag_bind[2];
+
+  /* F***ing libmysql only support fixed size buffers for string results of
+   * prepared statements.  So allocate 65k for the tag key and the tag value
+   * and hope it'll suffice. */
+  k = (char *) malloc(max_tag_len);
+  v = (char *) malloc(max_tag_len);
+
+  load_segs = mysql_stmt_init(d->mysql2);
+  if (!load_segs) exit_mysql_err(d->mysql2);
+  if (mysql_stmt_prepare(load_segs, load_segs_stmt, sizeof(load_segs_stmt)))
+    exit_stmt_err(load_segs);
+
+  memset(bind, 0, sizeof(bind));
+  bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
+  bind[0].buffer = (char *) &mysql_id;
+  bind[0].is_null = 0;
+  bind[0].length = 0;
+  if (mysql_stmt_bind_param(load_segs, bind))
+    exit_stmt_err(load_segs);
+
+  memset(bind, 0, sizeof(seg_bind));
+  seg_bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
+  seg_bind[0].buffer = (char *) &mysql_seg_id;
+  seg_bind[0].is_null = 0;
+  seg_bind[0].length = 0;
+  seg_bind[0].error = &res_error;
+  if (mysql_stmt_bind_result(load_segs, seg_bind))
+    exit_stmt_err(load_segs);
+
+  load_tags = mysql_stmt_init(d->mysql2);
+  if (!load_tags) exit_mysql_err(d->mysql2);
+  if (mysql_stmt_prepare(load_tags, load_tags_stmt, sizeof(load_tags_stmt)))
+    exit_stmt_err(load_tags);
+
+  memset(bind, 0, sizeof(bind));
+  bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
+  bind[0].buffer = (char *) &mysql_id;
+  bind[0].is_null = 0;
+  bind[0].length = 0;
+
+  if (mysql_stmt_bind_param(load_tags, bind))
+    exit_stmt_err(load_tags);
+
+  memset(bind, 0, sizeof(tag_bind));
+  tag_bind[0].buffer_type = MYSQL_TYPE_STRING;
+  tag_bind[0].buffer = k;
+  tag_bind[0].is_null = 0;
+  tag_bind[0].length = &res_len;
+  tag_bind[0].error = &res_error;
+  tag_bind[0].buffer_length = max_tag_len;
+  tag_bind[1].buffer_type = MYSQL_TYPE_STRING;
+  tag_bind[1].buffer = v;
+  tag_bind[1].is_null = 0;
+  tag_bind[1].length = &res_len;
+  tag_bind[1].error = &res_error;
+  tag_bind[1].buffer_length = max_tag_len;
+  if (mysql_stmt_bind_result(load_tags, tag_bind))
+    exit_stmt_err(load_tags);
+
+  if (mysql_query(d->mysql, "SELECT id, user_id, timestamp "
+      "FROM current_ways WHERE visible"))
+    exit_mysql_err(d->mysql);
+
+  res = mysql_use_result(d->mysql);
+  if (!res) exit_mysql_err(d->mysql);
+
+  while ((row = mysql_fetch_row(res))) {
+    uint64_t id;
+    const char *user_id, *timestamp;
+
+    id = parse<uint64_t>(row[0]);
+    user_id = row[1];
+    timestamp = row[2];
+
+    mysql_id = (long long) id;
+
+    if (mysql_stmt_execute(load_segs))
+      exit_stmt_err(load_segs);
+
+    if (mysql_stmt_store_result(load_segs))
+      exit_stmt_err(load_segs);
+
+    list<segment> segs;
+    while (!mysql_stmt_fetch(load_segs)) {
+      if (((uint64_t) mysql_seg_id) >= d->segs_len) continue;
+      segs.push_back(d->segs[mysql_seg_id]);
+      d->rem_segs[mysql_seg_id] = 0;
+    }
+
+    list<list<uint32_t> > node_lists;
+    while (segs.size()) {
+      list<uint32_t> node_list;
+      node_list.push_back(segs.front().from);
+      node_list.push_back(segs.front().to);
+      segs.pop_front();
+      while (true) {
+       bool found = false;
+       for (list<segment>::iterator it = segs.begin();
+           it != segs.end(); ) {
+         if (it->from == node_list.back()) {
+           node_list.push_back(it->to);
+           segs.erase(it++);
+           found = true;
+         } else if (it->to == node_list.front()) {
+           node_list.insert(node_list.begin(), it->from);
+           segs.erase(it++);
+           found = true;
+         } else {
+           ++it;
+         }
+       }
+       if (!found) break;
+      }
+      node_lists.push_back(node_list);
+    }
+
+    vector<uint64_t> ids; ids.reserve(node_lists.size());
+    bool orig_id_used = false;
+    for (list<list<uint32_t> >::iterator it = node_lists.begin();
+       it != node_lists.end(); ++it) {
+      uint64_t way_id;
+      int sid;
+      if (orig_id_used) {
+       way_id = d->new_way_id++;
+      } else {
+       way_id = id;
+       orig_id_used = true;
+      }
+      ids.push_back(way_id);
+
+      fprintf(d->ways, "\"" F_U64 "\",", way_id);
+      write_csv_col(d->ways, user_id, ',');
+      write_csv_col(d->ways, timestamp, '\n');
+
+      sid = 1;
+      for (list<uint32_t>::iterator nit = it->begin();
+         nit != it->end(); ++nit) {
+       fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, *nit, sid++);
+      }
+    }
+
+    if (mysql_stmt_execute(load_tags))
+      exit_stmt_err(load_tags);
+
+    if (mysql_stmt_store_result(load_tags))
+      exit_stmt_err(load_tags);
+
+    bool multiple_parts = ids.size() > 1,
+      create_multipolygon = false;
+
+    while (!mysql_stmt_fetch(load_tags)) {
+      if (multiple_parts && !create_multipolygon) {
+       if (!strcmp(k, "natural")) {
+         if (strcmp(v, "coastline")) {
+           create_multipolygon = true;
+         }
+       } else if (!strcmp(k, "waterway")) {
+         if (!strcmp(v, "riverbank")) {
+           create_multipolygon = true;
+         }
+       } else if (!strcmp(k, "leisure") || !strcmp(k, "landuse")
+           || !strcmp(k, "sport") || !strcmp(k, "amenity")
+           || !strcmp(k, "tourism") || !strcmp(k, "building")) {
+         create_multipolygon = true;
+       }
+      }
+
+      for (vector<uint64_t>::iterator it = ids.begin();
+         it != ids.end(); ++it) {
+       fprintf(d->way_tags, "\"" F_U64 "\",", *it);
+       write_csv_col(d->way_tags, k, ',');
+       write_csv_col(d->way_tags, v, '\n');
+      }
+    }
+
+    if (multiple_parts && create_multipolygon) {
+      uint64_t ent_id = d->new_relation_id++;
+
+      fprintf(d->relations, "\"" F_U64 "\",", ent_id);
+      write_csv_col(d->relations, user_id, ',');
+      write_csv_col(d->relations, timestamp, '\n');
+
+      fprintf(d->relation_tags,
+       "\"" F_U64 "\",\"type\",\"multipolygon\"\n", ent_id);
+
+      for (vector<uint64_t>::iterator it = ids.begin();
+         it != ids.end(); ++it) {
+       fprintf(d->relation_members,
+         "\"" F_U64 "\",\"way\",\"" F_U64 "\",\"\"\n", ent_id, *it);
+      }
+    }
+  }
+  if (mysql_errno(d->mysql)) exit_stmt_err(load_tags);
+
+  mysql_stmt_close(load_segs);
+  mysql_stmt_close(load_tags);
+
+  mysql_free_result(res);
+  free(k);
+  free(v);
+}
+
+static int read_seg_tags(char **tags, char **k, char **v) {
+  if (!**tags) return 0;
+  char *i = strchr(*tags, ';');
+  if (!i) i = *tags + strlen(*tags);
+  char *j = strchr(*tags, '=');
+  *k = *tags;
+  if (j && j < i) {
+    *v = j + 1;
+  } else {
+    *v = i;
+  }
+  *tags = *i ? i + 1 : i;
+  *i = '\0';
+  if (j) *j = '\0';
+  return 1;
+}
+
+static void mark_tagged_segs(struct data *d) {
+  MYSQL_RES *res;
+  MYSQL_ROW row;
+
+  if (mysql_query(d->mysql, "SELECT id, tags FROM current_segments "
+      "WHERE visible && tags != '' && tags != 'created_by=JOSM'"))
+    exit_mysql_err(d->mysql);
+
+  res = mysql_use_result(d->mysql);
+  if (!res) exit_mysql_err(d->mysql);
+
+  while ((row = mysql_fetch_row(res))) {
+    size_t id = parse<size_t>(row[0]);
+    if (d->rem_segs[id]) continue;
+    char *tags_it = row[1], *k, *v;
+    while (read_seg_tags(&tags_it, &k, &v)) {
+      if (strcmp(k, "created_by") &&
+         strcmp(k, "tiger:county") &&
+         strcmp(k, "tiger:upload_uuid") &&
+         strcmp(k, "converted_by") &&
+         (strcmp(k, "natural") || strcmp(v, "coastline")) &&
+         (strcmp(k, "source") || strcmp(v, "PGS"))) {
+       d->rem_segs[id] = 1;
+       break;
+      }
+    }
+  }
+
+  mysql_free_result(res);
+}
+
+static void convert_remaining_segs(struct data *d) {
+  MYSQL_STMT *load_seg;
+  MYSQL_BIND args[1], res[3];
+  const size_t max_tag_len = 1 << 16;
+  char *tags, timestamp[100];
+  char *k, *v;
+  int user_id;
+  long long mysql_id;
+  unsigned long res_len;
+  my_bool res_error;
+  const char load_seg_stmt[] =
+    "SELECT user_id, tags, CAST(timestamp AS CHAR) FROM current_segments "
+    "WHERE visible && id = ?";
+
+  tags = (char *) malloc(max_tag_len);
+
+  load_seg = mysql_stmt_init(d->mysql);
+  if (!load_seg) exit_mysql_err(d->mysql);
+  if (mysql_stmt_prepare(load_seg, load_seg_stmt, sizeof(load_seg_stmt)))
+    exit_stmt_err(load_seg);
+
+  memset(args, 0, sizeof(args));
+  args[0].buffer_type = MYSQL_TYPE_LONGLONG;
+  args[0].buffer = (char *) &mysql_id;
+  args[0].is_null = 0;
+  args[0].length = 0;
+  if (mysql_stmt_bind_param(load_seg, args))
+    exit_stmt_err(load_seg);
+
+  memset(res, 0, sizeof(res));
+  res[0].buffer_type = MYSQL_TYPE_LONG;
+  res[0].buffer = (char *) &user_id;
+  res[0].is_null = 0;
+  res[0].length = 0;
+  res[0].error = &res_error;
+  res[1].buffer_type = MYSQL_TYPE_STRING;
+  res[1].buffer = tags;
+  res[1].is_null = 0;
+  res[1].length = &res_len;
+  res[1].error = &res_error;
+  res[1].buffer_length = max_tag_len;
+  res[2].buffer_type = MYSQL_TYPE_STRING;
+  res[2].buffer = timestamp;
+  res[2].is_null = 0;
+  res[2].length = &res_len;
+  res[2].error = &res_error;
+  res[2].buffer_length = sizeof(timestamp);
+  if (mysql_stmt_bind_result(load_seg, res))
+    exit_stmt_err(load_seg);
+
+  for (size_t seg_id = 0; seg_id < d->segs_len; seg_id++) {
+    if (!d->rem_segs[seg_id]) continue;
+    segment seg = d->segs[seg_id];
+
+    mysql_id = seg_id;
+    if (mysql_stmt_execute(load_seg)) exit_stmt_err(load_seg);
+    if (mysql_stmt_store_result(load_seg)) exit_stmt_err(load_seg);
+
+    while (!mysql_stmt_fetch(load_seg)) {
+      uint64_t way_id = d->new_way_id++;
+
+      fprintf(d->ways, "\"" F_U64 "\",\"%i\",", way_id, user_id);
+      write_csv_col(d->ways, timestamp, '\n');
+
+      fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, seg.from, 1);
+      fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, seg.to, 2);
+
+      char *tags_it = tags;
+      while (read_seg_tags(&tags_it, &k, &v)) {
+       fprintf(d->way_tags, "\"" F_U64 "\",", way_id);
+       write_csv_col(d->way_tags, k, ',');
+       write_csv_col(d->way_tags, v, '\n');
+      }
+    }
+  }
+
+  mysql_stmt_close(load_seg);
+
+  free(tags);
+}
+
+static MYSQL *connect_to_mysql(char **argv) {
+  MYSQL *mysql = mysql_init(NULL);
+  if (!mysql) exit_mysql_err(mysql);
+
+  if (!mysql_real_connect(mysql, argv[1], argv[2], argv[3], argv[4],
+      argv[5][0] ? atoi(argv[5]) : 0, argv[6][0] ? argv[6] : NULL, 0))
+    exit_mysql_err(mysql);
+
+  if (mysql_set_character_set(mysql, "utf8"))
+    exit_mysql_err(mysql);
+
+  return mysql;
+}
+
+static void open_file(FILE **f, char *fn) {
+  *f = fopen(fn, "w+");
+  if (!*f) {
+    perror("fopen");
+    exit(EXIT_FAILURE);
+  }
+}
+
+int main(int argc, char **argv) {
+  struct data data;
+  struct data *d = &data;
+  size_t prefix_len;
+  char *tempfn;
+
+  if (argc != 8) {
+    printf("Usage: 007_remove_segments_helper host user passwd database port socket prefix\n");
+    exit(EXIT_FAILURE);
+  }
+
+  d->mysql = connect_to_mysql(argv);
+  d->mysql2 = connect_to_mysql(argv);
+
+  prefix_len = strlen(argv[7]);
+  tempfn = (char *) malloc(prefix_len + 15);
+  strcpy(tempfn, argv[7]);
+
+  strcpy(tempfn + prefix_len, "ways");
+  open_file(&d->ways, tempfn);
+
+  strcpy(tempfn + prefix_len, "way_nodes");
+  open_file(&d->way_nodes, tempfn);
+
+  strcpy(tempfn + prefix_len, "way_tags");
+  open_file(&d->way_tags, tempfn);
+
+  strcpy(tempfn + prefix_len, "relations");
+  open_file(&d->relations, tempfn);
+
+  strcpy(tempfn + prefix_len, "relation_members");
+  open_file(&d->relation_members, tempfn);
+
+  strcpy(tempfn + prefix_len, "relation_tags");
+  open_file(&d->relation_tags, tempfn);
+
+  free(tempfn);
+
+  find_maxids(d);
+  populate_segs(d);
+  convert_ways(d);
+  mark_tagged_segs(d);
+  convert_remaining_segs(d);
+
+  mysql_close(d->mysql);
+  mysql_close(d->mysql2);
+
+  fclose(d->ways);
+  fclose(d->way_nodes);
+  fclose(d->way_tags);
+
+  fclose(d->relations);
+  fclose(d->relation_members);
+  fclose(d->relation_tags);
+
+  free(d->segs);
+  free(d->rem_segs);
+
+  exit(EXIT_SUCCESS);
+}
index 3f0c433..a703b3a 100755 (executable)
@@ -18,7 +18,6 @@ begin
   user_count = User.count(:conditions => "active = true")
   tracepoint_count = Tracepoint.count()
   node_count = Node.count(:conditions => "visible = true")
-  segment_count = Segment.count(:conditions => "visible = true")
   way_count = Way.count(:conditions => "visible = true")
   tagged_way_count = Way.count(:conditions => "visible = true AND EXISTS (SELECT * FROM current_way_tags WHERE id = current_ways.id AND k <> 'created_by')")
 
@@ -26,7 +25,6 @@ begin
   puts "<tr><td>Number of users</td><td>#{user_count}</td></tr>"
   puts "<tr><td>Number of uploaded GPS points</td><td>#{tracepoint_count}</td></tr>"
   puts "<tr><td>Number of nodes</td><td>#{node_count}</td></tr>"
-  puts "<tr><td>Number of segments</td><td>#{segment_count}</td></tr>"
   puts "<tr><td>Number of ways</td><td>#{way_count}</td></tr>"
   puts "<tr><td>Number of ways with tags</td><td>#{tagged_way_count}</td></tr>"
   puts "</table>"
diff --git a/test/fixtures/current_relation_members.yml b/test/fixtures/current_relation_members.yml
new file mode 100644 (file)
index 0000000..bddc8a0
--- /dev/null
@@ -0,0 +1,23 @@
+t1:
+  id: 1
+  member_role: "some"
+  member_type: "way"
+  member_id: 3
+
+t2:
+  id: 1
+  member_role: "some"
+  member_type: "node"
+  member_id: 5
+
+t3:
+  id: 1
+  member_role: "some"
+  member_type: "relation"
+  member_id: 3
+
+t4:
+  id: 3
+  member_role: "some"
+  member_type: "node"
+  member_id: 5
diff --git a/test/fixtures/current_relation_tags.yml b/test/fixtures/current_relation_tags.yml
new file mode 100644 (file)
index 0000000..aaf06a3
--- /dev/null
@@ -0,0 +1,14 @@
+t1:
+  id: 1
+  k: test
+  v: yes
+
+t2:
+  id: 2
+  k: test
+  v: yes
+
+t2:
+  id: 3
+  k: test
+  v: yes
diff --git a/test/fixtures/current_relations.yml b/test/fixtures/current_relations.yml
new file mode 100644 (file)
index 0000000..c1f77d4
--- /dev/null
@@ -0,0 +1,17 @@
+visible_relation:
+  id: 1
+  user_id: 1
+  timestamp: 2007-01-01 00:00:00
+  visible: 1
+
+invisible_relation:
+  id: 2
+  user_id: 1
+  timestamp: 2007-01-01 00:00:00
+  visible: 0
+
+used_relation:
+  id: 3
+  user_id: 1
+  timestamp: 2007-01-01 00:00:00
+  visible: 1
diff --git a/test/fixtures/current_segments.yml b/test/fixtures/current_segments.yml
deleted file mode 100644 (file)
index ce37583..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-visible_segment:
-  id: 1
-  node_a: 3
-  node_b: 4
-  user_id: 1
-  visible: 1
-  tags: test=yes
-  timestamp: 2007-01-01 00:00:00
-
-invisible_segment:
-  id: 2
-  node_a: 3
-  node_b: 4
-  user_id: 1
-  visible: 0
-  tags: test=yes
-  timestamp: 2007-01-01 00:00:00
-
-used_segment:
-  id: 3
-  node_a: 3
-  node_b: 4
-  user_id: 1
-  visible: 1
-  tags: test=yes
-  timestamp: 2007-01-01 00:00:00
diff --git a/test/fixtures/current_way_nodes.yml b/test/fixtures/current_way_nodes.yml
new file mode 100644 (file)
index 0000000..ce394ed
--- /dev/null
@@ -0,0 +1,14 @@
+t1:
+  id: 1
+  node_id: 3
+  sequence_id: 1
+
+t2:
+  id: 2
+  node_id: 3
+  sequence_id: 1
+
+t3:
+  id: 3
+  node_id: 3
+  sequence_id: 1
diff --git a/test/fixtures/current_way_segments.yml b/test/fixtures/current_way_segments.yml
deleted file mode 100644 (file)
index db82de5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-t1:
-  id: 1
-  segment_id: 3
-  sequence_id: 1
-
-t2:
-  id: 2
-  segment_id: 3
-  sequence_id: 1
index 05d876f..375247e 100644 (file)
@@ -3,7 +3,13 @@ t1:
   k: test
   v: yes
 
-t1:
+t2:
   id: 2
   k: test
   v: yes
+
+t3:
+  id: 3
+  k: test
+  v: yes
+
index 97bc485..b129d7f 100644 (file)
@@ -9,3 +9,10 @@ invisible_way:
   user_id: 1
   timestamp: 2007-01-01 00:00:00
   visible: 0
+
+used_way:
+  id: 3
+  user_id: 1
+  timestamp: 2007-01-01 00:00:00
+  visible: 1
+
diff --git a/test/fixtures/relation_members.yml b/test/fixtures/relation_members.yml
new file mode 100644 (file)
index 0000000..27e8b53
--- /dev/null
@@ -0,0 +1,24 @@
+t1:
+  id: 1
+  member_role: "some"
+  member_type: "way"
+  member_id: 3
+  version: 1
+t2:
+  id: 1
+  member_role: "some"
+  member_type: "node"
+  member_id: 5
+  version: 1
+t3:
+  id: 1
+  member_role: "some"
+  member_type: "relation"
+  member_id: 3
+  version: 1
+t4:
+  id: 3
+  member_role: "some"
+  member_type: "node"
+  member_id: 5
+  version: 1
diff --git a/test/fixtures/relation_tags.yml b/test/fixtures/relation_tags.yml
new file mode 100644 (file)
index 0000000..39f4bd5
--- /dev/null
@@ -0,0 +1,17 @@
+t1:
+  id: 1
+  k: test
+  v: yes
+  version: 1
+
+t2:
+  id: 2
+  k: test
+  v: yes
+  version: 1
+
+t3:
+  id: 3
+  k: test
+  v: yes
+  version: 1
diff --git a/test/fixtures/relations.yml b/test/fixtures/relations.yml
new file mode 100644 (file)
index 0000000..cf1d1ff
--- /dev/null
@@ -0,0 +1,20 @@
+visible_relation:
+  id: 1
+  user_id: 1
+  timestamp: 2007-01-01 00:00:00
+  visible: 1
+  version: 1
+
+invisible_relation:
+  id: 2
+  user_id: 1
+  timestamp: 2007-01-01 00:00:00
+  visible: 0
+  version: 1
+
+used_relation:
+  id: 3
+  user_id: 1
+  timestamp: 2007-01-01 00:00:00
+  visible: 1
+  version: 1
diff --git a/test/fixtures/segments.yml b/test/fixtures/segments.yml
deleted file mode 100644 (file)
index ce37583..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-visible_segment:
-  id: 1
-  node_a: 3
-  node_b: 4
-  user_id: 1
-  visible: 1
-  tags: test=yes
-  timestamp: 2007-01-01 00:00:00
-
-invisible_segment:
-  id: 2
-  node_a: 3
-  node_b: 4
-  user_id: 1
-  visible: 0
-  tags: test=yes
-  timestamp: 2007-01-01 00:00:00
-
-used_segment:
-  id: 3
-  node_a: 3
-  node_b: 4
-  user_id: 1
-  visible: 1
-  tags: test=yes
-  timestamp: 2007-01-01 00:00:00
similarity index 50%
rename from test/fixtures/way_segments.yml
rename to test/fixtures/way_nodes.yml
index bbefff6..caeac16 100644 (file)
@@ -1,11 +1,17 @@
 t1:
   id: 1
-  segment_id: 3
+  node_id: 3
   sequence_id: 1
   version: 1
 
 t2:
   id: 2
-  segment_id: 3
+  node_id: 3
+  sequence_id: 1
+  version: 1
+
+t3:
+  id: 3
+  node_id: 3
   sequence_id: 1
   version: 1
index d1c69a6..39f4bd5 100644 (file)
@@ -9,3 +9,9 @@ t2:
   k: test
   v: yes
   version: 1
+
+t3:
+  id: 3
+  k: test
+  v: yes
+  version: 1
index 1448985..c8cf6dc 100644 (file)
@@ -11,3 +11,11 @@ invisible_way:
   timestamp: 2007-01-01 00:00:00
   visible: 0
   version: 1
+
+used_way:
+  id: 3
+  user_id: 1
+  timestamp: 2007-01-01 00:00:00
+  visible: 0
+  version: 1
+
diff --git a/test/functional/api_controller_test.rb b/test/functional/api_controller_test.rb
new file mode 100644 (file)
index 0000000..05cbe2a
--- /dev/null
@@ -0,0 +1,34 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'api_controller'
+
+# Re-raise errors caught by the controller.
+class ApiController; def rescue_action(e) raise e end; end
+
+class ApiControllerTest < Test::Unit::TestCase
+  api_fixtures
+
+  def setup
+    @controller = ApiController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def basic_authorization(user, pass)
+    @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}")
+  end
+
+  # -------------------------------------
+  # Test reading a bounding box.
+  # -------------------------------------
+
+  def test_map
+    node = current_nodes(:used_node_1)
+    bbox = "#{node.latitude-0.1},#{node.longitude-0.1},#{node.latitude+0.1},#{node.longitude+0.1}"
+    get :map, :bbox => bbox
+    if $VERBOSE
+        print @response.body
+    end
+    assert_response :success
+  end
+
+end
diff --git a/test/functional/relation_controller_test.rb b/test/functional/relation_controller_test.rb
new file mode 100644 (file)
index 0000000..8f8b727
--- /dev/null
@@ -0,0 +1,209 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'relation_controller'
+
+# Re-raise errors caught by the controller.
+class RelationController; def rescue_action(e) raise e end; end
+
+class RelationControllerTest < Test::Unit::TestCase
+  api_fixtures
+  fixtures :relations, :current_relations, :relation_members, :current_relation_members, :relation_tags, :current_relation_tags
+  set_fixture_class :current_relations => :Relation
+  set_fixture_class :relations => :OldRelation
+
+  def setup
+    @controller = RelationController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def basic_authorization(user, pass)
+    @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}")
+  end
+
+  def content(c)
+    @request.env["RAW_POST_DATA"] = c
+  end
+
+  # -------------------------------------
+  # Test reading relations.
+  # -------------------------------------
+
+  def test_read
+    # check that a visible relation is returned properly
+    get :read, :id => current_relations(:visible_relation).id
+    assert_response :success
+
+    # check that an invisible relation is not returned
+    get :read, :id => current_relations(:invisible_relation).id
+    assert_response :gone
+
+    # check chat a non-existent relation is not returned
+    get :read, :id => 0
+    assert_response :not_found
+
+    # check the "relations for node" mode
+    get :relations_for_node, :id => current_nodes(:node_used_by_relationship).id
+    assert_response :success
+    # FIXME check whether this contains the stuff we want!
+    if $VERBOSE
+        print @response.body
+    end
+
+    # check the "relations for way" mode
+    get :relations_for_way, :id => current_ways(:used_way).id
+    assert_response :success
+    # FIXME check whether this contains the stuff we want!
+    if $VERBOSE
+        print @response.body
+    end
+
+
+    # check the "relations for relation" mode
+    get :relations_for_relation, :id => current_relations(:used_relation).id
+    assert_response :success
+    # FIXME check whether this contains the stuff we want!
+    if $VERBOSE
+        print @response.body
+    end
+
+    # check the "full" mode
+    get :full, :id => current_relations(:visible_relation).id
+    assert_response :success
+    # FIXME check whether this contains the stuff we want!
+    if $VERBOSE
+        print @response.body
+    end
+  end
+
+  # -------------------------------------
+  # Test simple relation creation.
+  # -------------------------------------
+
+  def test_create
+    basic_authorization "test@openstreetmap.org", "test"
+
+    # create an relation without members
+    content "<osm><relation><tag k='test' v='yes' /></relation></osm>"
+    put :create
+    # hope for success
+    assert_response :success, 
+        "relation upload did not return success status"
+    # read id of created relation and search for it
+    relationid = @response.body
+    checkrelation = Relation.find(relationid)
+    assert_not_nil checkrelation, 
+        "uploaded relation not found in data base after upload"
+    # compare values
+    assert_equal checkrelation.members.length, 0, 
+        "saved relation contains members but should not"
+    assert_equal checkrelation.tags.length, 1, 
+        "saved relation does not contain exactly one tag"
+    assert_equal users(:normal_user).id, checkrelation.user_id, 
+        "saved relation does not belong to user that created it"
+    assert_equal true, checkrelation.visible, 
+        "saved relation is not visible"
+    # ok the relation is there but can we also retrieve it?
+    get :read, :id => relationid
+    assert_response :success
+
+
+    # create an relation with a node as member
+    nid = current_nodes(:used_node_1).id
+    content "<osm><relation><member type='node' ref='#{nid}' role='some'/>" +
+        "<tag k='test' v='yes' /></relation></osm>"
+    put :create
+    # hope for success
+    assert_response :success, 
+        "relation upload did not return success status"
+    # read id of created relation and search for it
+    relationid = @response.body
+    checkrelation = Relation.find(relationid)
+    assert_not_nil checkrelation, 
+        "uploaded relation not found in data base after upload"
+    # compare values
+    assert_equal checkrelation.members.length, 1, 
+        "saved relation does not contain exactly one member"
+    assert_equal checkrelation.tags.length, 1, 
+        "saved relation does not contain exactly one tag"
+    assert_equal users(:normal_user).id, checkrelation.user_id, 
+        "saved relation does not belong to user that created it"
+    assert_equal true, checkrelation.visible, 
+        "saved relation is not visible"
+    # ok the relation is there but can we also retrieve it?
+    
+    get :read, :id => relationid
+    assert_response :success
+
+    # create an relation with a way and a node as members
+    nid = current_nodes(:used_node_1).id
+    wid = current_ways(:used_way).id
+    content "<osm><relation><member type='node' ref='#{nid}' role='some'/>" +
+        "<member type='way' ref='#{wid}' role='other'/>" +
+        "<tag k='test' v='yes' /></relation></osm>"
+    put :create
+    # hope for success
+    assert_response :success, 
+        "relation upload did not return success status"
+    # read id of created relation and search for it
+    relationid = @response.body
+    checkrelation = Relation.find(relationid)
+    assert_not_nil checkrelation, 
+        "uploaded relation not found in data base after upload"
+    # compare values
+    assert_equal checkrelation.members.length, 2, 
+        "saved relation does not have exactly two members"
+    assert_equal checkrelation.tags.length, 1, 
+        "saved relation does not contain exactly one tag"
+    assert_equal users(:normal_user).id, checkrelation.user_id, 
+        "saved relation does not belong to user that created it"
+    assert_equal true, checkrelation.visible, 
+        "saved relation is not visible"
+    # ok the relation is there but can we also retrieve it?
+    get :read, :id => relationid
+    assert_response :success
+
+  end
+
+  # -------------------------------------
+  # Test creating some invalid relations.
+  # -------------------------------------
+
+  def test_create_invalid
+    basic_authorization "test@openstreetmap.org", "test"
+
+    # create a relation with non-existing node as member
+    content "<osm><relation><member type='node' ref='0'/><tag k='test' v='yes' /></relation></osm>"
+    put :create
+    # expect failure
+    assert_response :precondition_failed, 
+        "relation upload with invalid node did not return 'precondition failed'"
+  end
+
+  # -------------------------------------
+  # Test deleting relations.
+  # -------------------------------------
+  
+  def test_delete
+  return true
+
+    # first try to delete relation without auth
+    delete :delete, :id => current_relations(:visible_relation).id
+    assert_response :unauthorized
+
+    # now set auth
+    basic_authorization("test@openstreetmap.org", "test");  
+
+    # this should work
+    delete :delete, :id => current_relations(:visible_relation).id
+    assert_response :success
+
+    # this won't work since the relation is already deleted
+    delete :delete, :id => current_relations(:invisible_relation).id
+    assert_response :gone
+
+    # this won't work since the relation never existed
+    delete :delete, :id => 0
+    assert_response :not_found
+  end
+
+end
diff --git a/test/functional/segment_controller_test.rb b/test/functional/segment_controller_test.rb
deleted file mode 100644 (file)
index 1ff26ef..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-require File.dirname(__FILE__) + '/../test_helper'
-require 'segment_controller'
-
-# Re-raise errors caught by the controller.
-class SegmentController; def rescue_action(e) raise e end; end
-
-class SegmentControllerTest < Test::Unit::TestCase
-  api_fixtures
-
-  def setup
-    @controller = SegmentController.new
-    @request    = ActionController::TestRequest.new
-    @response   = ActionController::TestResponse.new
-  end
-
-  def test_create
-    # cannot read password from fixture as it is stored as MD5 digest
-    basic_authorization("test@openstreetmap.org", "test");  
-    na = current_nodes(:used_node_1).id
-    nb = current_nodes(:used_node_2).id
-    content("<osm><segment from='#{na}' to='#{nb}' /></osm>")
-    put :create
-    # hope for success
-    assert_response :success, "segment upload did not return success status"
-    # read id of created segment and search for it
-    segmentid = @response.body
-    checksegment = Segment.find(segmentid)
-    assert_not_nil checksegment, "uploaded segment not found in data base after upload"
-    # compare values
-    assert_equal na, checksegment.node_a, "saved segment does not match requested from-node"
-    assert_equal nb, checksegment.node_b, "saved segment does not match requested to-node"
-    assert_equal users(:normal_user).id, checksegment.user_id, "saved segment does not belong to user that created it"
-    assert_equal true, checksegment.visible, "saved segment is not visible"
-  end
-
-  def test_create_invalid
-    basic_authorization("test@openstreetmap.org", "test");  
-    # create a segment with one invalid node
-    na = current_nodes(:used_node_1).id
-    nb = 0
-    content("<osm><segment from='#{na}' to='#{nb}' /></osm>")
-    put :create
-    # expect failure
-    assert_response :precondition_failed, "upload of invalid segment did not return 'precondition failed'"
-  end
-
-  def test_read
-    # check that a visible segment is returned properly
-    get :read, :id => current_segments(:visible_segment).id
-    assert_response :success
-    # TODO: check for <segment> tag in return data
-
-    # check that an invisible segment is not returned
-    get :read, :id => current_segments(:invisible_segment).id
-    assert_response :gone
-
-    # check chat a non-existent segment is not returned
-    get :read, :id => 0
-    assert_response :not_found
-  end
-
-  # this tests deletion restrictions - basic deletion is tested in the unit
-  # tests for segment!
-  def test_delete
-
-    # first try to delete segment without auth
-    delete :delete, :id => current_segments(:visible_segment).id
-    assert_response :unauthorized
-
-    # now set auth
-    basic_authorization("test@openstreetmap.org", "test");  
-
-    # this should work
-    delete :delete, :id => current_segments(:visible_segment).id
-    assert_response :success
-
-    # this won't work since the segment is already deleted
-    delete :delete, :id => current_segments(:invisible_segment).id
-    assert_response :gone
-
-    # this won't work since the segment never existed
-    delete :delete, :id => 0
-    assert_response :not_found
-
-    # this won't work since the segment is in use
-    delete :delete, :id => current_segments(:used_segment).id
-    assert_response :precondition_failed
-  end
-
-
-  def basic_authorization(user, pass)
-    @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}")
-  end
-
-  def content(c)
-    @request.env["RAW_POST_DATA"] = c
-  end
-end
index c5a3f0b..933dfb5 100644 (file)
@@ -5,11 +5,7 @@ require 'way_controller'
 class WayController; def rescue_action(e) raise e end; end
 
 class WayControllerTest < Test::Unit::TestCase
-  fixtures :current_nodes, :nodes, :users, :current_segments, :segments, :ways, :current_ways, :way_tags, :current_way_tags, :way_segments, :current_way_segments
-  set_fixture_class :current_ways => :Way
-  set_fixture_class :ways => :OldWay
-  set_fixture_class :current_segments => :Segment
-  set_fixture_class :segments => :OldSegment
+  api_fixtures
 
   def setup
     @controller = WayController.new
@@ -41,6 +37,18 @@ class WayControllerTest < Test::Unit::TestCase
     # check chat a non-existent way is not returned
     get :read, :id => 0
     assert_response :not_found
+
+    # check the "ways for node" mode
+    get :ways_for_node, :id => current_nodes(:used_node_1).id
+    assert_response :success
+    # FIXME check whether this contains the stuff we want!
+    print @response.body
+
+    # check the "full" mode
+    get :full, :id => current_ways(:visible_way).id
+    assert_response :success
+    # FIXME check whether this contains the stuff we want!
+    print @response.body
   end
 
   # -------------------------------------
@@ -48,11 +56,12 @@ class WayControllerTest < Test::Unit::TestCase
   # -------------------------------------
 
   def test_create
-    sid = current_segments(:used_segment).id
+    nid1 = current_nodes(:used_node_1).id
+    nid2 = current_nodes(:used_node_2).id
     basic_authorization "test@openstreetmap.org", "test"
 
-    # create a way with pre-existing segment
-    content "<osm><way><seg id='#{sid}'/><tag k='test' v='yes' /></way></osm>"
+    # create a way with pre-existing nodes
+    content "<osm><way><nd ref='#{nid1}'/><nd ref='#{nid2}'/><tag k='test' v='yes' /></way></osm>"
     put :create
     # hope for success
     assert_response :success, 
@@ -63,10 +72,12 @@ class WayControllerTest < Test::Unit::TestCase
     assert_not_nil checkway, 
         "uploaded way not found in data base after upload"
     # compare values
-    assert_equal checkway.segs.length, 1, 
-        "saved way does not contain exactly one segment"
-    assert_equal checkway.segs[0], sid, 
-        "saved way does not contain the right segment"
+    assert_equal checkway.nds.length, 2, 
+        "saved way does not contain exactly one node"
+    assert_equal checkway.nds[0], nid1, 
+        "saved way does not contain the right node on pos 0"
+    assert_equal checkway.nds[1], nid2, 
+        "saved way does not contain the right node on pos 1"
     assert_equal users(:normal_user).id, checkway.user_id, 
         "saved way does not belong to user that created it"
     assert_equal true, checkway.visible, 
@@ -80,27 +91,19 @@ class WayControllerTest < Test::Unit::TestCase
   def test_create_invalid
     basic_authorization "test@openstreetmap.org", "test"
 
-    # create a way with non-existing segment
-    content "<osm><way><seg id='0'/><tag k='test' v='yes' /></way></osm>"
+    # create a way with non-existing node
+    content "<osm><way><nd ref='0'/><tag k='test' v='yes' /></way></osm>"
     put :create
     # expect failure
     assert_response :precondition_failed, 
-        "way upload with invalid segment did not return 'precondition failed'"
+        "way upload with invalid node did not return 'precondition failed'"
 
-    # create a way with no segments
+    # create a way with no nodes
     content "<osm><way><tag k='test' v='yes' /></way></osm>"
     put :create
     # expect failure
     assert_response :precondition_failed, 
-        "way upload with no segments did not return 'precondition failed'"
-
-    # create a way that has the same segment, twice
-    # (commented out - this is currently allowed!)
-    #sid = current_segments(:used_segment).id
-    #content "<osm><way><seg id='#{sid}'/><seg id='#{sid}'/><tag k='test' v='yes' /></way></osm>"
-    #put :create
-    #assert_response :internal_server_error,
-    #    "way upload with double segment did not return 'internal server error'"
+        "way upload with no node did not return 'precondition failed'"
   end
 
   # -------------------------------------
index a441122..f3baf4f 100644 (file)
@@ -32,18 +32,14 @@ class Test::Unit::TestCase
     set_fixture_class :current_nodes => :Node
     set_fixture_class :nodes => :OldNode
 
-    fixtures :current_segments, :segments
-    set_fixture_class :current_segments => :Segment
-    set_fixture_class :segments => :OldSegment
-
-    fixtures :current_ways, :current_way_segments, :current_way_tags
+    fixtures :current_ways, :current_way_nodes, :current_way_tags
     set_fixture_class :current_ways => :Way
-    set_fixture_class :current_way_segments => :WaySegment
+    set_fixture_class :current_way_nodes => :WayNode
     set_fixture_class :current_way_tags => :WayTag
 
-    fixtures :ways, :way_segments, :way_tags
+    fixtures :ways, :way_nodes, :way_tags
     set_fixture_class :ways => :OldWay
-    set_fixture_class :way_segments => :OldWaySegment
+    set_fixture_class :way_nodes => :OldWayNode
     set_fixture_class :way_tags => :OldWayTag
   end
 
index b3e8343..95321b5 100644 (file)
@@ -11,7 +11,7 @@ class NodeTest < Test::Unit::TestCase
                              :user_id => users(:normal_user).id,
                              :visible => 1,
                              :tags => "")
-    assert node_template.save_with_history
+    assert node_template.save_with_history!
 
     node = Node.find(node_template.id)
     assert_not_nil node
@@ -44,7 +44,7 @@ class NodeTest < Test::Unit::TestCase
     node_template.latitude = 12.3456
     node_template.longitude = 65.4321
     node_template.tags = "updated=yes"
-    assert node_template.save_with_history
+    assert node_template.save_with_history!
 
     node = Node.find(node_template.id)
     assert_not_nil node
@@ -76,7 +76,7 @@ class NodeTest < Test::Unit::TestCase
     assert_not_nil old_node_template
 
     node_template.visible = 0
-    assert node_template.save_with_history
+    assert node_template.save_with_history!
 
     node = Node.find(node_template.id)
     assert_not_nil node
diff --git a/test/unit/segment_test.rb b/test/unit/segment_test.rb
deleted file mode 100644 (file)
index e8af3df..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-require File.dirname(__FILE__) + '/../test_helper'
-
-class SegmentTest < Test::Unit::TestCase
-  fixtures :current_nodes, :nodes, :current_segments, :segments, :users
-  set_fixture_class :current_segments => :Segment
-  set_fixture_class :segments => :OldSegment
-  set_fixture_class :current_nodes => :Node
-  set_fixture_class :nodes => :OldNode
-
-  def test_create
-
-    segment_template = Segment.new(:node_a => nodes(:used_node_1).id,
-                             :node_b => nodes(:used_node_2).id,
-                             :user_id => users(:normal_user).id,
-                             :visible => 1,
-                             :tags => "")
-    assert segment_template.save_with_history
-
-    segment = Segment.find(segment_template.id)
-    assert_not_nil segment
-    assert_equal segment_template.node_a, segment.node_a
-    assert_equal segment_template.node_b, segment.node_b
-    assert_equal segment_template.user_id, segment.user_id
-    assert_equal segment_template.visible, segment.visible
-    assert_equal segment_template.tags, segment.tags
-    assert_equal segment_template.timestamp.to_i, segment.timestamp.to_i
-
-    assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 1
-    old_segment = OldSegment.find(:first, :conditions => [ "id = ?", segment_template.id ])
-    assert_not_nil old_segment
-    assert_equal segment_template.node_a, old_segment.node_a
-    assert_equal segment_template.node_b, old_segment.node_b
-    assert_equal segment_template.user_id, old_segment.user_id
-    assert_equal segment_template.visible, old_segment.visible
-    assert_equal segment_template.tags, old_segment.tags
-    assert_equal segment_template.timestamp.to_i, old_segment.timestamp.to_i
-  end
-
-  def test_update
-    segment_template = Segment.find(1)
-    assert_not_nil segment_template
-
-    assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 1
-    old_segment_template = OldSegment.find(:first, :conditions => [ "id = ?", segment_template.id ])
-    assert_not_nil old_segment_template
-
-    segment_template.node_a = nodes(:used_node_2).id
-    segment_template.node_b = nodes(:used_node_1).id
-    segment_template.tags = "updated=yes"
-    assert segment_template.save_with_history
-
-    segment = Segment.find(segment_template.id)
-    assert_not_nil segment
-    assert_equal segment_template.node_a, segment.node_a
-    assert_equal segment_template.node_b, segment.node_b
-    assert_equal segment_template.user_id, segment.user_id
-    assert_equal segment_template.visible, segment.visible
-    assert_equal segment_template.tags, segment.tags
-    assert_equal segment_template.timestamp.to_i, segment.timestamp.to_i
-
-    assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 2
-    assert_equal OldSegment.find(:all, :conditions => [ "id = ? and timestamp = ?", segment_template.id, segment_template.timestamp ]).length, 1
-    old_segment = OldSegment.find(:first, :conditions => [ "id = ? and timestamp = ?", segment_template.id, segment_template.timestamp ])
-    assert_not_nil old_segment
-    assert_equal segment_template.node_a, old_segment.node_a
-    assert_equal segment_template.node_b, old_segment.node_b
-    assert_equal segment_template.user_id, old_segment.user_id
-    assert_equal segment_template.visible, old_segment.visible
-    assert_equal segment_template.tags, old_segment.tags
-    assert_equal segment_template.timestamp.to_i, old_segment.timestamp.to_i
-  end
-
-  def test_delete
-    segment_template = Segment.find(1)
-    assert_not_nil segment_template
-
-    assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 1
-    old_segment_template = OldSegment.find(:first, :conditions => [ "id = ?", segment_template.id ])
-    assert_not_nil old_segment_template
-
-    segment_template.visible = 0
-    assert segment_template.save_with_history
-
-    segment = Segment.find(segment_template.id)
-    assert_not_nil segment
-    assert_equal segment_template.node_a, segment.node_a
-    assert_equal segment_template.node_b, segment.node_b
-    assert_equal segment_template.user_id, segment.user_id
-    assert_equal segment_template.visible, segment.visible
-    assert_equal segment_template.tags, segment.tags
-    assert_equal segment_template.timestamp.to_i, segment.timestamp.to_i
-
-    assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 2
-    assert_equal OldSegment.find(:all, :conditions => [ "id = ? and timestamp = ?", segment_template.id, segment_template.timestamp ]).length, 1
-    old_segment = OldSegment.find(:first, :conditions => [ "id = ? and timestamp = ?", segment_template.id, segment_template.timestamp ])
-    assert_not_nil old_segment
-    assert_equal segment_template.node_a, old_segment.node_a
-    assert_equal segment_template.node_b, old_segment.node_b
-    assert_equal segment_template.user_id, old_segment.user_id
-    assert_equal segment_template.visible, old_segment.visible
-    assert_equal segment_template.tags, old_segment.tags
-    assert_equal segment_template.timestamp.to_i, old_segment.timestamp.to_i
-  end
-end