conflict 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -34,7 +34,7 @@ end
34
34
 
35
35
  spec = Gem::Specification.new do |s|
36
36
  s.name = 'conflict'
37
- s.version = '0.1.0'
37
+ s.version = '0.2.0'
38
38
  s.author = 'Dennis Byrne'
39
39
  s.email = 'dbyrne@thoughtworks.com'
40
40
  s.platform = Gem::Platform::RUBY
data/lib/conflict/core.rb CHANGED
@@ -18,6 +18,7 @@
18
18
  module Conflict
19
19
 
20
20
  DIFF_URL = "/diff"
21
+ DIFF_CVS_URL = "/diff.cvs"
21
22
  STATUS_URL = "/status.xml"
22
23
  PORT = 80 # tcp/ip
23
24
  TTL = 43200 # event staleness, 12 hours in secs
@@ -40,7 +41,15 @@ class Server
40
41
  RequestValidator::new().validate request
41
42
  client = request.query['client'].to_s.strip
42
43
  events = RequestParser::new(@cfg[:ttl]).parse request.query['diff'], request.query['info'], client
43
- conflicts = @db.find_conflicts(client, events)
44
+ conflicts = @db.find_conflicts(client, events).sort_by {|c| c.remote_client}
45
+ res.body = conflicts.to_yaml
46
+ }
47
+
48
+ @s.mount_proc(DIFF_CVS_URL){|request, res|
49
+ RequestValidator::new().validate_cvs request
50
+ client = request.query['client'].to_s.strip
51
+ events = RequestParser::new(@cfg[:ttl]).parse_cvs request.query['diff'], client
52
+ conflicts = @db.find_conflicts(client, events).sort_by {|c| c.remote_client}
44
53
  res.body = conflicts.to_yaml
45
54
  }
46
55
 
@@ -72,26 +81,41 @@ end
72
81
  class RequestValidator
73
82
 
74
83
  def validate request
84
+
85
+ validate_common request
75
86
 
76
- request_method = request.request_method
87
+ info = request.query['info']
77
88
 
89
+ raise "info parameter cannot be nil or empty" if info.nil? || info.empty?
90
+
91
+ diff = request.query['diff']
92
+
93
+ raise "diff count " + diff.list.size.to_s + " != info count " + info.list.size.to_s if diff.list.size != info.list.size
94
+ end
95
+
96
+ def validate_cvs request
97
+
98
+ validate_common request
99
+
100
+ end
101
+
102
+ private
103
+ def validate_common request
104
+
105
+ request_method = request.request_method
106
+
78
107
  raise "only POST HTTP requests supported, not " + request_method if ! "POST".eql?(request_method)
79
-
108
+
80
109
  client = request.query['client'].to_s.strip
81
-
82
- raise "client parameter is nil or empty" if nil.eql?(client) || client.empty?
83
-
110
+
111
+ raise "client parameter is nil or empty" if client.nil? || client.empty?
112
+
84
113
  diff = request.query['diff']
85
-
86
- raise "diff parameter is nil" if nil.eql?(diff)
87
-
88
- info = request.query['info']
89
-
90
- raise "info parameter cannot be nil or empty" if nil.eql?(info) || info.empty?
114
+
115
+ raise "diff parameter is nil" if diff.nil?
91
116
 
92
- raise "diff count " + diff.list.size.to_s + " != info count " + info.list.size.to_s if diff.list.size != info.list.size
93
117
  end
94
-
118
+
95
119
  end
96
120
 
97
121
  class DataBase
@@ -145,8 +169,8 @@ class DataBase
145
169
 
146
170
  def validate_input client, events
147
171
 
148
- raise ConflictException::new("client cannot be empty") if nil.eql?(client) || client.empty?
149
- raise ConflictException::new("events cannot be nil") if nil.eql?(events)
172
+ raise ConflictException::new("client cannot be empty") if client.nil? || client.empty?
173
+ raise ConflictException::new("events cannot be nil") if events.nil?
150
174
 
151
175
  resources = []
152
176
 
@@ -167,4 +191,4 @@ class ConflictException < Exception
167
191
  end
168
192
  end
169
193
 
170
- end
194
+ end
@@ -22,16 +22,20 @@ class Conflict
22
22
  attr_reader :local, :remote
23
23
 
24
24
  def initialize local, remote
