Added label objects
authorNeil Smith <neil.github@njae.me.uk>
Fri, 3 Feb 2012 17:12:32 +0000 (17:12 +0000)
committerNeil Smith <neil.github@njae.me.uk>
Fri, 3 Feb 2012 17:12:32 +0000 (17:12 +0000)
12 files changed:
Gemfile
Gemfile.lock
lib/erd_handler.rb
lib/erd_handler/box.rb
lib/erd_handler/erd.rb
lib/erd_handler/label.rb [new file with mode: 0644]
lib/erd_handler/link.rb
spec/erd_handler/box_spec.rb [new file with mode: 0644]
spec/erd_handler/erd_spec.rb
spec/erd_handler/label_spec.rb [new file with mode: 0644]
spec/fixtures/complex_erd.xml [new file with mode: 0644]
spec/fixtures/single_box_self_loop_erd.xml [new file with mode: 0644]

diff --git a/Gemfile b/Gemfile
index 116c2fcd6ac2abbead5e317c6beedf478dbc8310..f73bc3edac18f0ed5de2a0ca548ddc4681b39871 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -6,9 +6,10 @@ source "http://rubygems.org"
 # Add dependencies to develop your gem here.
 # Include everything needed to run rake, tests, features, etc.
 group :development do
-  gem "rspec", "~> 2.6.0"
-  gem "bundler", "~> 1.0.0"
+  gem "rspec"
+  gem "bundler"
   gem "rdoc"
   gem "rake"
-  gem "graph.njae", "0.2.3"
+  gem "graph.njae"
+  gem "porter2stemmer"
 end
index 9962be5260c360de0a6f9ffd558cbb804abd9731..d440e9ca5807db2dedee9b11b5a3694975ad8e8e 100644 (file)
@@ -1,25 +1,29 @@
 GEM
   remote: http://rubygems.org/
   specs:
-    diff-lcs (1.1.2)
+    diff-lcs (1.1.3)
     graph.njae (0.2.3)
-    rake (0.9.2)
-    rdoc (3.7)
-    rspec (2.6.0)
-      rspec-core (~> 2.6.0)
-      rspec-expectations (~> 2.6.0)
-      rspec-mocks (~> 2.6.0)
-    rspec-core (2.6.4)
-    rspec-expectations (2.6.0)
+    json (1.6.5)
+    porter2stemmer (1.0.0)
+    rake (0.9.2.2)
+    rdoc (3.12)
+      json (~> 1.4)
+    rspec (2.8.0)
+      rspec-core (~> 2.8.0)
+      rspec-expectations (~> 2.8.0)
+      rspec-mocks (~> 2.8.0)
+    rspec-core (2.8.0)
+    rspec-expectations (2.8.0)
       diff-lcs (~> 1.1.2)
-    rspec-mocks (2.6.0)
+    rspec-mocks (2.8.0)
 
 PLATFORMS
   ruby
 
 DEPENDENCIES
-  bundler (~> 1.0.0)
-  graph.njae (= 0.2.3)
+  bundler
+  graph.njae
+  porter2stemmer
   rake
   rdoc
-  rspec (~> 2.6.0)
+  rspec
index 6524d3140db8abe649d81d0138cbe8c7f32706c6..241db56426ace2871c6e77a53b4b0927cff1b7ba 100644 (file)
@@ -1,11 +1,14 @@
 require 'bundler/setup'
 
-require "rexml/document"
+require 'rexml/document'
 include REXML  
 
-require "graph.njae"
+require 'graph.njae'
 include GraphNjae
 
+require 'porter2stemmer'
+
+require 'erd_handler/label'
 require 'erd_handler/erd'
 require 'erd_handler/box'
 require 'erd_handler/link'
index 7389955d4b58d2927efc70703e9b3e966f799ded..75543eb22674b1dcc92f5734dcc6ea6366a929e3 100644 (file)
@@ -8,7 +8,7 @@ module ErdHandler
     
     def read(box_element)
       self.id = box_element.attributes["id"].to_i
