conflict 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+