25
- raise ConflictException::new("local event cannot be nil") if nil.eql?(local)
26
- raise ConflictException::new("remote event cannot be nil") if nil.eql?(remote)
25
+ raise ConflictException::new("local event cannot be nil") if local.nil?
26
+ raise ConflictException::new("remote event cannot be nil") if remote.nil?
27
27
  raise ConflictException::new("local resource '" + local.resource + "' does not match remote resource '" + remote.resource + "'") if ! local.resource.eql?(remote.resource)
28
28
  raise ConflictException::new("local and remote cannot have the same identity") if local == remote
29
29
  #what about = names, not = resources?
30
30
  raise ConflictException::new("local and remote cannot be eql") if local.eql?(remote)
31
- # using clone because jvYaml does not currently support aliasing
31
+ # using clone because I may have to write a parser in .net
32
32
  @local, @remote = local.clone, remote.clone
33
33
  end
34
-
34
+
35
+ def remote_client
36
+ remote.client
37
+ end
38
+
35
39
  def eql? other
36
40
  @local.eql?(other.local) and @remote.eql?(other.remote)
37
41
  end
@@ -48,15 +52,16 @@ class Event
48
52
  def initialize client, action, resource, created=Time.now, ttl=TTL
49
53
 
50
54
  raise ConflictException::new("invalid action '" + action.to_s + "'") if ! ['added', 'changed', 'deleted'].index(action)
51
- raise ConflictException::new("client must not be empty value") if nil.eql?(client) or client.empty?
52
- raise ConflictException::new("resource must not be empty value") if nil.eql?(resource) or resource.empty?
55
+ raise ConflictException::new("client must not be empty value") if client.nil? or client.empty?
56
+ raise ConflictException::new("resource must not be empty value") if resource.nil? or resource.empty?
53
57
  raise ConflictException::new("ttl cannot be negative") if ttl < 0
54
58
  @client, @action, @resource = client, action, resource
55
59
  # kept around for staleness expiration
56
60
  @created_time = created
57
61
  # rendered in YAML
58
- @created = [created.year, created.month, created.day, created.hour, created.min] * "-"
62
+ @created = created.year.to_s + "/" + created.month.to_s + "/" + created.day.to_s + " " + created.hour.to_s + ":" + created.min.to_s
59
63
  @ttl = ttl
64
+ @reserved = ''
60
65
  end
61
66
 
62
67
  def eql? other
@@ -71,7 +76,7 @@ class Event
71
76
  fields = self.instance_variables.clone
72
77
  fields.delete "@created_time" # keep data/time out of integration point ( ruby != java != .net )
73
78
  fields.delete "@ttl"
74
- fields
79
+ fields.sort
75
80
  end
76
81
 
77
82
  yaml_as "tag:java.yaml.org,2002:object:com.thoughtworks.conflict.Event"
@@ -34,18 +34,35 @@ class RequestParser
34
34
  diff_data = diff.list[i].to_s.strip
35
35
  info_data = info.list[i].to_s.strip
36
36
 
37
- raise "no value for info @ index " + i.to_s if nil.eql?(info_data) or info_data.empty?
37
+ raise "no value for info @ index " + i.to_s if info_data.nil? or info_data.empty?
38
38
 
39
39
  lambda {
40
40
  infos = InfoParser::new().parse(info_data)
41
- events = events | DiffParser::new(infos.merge({:ttl=>@ttl})).parse(diff_data, client)
42
- }.call if ! nil.eql?(diff_data) && ! diff_data.empty?
41
+ events = events | DiffParser::new(infos.merge({:ttl=>@ttl})).parse(diff_data, client, infos[:url], infos[:path])
42
+ }.call if ! diff_data.nil? && ! diff_data.empty?
43
43
 
44
44
  end
45
45
 
46
46
  events
47
47
 
48
48
  end
49
+
50
+ def parse_cvs diffs, client
51
+
52
+ events = []
53
+
54
+ diffs.each do | diff |
55
+
56
+ d = diff.to_s.strip
57
+ lambda{
58
+ events = events | DiffParser::new({:ttl=>@ttl}).parse_cvs(d, client)
59
+ }.call if ! d.nil? && ! d.empty?
60
+
61
+ end
62
+
63
+ events
64
+
65
+ end
49
66
 
50
67
  end
51
68
 