-      self.name = box_element.attributes["name"]
+      self.name = Label.new box_element.attributes["name"]
       self.mark = box_element.attributes["mark"].to_f
       
       self.x = box_element.elements["location"].attributes["x"].to_f
@@ -19,5 +19,14 @@ module ErdHandler
       self
     end
     
+    def contains?(other)
+      self.x < other.x and self.x + self.width > other.x + other.width and 
+          self.y < other.y and self.y + self.height > other.y + other.height
+    end
+
+    def within?(other)
+      other.contains?(self)
+    end
+    
   end
 end
index 142f575d8851044d9299090b069599526aad7480..bf740072d432b883cf7e58d891d56c4431418051 100644 (file)
@@ -10,7 +10,7 @@ module ErdHandler
       doc = Document.new(source)
       raise InvalidErdFile unless doc.elements.to_a.length == 1 and doc.elements[1].name.downcase == 'drawing'
       self.mark = doc.elements['Drawing'].attributes["mark"].to_f
-      self.name = doc.elements['Drawing'].attributes["name"]
+      self.name = Label.new doc.elements['Drawing'].attributes["name"]
       doc.elements.each('Drawing/box') do |box_element|
         self << Box.new(box_element)
       end
diff --git a/lib/erd_handler/label.rb b/lib/erd_handler/label.rb
new file mode 100644 (file)
index 0000000..ad81618
--- /dev/null
@@ -0,0 +1,47 @@
+class Label 
+  
+  attr_reader :original, :processed
+  
+  def initialize(original)
+    @original = original
+    @processed = [original]
+  end
+  
+  def split(opts = {})
+    if opts.class == Regexp
+      regexp = opts
+      split_camel_case = true
+    else
+      regexp = opts[:regexp] || /[ _,.-]+/
+      if opts.has_key? :camel_case
+        split_camel_case = opts[:camel_case]
+      else
+        split_camel_case = true
+      end
+    end
+    @processed = @processed.map do |segment|
+      segment.split(regexp)
+    end.flatten
+    
+    if split_camel_case
+      @processed = @processed.map do |segment|
+        segment.split(/(?=[A-Z])/)
+      end.flatten
+    end
+    self
+  end
+  
+  def downcase
+    @processed = @processed.map do |segment| segment.downcase end
+    self
+  end
+  
+  def stem(gb_english = false)
+    @processed = @processed.map do |segment| segment.stem(gb_english) end
+    self
+  end
+  
+  def tidy
+    self.split.downcase.stem
+  end
+end
\ No newline at end of file
index 4a7b181c0eb808eb7fcea507af087c21a4ba7743..63bfd0600182574a9a527c18c1a8c68421ac46c9 100644 (file)
@@ -9,7 +9,7 @@ module ErdHandler
     def read(link_element, vertices)
       self.id = link_element.attributes["id"].to_i
       self.mark = link_element.attributes["mark"].to_f
-      self.name = link_element.elements['moveableName'].attributes['name']
+      self.name = Label.new link_element.elements['moveableName'].attributes['name']
       box1 = vertices.select {|v| v.id == link_element.elements['box1'].attributes['id'].to_i}[0]
       box2 = vertices.select {|v| v.id == link_element.elements['box2'].attributes['id'].to_i}[0]
       self << box1 << box2
@@ -27,5 +27,6 @@ module ErdHandler
       c2.blob = link_element.elements['box2EndAdornments'].attributes['blob'].downcase.intern
       c2.crowsfoot = link_element.elements['box2EndAdornments'].attributes['crowsfoot'].downcase.intern
     end
+    
   end
 end