@@ -55,7 +72,7 @@ class InfoParser
55
72
 
56
73
  def parse info
57
74
 
58
- raise ConflictException::new("info cannot be nil or empty") if nil.eql?(info) || info.empty?
75
+ raise ConflictException::new("info cannot be nil or empty") if info.nil? || info.empty?
59
76
 
60
77
  properties = {}
61
78
 
@@ -72,20 +89,24 @@ end
72
89
  class DiffParser
73
90
 
74
91
  @@key = "Index: "
75
-
92
+ @@key_cvs = 'RCS file: '
93
+
76
94
  def initialize cfg
77
- raise ConflictException::new("cfg cannot be nil") if nil.eql?(cfg)
78
- raise ConflictException::new("url value cannot be nil or empty") if nil.eql?(cfg[:url]) or cfg[:url].empty?
79
- raise ConflictException::new("path value cannot be nil or empty") if nil.eql?(cfg[:path]) or cfg[:path].empty?
80
- raise ConflictException::new("ttl value cannot be nil") if nil.eql?(cfg[:ttl])
95
+ raise ConflictException::new("cfg cannot be nil") if cfg.nil?
96
+ raise ConflictException::new("ttl value cannot be nil") if cfg[:ttl].nil?
81
97
  raise ConflictException::new("ttl cannot be negative") if cfg[:ttl] < 0
82
98
  @cfg = cfg
83
99
  end
84
100
 
85
- def parse diff, client # would be nice to just get a 3rd party for this
101
+ def parse diff, client, url, path
102
+
103
+ # would be nice to just get a 3rd party for this
86
104
 
87
- raise ConflictException::new("client cannot by empty") if nil.eql?(client) || client.empty?
88
- raise ConflictException::new("diff cannot by empty") if nil.eql?(diff) || diff.empty?
105
+ raise ConflictException::new("client cannot by empty") if client.nil? || client.empty?
106
+ raise ConflictException::new("diff cannot by empty") if diff.nil? || diff.empty?
107
+ raise ConflictException::new("url value cannot be nil or empty") if url.nil? or url.empty?
108
+ raise ConflictException::new("path value cannot be nil or empty") if path.nil? or path.empty?
109
+
89
110
  events = []
90
111
  resource = ""
91
112
  lines = diff.split(%r{\n})
@@ -101,7 +122,7 @@ class DiffParser
101
122
  else
102
123
  if line.to_s.index("@@ ") == 0
103
124
  action = infer_action line
104
- resource_full = infer_resource(@cfg[:url].to_s, @cfg[:path], resource)
125
+ resource_full = infer_resource(url.to_s, path, resource)
105
126
  events << Event::new(client, action, resource_full, now, @cfg[:ttl])
106
127
  bit = ! bit
107
128
  end
@@ -111,6 +132,29 @@ class DiffParser
111
132
  events
112
133
  end
113
134
 
135
+ def parse_cvs diff, client
136
+
137
+ raise ConflictException::new("client cannot by empty") if client.nil? || client.empty?
138
+ raise ConflictException::new("diff cannot by empty") if diff.nil? || diff.empty?
139
+
140
+ events = []
141
+
142
+ lines = diff.split(%r{\n})
143
+ now = Time.now
144
+
145
+ lines.each do |line|
146
+
147
+ if line.to_s.index(@@key_cvs) == 0
148
+ resource = line.to_s.sub(@@key_cvs, '')
149
+ events << Event::new(client, 'changed', resource.slice!(0..(resource.index(",") - 1)), now, @cfg[:ttl])
150
+ end
151
+
152
+ end
153
+
154
+ events
155
+
156
+ end
157
+
114
158
  private
115
159
  def infer_action numbers # we are assuming a lot about unified diff here
116
160
  tokens = numbers.split(' ')
@@ -127,4 +171,4 @@ class DiffParser
127
171
 
128
172
  end
129
173
 