diff --git a/spec/erd_handler/box_spec.rb b/spec/erd_handler/box_spec.rb
new file mode 100644 (file)
index 0000000..82005d6
--- /dev/null
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+module ErdHandler
+  describe Box do
+    # Needed as RSpec includes the Psych YAML reader, which defines :y as
+    # a method for generating YAML of an object.
+    class Box
+      undef_method :y
+    end
+
+    describe "#contains?" do
+      it "reports when a box contains another" do 
+        b1 = Box.new 
+        b1.x = 10 ; b1.y = 10 ; b1.width = 20 ; b1.height = 20
+        b2 = Box.new ; b2.x =  5 ; b2.y =  5 ; b2.width = 30 ; b2.height = 30
+        b3 = Box.new ; b3.x = 15 ; b3.y = 15 ; b3.width = 20 ; b3.height = 20
+        
+        b1.should_not be_contains(b2)
+        b2.should be_contains(b1)
+        b1.should_not be_contains(b3)
+        b3.should_not be_contains(b1)
+      end
+    end # contains?
+
+    describe "#within?" do
+      it "reports when a box is within another" do 
+        b1 = Box.new 
+        b1.x = 10 ; b1.y = 10 ; b1.width = 20 ; b1.height = 20
+        b2 = Box.new ; b2.x =  5 ; b2.y =  5 ; b2.width = 30 ; b2.height = 30
+        b3 = Box.new ; b3.x = 15 ; b3.y = 15 ; b3.width = 20 ; b3.height = 20
+        
+        # Can't use the standard RSpec predicate notation as it clashes with 
+        # floating-point expectations
+        b1.within?(b2).should == true
+        b2.within?(b1).should == false
+        b1.within?(b3).should == false
+        b3.within?(b1).should == false
+      end
+    end # contains?
+
+  end
+end
index 0c32a815805b1c7afcd93be5809f4ac88b5ba8f8..d6d5561e4cb34274d26b0e0fa4fda870ed2b3751 100644 (file)
@@ -79,56 +79,62 @@ module ErdHandler
         erd.should have(6).edges
         
         b0 = erd.vertices.find {|b| b.id == 0}
-        b0.name.should == "Unit"
+        b0.name.original.should == "Unit"
         b0.should have(2).neighbours
         
         b1 = erd.vertices.find {|b| b.id == 1}
-        b1.name.should == "Employee"
+        b1.name.original.should == "Employee"
         b1.should have(2).neighbours
         
         b2 = erd.vertices.find {|b| b.id == 2}
-        b2.name.should == "Course"
+        b2.name.original.should == "Course"
         b2.should have(3).neighbours
 
         b3 = erd.vertices.find {|b| b.id == 3}
-        b3.name.should == "Presentation"
+        b3.name.original.should == "Presentation"
         b3.should have(3).neighbours
         
         b4 = erd.vertices.find {|b| b.id == 4}
-        b4.name.should == "Client"
+        b4.name.original.should == "Client"
         b4.should have(1).neighbours
 
         l0 = erd.edges.find {|e| e.id == 0}
+        l0.name.original.should == "ConsistsOf"
         l0.connections.find {|c| c.end == b0}.blob.should be :closed
         l0.connections.find {|c| c.end == b0}.crowsfoot.should be :yes
         l0.connections.find {|c| c.end == b2}.blob.should be :closed
         l0.connections.find {|c| c.end == b2}.crowsfoot.should be :no
 
         l1 = erd.edges.find {|e| e.id == 1}
+        l1.name.original.should == "Prepares"
         l1.connections.find {|c| c.end == b0}.blob.should be :open
         l1.connections.find {|c| c.end == b0}.crowsfoot.should be :yes
         l1.connections.find {|c| c.end == b1}.blob.should be :closed
         l1.connections.find {|c| c.end == b1}.crowsfoot.should be :no
 
         l2 = erd.edges.find {|e| e.id == 2}
+        l2.name.original.should == "Presents"
         l2.connections.find {|c| c.end == b1}.blob.should be :closed
         l2.connections.find {|c| c.end == b1}.crowsfoot.should be :no
         l2.connections.find {|c| c.end == b3}.blob.should be :closed
         l2.connections.find {|c| c.end == b3}.crowsfoot.should be :yes
 
         l3 = erd.edges.find {|e| e.id == 3}