130
- end
174
+ end
data/test/common.rb CHANGED
@@ -1,132 +1,132 @@
1
- # Licensed to the Apache Software Foundation (ASF) under one
2
- # or more contributor license agreements. See the NOTICE file
3
- # distributed with this work for additional information
4
- # regarding copyright ownership. The ASF licenses this file
5
- # to you under the Apache License, Version 2.0 (the
6
- # "License"); you may not use this file except in compliance
7
- # with the License. You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing,
12
- # software distributed under the License is distributed on an
13
- # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
- # KIND, either express or implied. See the License for the
15
- # specific language governing permissions and limitations
16
- # under the License.
17
-
18
- require 'test/unit'
19
- require 'net/http'
20
- module Conflict
21
-
22
- class DataBase
23
- def presence_of item
24
- @db.index(item)
25
- end
26
- def insert item
27
- @db << item
28
- end
29
- end
30
-
31
- class Client
32
-
33
- def initialize cfg={}
34
- @cfg = {}
35
- @cfg[:command] = cfg[:command] ||= "svn diff"
36
- @cfg[:id] = cfg[:id] ||= "Conflict Ruby client"
37
- @cfg[:ip] = cfg[:ip] ||= "127.0.0.1"
38
- @cfg[:port] = cfg[:port] ||= PORT
39
- @cfg[:poll] = cfg[:poll] ||= 3
40
- end
41
-
42
- def resend
43
- raise ConflictException::new('do not call resend before calling send') if ! defined?(@last_diff) or ! defined?(@last_info)
44
- diff @last_diff, @last_info
45
- end
46
-
47
- def diff diff, info
48
-
49
- raise ConflictException::new('info cannot be nil or empty') if nil.eql?(info) || info.empty?
50
-
51
- response = multi_diff [diff], [info] # a multi diff of one
52
- @last_diff, @last_info = diff, info
53
- response
54
- end
55
-
56
- def multi_diff diff, info
57
- body = {"client" => @cfg[:id]}
58
- diff.each do | d | body = body.merge("diff"=>d, "path"=>".") end
59
- info.each do | i | body = body.merge("info"=>i) end
60
- YAML.load send(DIFF_URL, body)
61
- end
62
-
63
- def revert info
64
- diff("", info)
65
- end
66
-
67
- #make this static?
68
- def count
69
- body = send(STATUS_URL, {})
70
- document = REXML::Document.new(body)
71
- document.root.elements['events'].size
72
- end
73
-
74
- def poll
75
-
76
- while true
77
- diff = `#{@cfg[:command]}`
78
- info = `svn info`
79
- puts multi_diff([diff], [info]).to_yaml
80
- sleep @cfg[:poll]
81
- end
82
-
83
- end
84
-
85
- def to_s
86
- self.class.to_s + " " + @cfg.to_s
87
- end
88
-
89
- private
90
- def send url, body
91
- req = Net::HTTP::Post.new(url)
92
- req.set_form_data(body)
93
- http = Net::HTTP.new(@cfg[:ip],@cfg[:port])
94
- res = http.request(req)
95
- res.body
96
- end
97
-
98
- end
99
-
100
- end
101
-
102
- module ConflictConstants
103
-
104
- CLIENT_ONE = "unit test"
105
- CLIENT_TWO = "unit test 2"
106
- CLIENT_THREE = "unit test 3"
107
- NO_CONFLICT = [].to_yaml
108
- CONTROLLER = "com.thoughtworks.Controller.java"
109
- VIEW = "com.thoughtworks.View.java"
110
- MODEL = "com.thoughtworks.Model.java"
111
- DATA_DIR = './test/data/'
112
- STOMP_DIR = DATA_DIR + 'stomp/'
113
- STOMP_1_INFO_PATH = STOMP_DIR + 'stomp.1.info.txt'
114
- STOMP_2_INFO_PATH = STOMP_DIR + 'stomp.2.info.txt'
115
- STOMP_1_DIFF_PATH = STOMP_DIR + 'stomp.1.diff.txt'
116
- STOMP_2_DIFF_PATH = STOMP_DIR + 'stomp.2.diff.txt'
117
- STOMP_1_URL = 'http://svn.codehaus.org/stomp/ruby/trunk'
118
- STOMP_2_URL = 'http://svn.codehaus.org/stomp/ruby/trunk/lib'
119
- STOMP_RESOURCE = STOMP_2_URL + "/stomp.rb"
120
- CONFLICT_URL = 'https://conflict.svn.sourceforge.net/svnroot/conflict/src/ruby'
121
-
122
- end
123
-
124
- class File
125
- def to_s
126
- string = ""
127
- self.each do | line | string += line.to_s end
128
- self.close
129
- string
130
- end
131
- end
132
-
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'test/unit'
19
+ require 'net/http'
20
+ module Conflict
21
+
22
+ class DataBase
23
+ def presence_of item
24
+ @db.index(item)
25
+ end
26
+ def insert item
27
+ @db << item
28
+ end
29
+ end
30
+
31
+ class Client
32
+
33
+ def initialize cfg={}
34
+ @cfg = {}
35
+ @cfg[:command] = cfg[:command] ||= "svn diff"
36
+ @cfg[:id] = cfg[:id] ||= "Conflict Ruby client"
37
+ @cfg[:ip] = cfg[:ip] ||= "127.0.0.1"
38
+ @cfg[:port] = cfg[:port] ||= PORT
39
+ @cfg[:poll] = cfg[:poll] ||= 3
40
+ end
41
+
42
+ def resend
43
+ raise ConflictException::new('do not call resend before calling send') if ! defined?(@last_diff) or ! defined?(@last_info)
44
+ diff @last_diff, @last_info
45
+ end
46
+
47
+ def diff diff, info
48
+
49
+ raise ConflictException::new('info cannot be nil or empty') if nil.eql?(info) || info.empty?
50
+
51
+ response = multi_diff [diff], [info] # a multi diff of one
52
+ @last_diff, @last_info = diff, info
53
+ response
54
+ end
55
+
56
+ def multi_diff diff, info
57
+ body = {"client" => @cfg[:id]}
58
+ diff.each do | d | body = body.merge("diff"=>d, "path"=>".") end
59
+ info.each do | i | body = body.merge("info"=>i) end
60
+ YAML.load send(DIFF_URL, body)
61
+ end
62
+
63
+ def revert info
64
+ diff("", info)
65
+ end
66
+
67
+ #make this static?
68
+ def count
69
+ body = send(STATUS_URL, {})
70
+ document = REXML::Document.new(body)
71
+ document.root.elements['events'].size
72
+ end
73
+
74
+ def poll
75
+
76
+ while true
77
+ diff = `#{@cfg[:command]}`
78
+ info = `svn info`
79
+ puts multi_diff([diff], [info]).to_yaml
80
+ sleep @cfg[:poll]
81
+ end
82
+
83
+ end
84
+
85
+ def to_s
86
+ self.class.to_s + " " + @cfg.to_s
87
+ end
88
+
89
+ private
90
+ def send url, body
91
+ req = Net::HTTP::Post.new(url)
92
+ req.set_form_data(body)
93
+ http = Net::HTTP.new(@cfg[:ip],@cfg[:port])
94
+ res = http.request(req)
95
+ res.body
96
+ end
97
+
98
+ end
99
+
100
+ end
101
+
102
+ module ConflictConstants
103
+
104
+ CLIENT_ONE = "unit test"
105
+ CLIENT_TWO = "unit test 2"
106
+ CLIENT_THREE = "unit test 3"
107
+ NO_CONFLICT = [].to_yaml
108
+ CONTROLLER = "com.thoughtworks.Controller.java"
109
+ VIEW = "com.thoughtworks.View.java"
110
+ MODEL = "com.thoughtworks.Model.java"
111
+ DATA_DIR = './test/data/'
112
+ STOMP_DIR = DATA_DIR + 'stomp/'
113
+ STOMP_1_INFO_PATH = STOMP_DIR + 'stomp.1.info.txt'
114
+ STOMP_2_INFO_PATH = STOMP_DIR + 'stomp.2.info.txt'
115
+ STOMP_1_DIFF_PATH = STOMP_DIR + 'stomp.1.diff.txt'
116
+ STOMP_2_DIFF_PATH = STOMP_DIR + 'stomp.2.diff.txt'
117
+ STOMP_1_URL = 'http://svn.codehaus.org/stomp/ruby/trunk'
118
+ STOMP_2_URL = 'http://svn.codehaus.org/stomp/ruby/trunk/lib'
119
+ STOMP_RESOURCE = STOMP_2_URL + "/stomp.rb"
120
+ CONFLICT_URL = 'https://conflict.svn.sourceforge.net/svnroot/conflict/src/ruby'
121
+
122
+ end
123
+
124
+ class File
125
+ def to_s
126
+ string = ""
127
+ self.each do | line | string += line.to_s end
128
+ self.close
129
+ string
130
+ end
131
+ end
132
+