+        l3.name.original.should == "Presented"
         l3.connections.find {|c| c.end == b2}.blob.should be :open
         l3.connections.find {|c| c.end == b2}.crowsfoot.should be :no
         l3.connections.find {|c| c.end == b3}.blob.should be :closed
         l3.connections.find {|c| c.end == b3}.crowsfoot.should be :yes
 
         l4 = erd.edges.find {|e| e.id == 4}
+        l4.name.original.should == "Recieves"
         l4.connections.find {|c| c.end == b3}.blob.should be :closed
         l4.connections.find {|c| c.end == b3}.crowsfoot.should be :yes
         l4.connections.find {|c| c.end == b4}.blob.should be :closed
         l4.connections.find {|c| c.end == b4}.crowsfoot.should be :no
 
         l5 = erd.edges.find {|e| e.id == 5}
+        l5.name.original.should == "IsPrerequisiteOf"
         l5.connections.find {|c| c.crowsfoot == :yes}.blob.should be :open
         l5.connections.find {|c| c.crowsfoot == :no}.blob.should be :open
         l5.connections.find {|c| c.crowsfoot == :yes}.end.should be b2
diff --git a/spec/erd_handler/label_spec.rb b/spec/erd_handler/label_spec.rb
new file mode 100644 (file)
index 0000000..d2fb0c8
--- /dev/null
@@ -0,0 +1,119 @@
+require 'spec_helper'
+
+module ErdHandler
+  describe Label do
+    describe '#original' do
+      it "reports the string it was initialised with" do
+        test_label = "Test label"
+        l1 = Label.new test_label
+        l1.original.should == test_label
+      end
+    end # original
+
+    describe '#processed' do
+      it "reports the original if no processing has been done" do
+        test_label = "Test label"
+        l1 = Label.new test_label
+        l1.processed.should == [test_label]
+        l1.original.should == test_label
+      end
+    end # processed
+
+    describe '#split' do
+      it "splits the original on the specified regexp" do
+        l1 = Label.new "Test label"
+        l1.split(/ /)
+        l1.processed.should == ["Test", "label"]
+        l1.original.should == "Test label"
+        
+        l1 = Label.new "Test_label"
+        l1.split(/_/)
+        l1.processed.should == ["Test", "label"]
+        
+        l1 = Label.new "Test label_string"
+        l1.split(/[ _]/)
+        l1.processed.should == ["Test", "label", "string"]
+      end
+      
+      it "splits the original on camel case" do
+        l1 = Label.new "TestLabel"
+        l1.split :camel_case => true
+        l1.processed.should == ["Test", "Label"]
+        l1.original.should == "TestLabel"
+
+        l2 = Label.new "testLabel"
+        l2.split :camel_case => true
+        l2.processed.should == ["test", "Label"]
+        l2.original.should == "testLabel"
+      end
+
+      it "doesn't split the original on camel case if asked not to" do
+        l1 = Label.new "TestLabel"
+        l1.split :camel_case => false
+        l1.processed.should == ["TestLabel"]
+        l1.original.should == "TestLabel"
+        
+        l2 = Label.new "TestLabel"
+        l2.split :camel_case => nil
+        l2.processed.should == ["TestLabel"]
+        l2.original.should == "TestLabel"
+      end
+      
+      it "splits the original using a default regexp" do
+        l1 = Label.new "Test label_string"
+        l1.split
+        l1.processed.should == ["Test", "label", "string"]
+      end
+      
+      it "splits the original on camel case by default" do
+        l1 = Label.new "TestLabel"
+        l1.split
+        l1.processed.should == ["Test", "Label"]
+        l1.original.should == "TestLabel"
+      end
+
+      it "splits the original on punctuation and camel case by default" do
+        l1 = Label.new "TestLabel is_split, he,said"
+        l1.split
+        l1.processed.should == ["Test", "Label", "is", "split", "he", "said"]
+        l1.original.should == "TestLabel is_split, he,said"
+      end
+
+      it "is idempotent" do
+        l1 = Label.new "TestLabel is_split, he,said"
+        res1 = l1.split.dup
+        res2 = l1.split
+        res1.processed.should == res2.processed
+        l1.original.should == "TestLabel is_split, he,said"
+      end
+    end # split
+
+    describe "#downcase" do
+      it "downcases all parts of the processed label" do
+        l1 = Label.new "Test label_string"
+        l1.split.downcase
+        l1.processed.should == ["test", "label", "string"]
+      end
+    end # downcase
+    
+    describe "#stem" do
+      it "stems all parts of the processed label" do
+        l1 = Label.new "testing labeller string pontificated"
+        l1.split.stem
+        l1.processed.should == ["test", "label", "string", "pontif"]
+      end
+    end # stem
+    
+    describe "#tidy" do
+      it "tidies a label" do
+        l1 = Label.new "testingLabeller string, he_pontificated"
+        l2 = Label.new l1.original
+        l1.tidy
+        l2.split.downcase.stem
+        l1.processed.should == l2.processed
+
+      end
+    end # tidy
+    
+  end
+end
diff --git a/spec/fixtures/complex_erd.xml b/spec/fixtures/complex_erd.xml
new file mode 100644 (file)
index 0000000..26d808b
--- /dev/null
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><Drawing
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   name="Untitled" mark="4.0">
+
+<box id="0" name="Unit" mark="0.0">
+  <location x="121.0" y="113.0"/>
+  <size width="80.0" height="40.0"/>
+  <comment></comment>
+</box>
+
+<box id="1" name="Employee" mark="0.0">
+  <location x="356.0" y="129.0"/>
+  <size width="80.0" height="40.0"/>
+  <comment></comment>
+</box>
+
+<box id="2" name="Course" mark="0.0">
+  <location x="132.0" y="265.0"/>
+  <size width="80.0" height="40.0"/>
+  <comment></comment>
+</box>
+
+<box id="3" name="Presentation" mark="0.0">
+  <location x="383.0" y="277.0"/>
+  <size width="80.0" height="40.0"/>
+  <comment></comment>
+</box>
+
+<box id="4" name="Client" mark="0.0">
+  <location x="389.0" y="421.0"/>
+  <size width="80.0" height="40.0"/>
+  <comment></comment>
+</box>
+
+<link id="0" mark="0.0">
+  <moveableName name="ConsistsOf">
+    <point x="156" y="214"/>
+  </moveableName>
+  <converseName name="converseName"/>
+  <box1 id="0"/>
+  <box2 id="2"/>
+  <bendPoints>
+    <point x="161.0" y="133.0"/>
+    <point x="162.44736842105263" y="153.0"/>
+    <point x="166.5" y="209.0"/>
+    <point x="170.55263157894737" y="265.0"/>
+    <point x="172.0" y="285.0"/>
+  </bendPoints>
+  <midMoved value="false"/>
+  <box1EndAdornments blob="Closed" crowsfoot="Yes"/>
+  <box2EndAdornments blob="Closed" crowsfoot="No"/>
+  <comment></comment>
+</link>
+
+<link id="1" mark="0.0">
+  <moveableName name="Prepares">
+    <point x="257" y="149"/>
+  </moveableName>
+  <converseName name="converseName"/>
+  <box1 id="0"/>
+  <box2 id="1"/>
+  <bendPoints>
+    <point x="161.0" y="133.0"/>
+    <point x="201.0" y="135.72340425531914"/>
+    <point x="278.5" y="141.0"/>
+    <point x="356.0" y="146.27659574468086"/>
+    <point x="396.0" y="149.0"/>
+  </bendPoints>
+  <midMoved value="false"/>
+  <box1EndAdornments blob="Open" crowsfoot="Yes"/>
+  <box2EndAdornments blob="Closed" crowsfoot="No"/>
+  <comment></comment>
+</link>
+
+<link id="2" mark="0.0">
+  <moveableName name="Presents">
+    <point x="417" y="203"/>
+  </moveableName>
+  <converseName name="converseName"/>
+  <box1 id="1"/>
+  <box2 id="3"/>
+  <bendPoints>
+    <point x="396.0" y="149.0"/>
+    <point x="399.64864864864865" y="169.0"/>
+    <point x="409.5" y="223.0"/>
+    <point x="419.35135135135135" y="277.0"/>
+    <point x="423.0" y="297.0"/>
+  </bendPoints>
+  <midMoved value="false"/>
+  <box1EndAdornments blob="Closed" crowsfoot="No"/>
+  <box2EndAdornments blob="Closed" crowsfoot="Yes"/>
+  <comment></comment>
+</link>
+
+<link id="3" mark="0.0">
+  <moveableName name="Presented">
+    <point x="288" y="296"/>
+  </moveableName>
+  <converseName name="converseName"/>
+  <box1 id="2"/>
+  <box2 id="3"/>
+  <bendPoints>
+    <point x="172.0" y="285.0"/>
+    <point x="212.0" y="286.9123505976096"/>
+    <point x="297.5" y="291.0"/>
+    <point x="383.0" y="295.0876494023904"/>
+    <point x="423.0" y="297.0"/>
+  </bendPoints>
+  <midMoved value="false"/>
+  <box1EndAdornments blob="Open" crowsfoot="No"/>
+  <box2EndAdornments blob="Closed" crowsfoot="Yes"/>
+  <comment></comment>
+</link>
+
+<link id="4" mark="0.0">
+  <moveableName name="Recieves">
+    <point x="439" y="361"/>
+  </moveableName>
+  <converseName name="converseName"/>
+  <box1 id="3"/>
+  <box2 id="4"/>
+  <bendPoints>
+    <point x="423.0" y="297.0"/>
+    <point x="423.8333333333333" y="317.0"/>
+    <point x="426.0" y="369.0"/>
+    <point x="428.1666666666667" y="421.0"/>
+    <point x="429.0" y="441.0"/>
+  </bendPoints>
+  <midMoved value="false"/>
+  <box1EndAdornments blob="Closed" crowsfoot="Yes"/>
+  <box2EndAdornments blob="Closed" crowsfoot="No"/>
+  <comment></comment>
+</link>
+
+<selfLink id="5" mark="0.0">
+  <moveableName name="IsPrerequisiteOf">
+    <point x="40.0" y="228.0"/>
+  </moveableName>
+  <converseName name="converseName"/>
+  <box1 id="2"/>
+  <box2 id="2"/>
+  <cornerNo value="1"/>
+  <bendPoints>
+    <point x="132.0" y="265.0"/>
+    <point x="112.0" y="250.0"/>
+    <point x="132.0" y="265.0"/>
+  </bendPoints>
+  <midMoved value="false"/>
+  <box1EndAdornments blob="Open" crowsfoot="Yes"/>
+  <box2EndAdornments blob="Open" crowsfoot="No"/>
+  <comment></comment>
+</selfLink>
+
+</Drawing>
\ No newline at end of file
diff --git a/spec/fixtures/single_box_self_loop_erd.xml b/spec/fixtures/single_box_self_loop_erd.xml
new file mode 100644 (file)
index 0000000..d44a486
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><Drawing
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   name="Untitled" mark="4.5">
+
+<box id="3" name="Course" mark="0.0">
+  <location x="91.0" y="235.0"/>
+  <size width="80.0" height="40.0"/>
+  <comment></comment>
+</box>
+
+<selfLink id="5" mark="0.0">
+  <moveableName name="AssociatedWith">
+    <point x="71.0" y="200.0"/>
+  </moveableName>
+  <converseName name="converseName"/>
+  <box1 id="3"/>
+  <box2 id="3"/>
+  <cornerNo value="1"/>
+  <bendPoints>
+    <point x="91.0" y="235.0"/>
+    <point x="71.0" y="220.0"/>
+    <point x="91.0" y="235.0"/>
+  </bendPoints>
+  <midMoved value="false"/>
+  <box1EndAdornments blob="Closed" crowsfoot="Yes"/>
+  <box2EndAdornments blob="Open" crowsfoot="No"/>
+  <comment></comment>
+</selfLink>
+
+</Drawing>
\ No newline at end of file