wagn 1.14.1 → 1.14.2
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.
- checksums.yaml +4 -4
- data/.tm_properties +2 -1
- data/VERSION +1 -1
- data/app/controllers/card_controller.rb +4 -3
- data/db/bootstrap/card_acts.yml +1 -1
- data/db/bootstrap/cards.yml +1028 -1028
- data/db/migrate_core_cards/20140629222005_add_email_cards.rb +2 -25
- data/db/migrate_core_cards/20141204061304_watchers_to_following.rb +38 -0
- data/db/version_core_cards.txt +1 -1
- data/features/history.feature +21 -0
- data/features/step_definitions/history_steps.rb +3 -0
- data/features/step_definitions/wagn_steps.rb +10 -1
- data/lib/card/act.rb +2 -2
- data/lib/card/action.rb +9 -9
- data/lib/card/diff.rb +370 -211
- data/lib/card/exceptions.rb +2 -0
- data/lib/card/format.rb +5 -3
- data/lib/card/query.rb +4 -4
- data/lib/card/query/card_spec.rb +69 -51
- data/lib/card/set.rb +3 -2
- data/lib/wagn.rb +15 -3
- data/lib/wagn/all.rb +1 -3
- data/lib/wagn/application.rb +10 -1
- data/lib/wagn/generators/wagn/templates/Gemfile +6 -1
- data/lib/wagn/log.rb +69 -0
- data/lib/wagn/tasks/wagn.rake +1 -1
- data/mod/01_core/set/all/collection.rb +8 -5
- data/mod/01_core/set/all/content.rb +1 -1
- data/mod/01_core/set/all/fetch.rb +34 -32
- data/mod/01_core/set/all/notify.rb +2 -2
- data/mod/01_core/set/all/phases.rb +5 -0
- data/mod/01_core/set/all/trash.rb +1 -1
- data/mod/02_basic_types/set/type/pointer.rb +4 -0
- data/mod/03_machines/set/type/coffee_script.rb +10 -0
- data/mod/03_machines/set/type/css.rb +9 -0
- data/mod/03_machines/set/type/java_script.rb +5 -0
- data/mod/03_machines/set/type/scss.rb +8 -2
- data/mod/05_standard/set/all/history.rb +10 -1
- data/mod/05_standard/set/type/html.rb +4 -0
- data/mod/05_standard/spec/set/all/base_spec.rb +2 -0
- data/mod/05_standard/spec/set/all/history_spec.rb +12 -0
- data/mod/06_email/set/all/email_html.rb +0 -4
- data/mod/06_email/set/type/email_template.rb +6 -2
- data/spec/lib/card/diff_spec.rb +207 -107
- data/spec/lib/card/query_spec.rb +14 -13
- data/test/fixtures/card_actions.yml +21 -0
- data/test/fixtures/card_acts.yml +103 -85
- data/test/fixtures/card_changes.yml +53 -23
- data/test/fixtures/cards.yml +1349 -1331
- data/test/seed.rb +5 -1
- metadata +8 -3
- data/lib/wagn/config/initializers/airbrake.rb +0 -9
@@ -125,36 +125,13 @@ class AddEmailCards < Wagn::CoreMigration
|
|
125
125
|
Card.create! :name => '*following+*right+*default', :type_code=>:pointer
|
126
126
|
Card.create! :name => '*following+*right+*update', :content=>'_left'
|
127
127
|
Card.create! :name => '*following+*right+*create', :content=>'_left'
|
128
|
-
Card::Codename.reset_cache
|
129
|
-
|
130
|
-
# move old watch rules
|
131
|
-
# +watchers
|
132
|
-
follower_hash = Hash.new { |h, v| h[v] = [] }
|
133
128
|
|
134
|
-
Card.search(:right_plus => {:codename=> "watchers"}).each do |card|
|
135
|
-
if watched = card.left
|
136
|
-
card.item_names.each do |user_name|
|
137
|
-
follower_hash[user_name] << watched.name
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
follower_hash.each do |user, items|
|
143
|
-
if card=Card.fetch(user) and card.account
|
144
|
-
following = card.fetch :trait=>"following", :new=>{}
|
145
|
-
following.items = items
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
if watchers = Card[:watchers]
|
150
|
-
watchers.update_attributes :codename=>nil
|
151
|
-
watchers.delete!
|
152
|
-
end
|
153
|
-
|
154
129
|
if send = Card[:send]
|
155
130
|
send.update_attributes :codename=>nil
|
156
131
|
send.delete!
|
157
132
|
end
|
133
|
+
|
134
|
+
|
158
135
|
end
|
159
136
|
end
|
160
137
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
class WatchersToFollowing < Wagn::CoreMigration
|
4
|
+
def up
|
5
|
+
|
6
|
+
follower_hash = Hash.new { |h, v| h[v] = [] }
|
7
|
+
|
8
|
+
#NOTE: this migration must find cards in the trash, because the original (1.14.0) migration attempt
|
9
|
+
# did not successfully migration to the +*following card but did successfully delete +*watchers cards.
|
10
|
+
# Therefore cards migrated using 1.14.0 or 1.14.1 will not have the correct migrations
|
11
|
+
|
12
|
+
if watcher_card = Card.find_by_key('*watcher')
|
13
|
+
Card.find_by_sql("select * from cards where right_id = #{watcher_card.id}").each do |card|
|
14
|
+
card.include_set_modules
|
15
|
+
|
16
|
+
if watched = card.left
|
17
|
+
card.item_names.each do |user_name|
|
18
|
+
follower_hash[user_name] << watched.name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
follower_hash.each do |user, items|
|
24
|
+
if card=Card.fetch(user) and card.account
|
25
|
+
following = card.fetch :trait=>"following", :new=>{}
|
26
|
+
items.each { |item| following.add_item item }
|
27
|
+
following.save!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if watchers = Card[:watchers]
|
33
|
+
watchers.update_attributes :codename=>nil
|
34
|
+
watchers.delete!
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/db/version_core_cards.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
20141204061304
|
@@ -0,0 +1,21 @@
|
|
1
|
+
@javascript
|
2
|
+
Feature: History
|
3
|
+
As an editor
|
4
|
+
I want to be able to browse through the history and save an old version as current.
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I am signed in as Joe Admin
|
8
|
+
Then the card First should not contain "chicken"
|
9
|
+
|
10
|
+
Scenario: view history and rollback
|
11
|
+
When I go to url "/First?view=history"
|
12
|
+
Then In the main card content I should see a del with content "egg"
|
13
|
+
|
14
|
+
When I expand act 2
|
15
|
+
And In the main card content I click "Hide changes"
|
16
|
+
Then In the main card content I should not see a del with content "egg"
|
17
|
+
|
18
|
+
When In the main card content I click "Save as current"
|
19
|
+
Then the card First should contain "chicken"
|
20
|
+
|
21
|
+
|
@@ -150,7 +150,8 @@ end
|
|
150
150
|
|
151
151
|
Then /debug/ do
|
152
152
|
if RUBY_VERSION =~ /^2/
|
153
|
-
|
153
|
+
require 'pry'
|
154
|
+
binding.pry
|
154
155
|
else
|
155
156
|
debugger
|
156
157
|
end
|
@@ -251,6 +252,14 @@ Then /^In (.*) I should (not )?see a ([^\"]*) with class "([^\"]*)"$/ do |select
|
|
251
252
|
end
|
252
253
|
end
|
253
254
|
|
255
|
+
Then /^In (.*) I should (not )?see a ([^\"]*) with content "([^\"]*)"$/ do |selection, neg, element, content|
|
256
|
+
# checks for existence of a element with a class in a selection context
|
257
|
+
element = 'a' if element == 'link'
|
258
|
+
within scope_of(selection) do
|
259
|
+
page.send( ( neg ? :should_not : :should ), have_css( element, :text=>content ) )
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
254
263
|
Then /^the "([^"]*)" field should contain "([^"]*)"$/ do |field, value|
|
255
264
|
expect(field_labeled(field).value).to match(/#{value}/)
|
256
265
|
end
|
data/lib/card/act.rb
CHANGED
@@ -18,9 +18,9 @@ class Card
|
|
18
18
|
|
19
19
|
def self.find_all_with_actions_on card_ids, args={}
|
20
20
|
sql = if args[:with_drafts]
|
21
|
-
'card_actions.card_id IN (:card_ids) AND ( (draft
|
21
|
+
'card_actions.card_id IN (:card_ids) AND ( (draft is false OR draft IS null) OR actor_id = :current_user_id)'
|
22
22
|
else
|
23
|
-
'card_actions.card_id IN (:card_ids) AND ( (draft
|
23
|
+
'card_actions.card_id IN (:card_ids) AND ( (draft is false OR draft IS null) )'
|
24
24
|
end
|
25
25
|
Card::Act.joins(:actions).where(sql,
|
26
26
|
{:card_ids => card_ids, :current_user_id=>Card::Auth.current_id }).uniq.order(:id).reverse_order
|
data/lib/card/action.rb
CHANGED
@@ -160,31 +160,31 @@ class Card
|
|
160
160
|
# end
|
161
161
|
|
162
162
|
|
163
|
-
def name_diff
|
163
|
+
def name_diff opts={}
|
164
164
|
if new_name?
|
165
|
-
Card::Diff
|
165
|
+
Card::Diff.complete old_values[:name], new_values[:name], opts
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
169
|
-
def cardtype_diff
|
169
|
+
def cardtype_diff opts={}
|
170
170
|
if new_type?
|
171
|
-
Card::Diff
|
171
|
+
Card::Diff.complete old_values[:cardtype], new_values[:cardtype], opts
|
172
172
|
end
|
173
173
|
end
|
174
174
|
|
175
|
-
def content_diff diff_type=:expanded
|
175
|
+
def content_diff diff_type=:expanded, opts=nil
|
176
176
|
if new_content?
|
177
177
|
if diff_type == :summary
|
178
|
-
content_diff_builder.summary
|
178
|
+
content_diff_builder(opts).summary
|
179
179
|
else
|
180
|
-
content_diff_builder.complete
|
180
|
+
content_diff_builder(opts).complete
|
181
181
|
end
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
185
|
-
def content_diff_builder
|
185
|
+
def content_diff_builder opts=nil
|
186
186
|
@content_diff_builder ||= begin
|
187
|
-
Card::Diff::DiffBuilder.new(old_values[:content], new_values[:content],
|
187
|
+
Card::Diff::DiffBuilder.new(old_values[:content], new_values[:content], opts || card.diff_args)
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
data/lib/card/diff.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
module Card::Diff
|
3
3
|
|
4
|
-
def
|
5
|
-
DiffBuilder.new(a, b).complete
|
4
|
+
def self.complete a, b, opts={}
|
5
|
+
DiffBuilder.new(a, b, opts).complete
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
DiffBuilder.new(a, b).summary
|
8
|
+
def self.summary a, b, opts={}
|
9
|
+
DiffBuilder.new(a, b, opts).summary
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.render_added_chunk text
|
@@ -16,255 +16,414 @@ module Card::Diff
|
|
16
16
|
def self.render_deleted_chunk text, count=true
|
17
17
|
"<del class='diffdel diff-red'>#{text}</del>"
|
18
18
|
end
|
19
|
+
|
20
|
+
def self.render_chunk action, text
|
21
|
+
case action
|
22
|
+
when '+' then render_added_chunk text
|
23
|
+
when :added then render_added_chunk text
|
24
|
+
when '-' then render_deleted_chunk text
|
25
|
+
when :deleted then render_deleted_chunk text
|
26
|
+
else text
|
27
|
+
end
|
28
|
+
end
|
19
29
|
|
20
30
|
class DiffBuilder
|
31
|
+
attr_reader :summary, :complete
|
32
|
+
|
33
|
+
# diff options
|
34
|
+
# :format => :html|:text|:pointer|:raw
|
35
|
+
# :html = maintain html structure, but compare only content
|
36
|
+
# :text = remove all html tags; compare plain text
|
37
|
+
# :pointer = remove all double square brackets
|
38
|
+
# :raw = escape html tags and compare everything
|
39
|
+
#
|
40
|
+
# :summary => {:length=><number> , :joint=><string> }
|
41
|
+
|
21
42
|
def initialize(old_version, new_version, opts={})
|
22
|
-
@
|
23
|
-
@
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
|
43
|
+
@new_version = new_version
|
44
|
+
@old_version = old_version
|
45
|
+
@lcs_opts = lcs_opts_for_format opts[:format]
|
46
|
+
@lcs_opts[:summary] = opts[:summary]
|
47
|
+
@dels_cnt = 0
|
48
|
+
@adds_cnt = 0
|
49
|
+
|
50
|
+
if not @new_version
|
51
|
+
@complete = ''
|
52
|
+
@summary = ''
|
28
53
|
else
|
29
|
-
|
30
|
-
@new_version = CGI::escapeHTML(@new_version)
|
54
|
+
lcs_diff
|
31
55
|
end
|
32
|
-
@summary = false
|
33
|
-
@complete = false
|
34
|
-
@adds = 0
|
35
|
-
@dels = 0
|
36
56
|
end
|
37
57
|
|
38
|
-
|
39
58
|
def red?
|
40
|
-
|
59
|
+
@dels_cnt > 0
|
41
60
|
end
|
61
|
+
|
42
62
|
def green?
|
43
|
-
|
63
|
+
@adds_cnt > 0
|
44
64
|
end
|
45
65
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
66
|
+
def lcs_opts_for_format format
|
67
|
+
opts = {}
|
68
|
+
case format
|
69
|
+
when :html
|
70
|
+
opts[:exclude] = /^</
|
71
|
+
when :text
|
72
|
+
opts[:reject] = /^</
|
73
|
+
opts[:postprocess] = Proc.new do |word|
|
74
|
+
word.gsub("\n",'<br>')
|
75
|
+
end
|
76
|
+
when :pointer
|
77
|
+
opts[:preprocess] = Proc.new do |word|
|
78
|
+
word.gsub('[[','').gsub(']]','')
|
79
|
+
end
|
80
|
+
else #:raw
|
81
|
+
opts[:preprocess] = Proc.new do |word|
|
82
|
+
CGI::escapeHTML(word)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
opts
|
86
|
+
end
|
87
|
+
|
88
|
+
def lcs_diff
|
89
|
+
@lcs = LCS.new(@old_version, @new_version, @lcs_opts)
|
90
|
+
@summary = @lcs.summary
|
91
|
+
@complete = @lcs.complete
|
92
|
+
@dels_cnt = @lcs.dels_cnt
|
93
|
+
@adds_cnt = @lcs.adds_cnt
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
class LCS
|
98
|
+
attr_reader :adds_cnt, :dels_cnt
|
99
|
+
def initialize old_text, new_text, opts, summary=nil
|
100
|
+
@reject_pattern = opts[:reject] # regex; remove match completely from diff
|
101
|
+
@exclude_pattern = opts[:exclude] # regex; put back to the result after diff
|
102
|
+
@preprocess = opts[:preprocess] # block; called with every word
|
103
|
+
@postprocess = opts[:postprocess] # block; called with complete diff
|
104
|
+
|
105
|
+
@adds_cnt = 0
|
106
|
+
@dels_cnt = 0
|
107
|
+
|
108
|
+
@splitters = %w( <[^>]+> \[\[[^\]]+\]\] \{\{[^}]+\}\} \s+ )
|
109
|
+
@disjunction_pattern = /^\s/
|
110
|
+
@summary ||= Summary.new opts[:summary]
|
111
|
+
if not old_text
|
112
|
+
list = split_and_preprocess(new_text)
|
113
|
+
if @exclude_pattern
|
114
|
+
list = list.reject{ |word| word.match @exclude_pattern }
|
63
115
|
end
|
64
|
-
|
116
|
+
text = postprocess list.join
|
117
|
+
@result = added_chunk text
|
118
|
+
@summary.add text
|
65
119
|
else
|
66
|
-
|
67
|
-
|
68
|
-
added_chunk(res, false)
|
120
|
+
init_diff old_text, new_text
|
121
|
+
run_diff
|
69
122
|
end
|
70
123
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
124
|
+
|
125
|
+
def summary
|
126
|
+
@summary.result
|
127
|
+
end
|
128
|
+
|
129
|
+
def complete
|
130
|
+
@result
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def init_diff old_text, new_text
|
136
|
+
@adds = []
|
137
|
+
@dels = []
|
138
|
+
@result = ''
|
139
|
+
old_words, old_ex = separate_comparables_from_excludees old_text
|
140
|
+
new_words, new_ex = separate_comparables_from_excludees new_text
|
141
|
+
|
142
|
+
@words = {
|
143
|
+
:old => old_words,
|
144
|
+
:new => new_words
|
145
|
+
}
|
146
|
+
@excludees = {
|
147
|
+
:old => ExcludeeIterator.new(old_ex),
|
148
|
+
:new => ExcludeeIterator.new(new_ex)
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
def run_diff
|
153
|
+
prev_action = nil
|
154
|
+
::Diff::LCS.traverse_balanced(@words[:old], @words[:new]) do |word|
|
155
|
+
|
156
|
+
if prev_action
|
157
|
+
if prev_action != word.action and
|
158
|
+
!(prev_action == '-' and word.action == '!') and
|
159
|
+
!(prev_action == '!' and word.action == '+')
|
160
|
+
|
161
|
+
# delete and/or add section stops here; write changes to result
|
162
|
+
write_dels
|
163
|
+
write_adds
|
164
|
+
|
165
|
+
write_excludees # new neutral section starts, we can just write excludees to result
|
166
|
+
|
167
|
+
else # current word belongs to edit of previous word
|
168
|
+
case word.action
|
169
|
+
when '-'
|
170
|
+
del_old_excludees
|
171
|
+
when '+'
|
172
|
+
add_new_excludees
|
173
|
+
when '!'
|
174
|
+
del_old_excludees
|
175
|
+
add_new_excludees
|
176
|
+
else
|
177
|
+
write_excludees
|
178
|
+
end
|
179
|
+
end
|
79
180
|
else
|
80
|
-
|
181
|
+
write_excludees
|
81
182
|
end
|
82
|
-
|
83
|
-
|
84
|
-
|
183
|
+
|
184
|
+
process_word word
|
185
|
+
prev_action = word.action
|
186
|
+
end
|
187
|
+
write_dels
|
188
|
+
write_adds
|
189
|
+
write_excludees
|
190
|
+
|
191
|
+
@result = postprocess @result
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
def added_chunk text, count=true
|
196
|
+
@adds_cnt += 1 if count
|
197
|
+
Card::Diff.render_added_chunk text
|
85
198
|
end
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
89
|
-
|
90
|
-
def clear_stats
|
91
|
-
@adds = 0
|
92
|
-
@dels = 0
|
93
|
-
end
|
94
|
-
|
95
|
-
def added_chunk text, count=true
|
96
|
-
@adds += 1 if count
|
97
|
-
Card::Diff.render_added_chunk text
|
98
|
-
end
|
99
|
-
|
100
|
-
def deleted_chunk text, count=true
|
101
|
-
@dels += 1 if count
|
102
|
-
Card::Diff.render_deleted_chunk text
|
103
|
-
end
|
104
|
-
|
105
199
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
when :added then added_chunk(text,count)
|
110
|
-
when '-' then deleted_chunk(text,count)
|
111
|
-
when :deleted then deleted_chunk(text,count)
|
112
|
-
else text
|
200
|
+
def deleted_chunk text, count=true
|
201
|
+
@dels_cnt += 1 if count
|
202
|
+
Card::Diff.render_deleted_chunk text
|
113
203
|
end
|
114
|
-
end
|
115
204
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
205
|
+
|
206
|
+
def write_unchanged text
|
207
|
+
@result << text
|
208
|
+
@summary.omit
|
209
|
+
end
|
210
|
+
|
211
|
+
def write_dels
|
212
|
+
if !@dels.empty?
|
213
|
+
@result << deleted_chunk(@dels.join)
|
214
|
+
@summary.delete @dels.join
|
215
|
+
@dels = []
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def write_adds
|
220
|
+
if !@adds.empty?
|
221
|
+
@result << added_chunk(@adds.join)
|
222
|
+
@summary.add @adds.join
|
223
|
+
@adds = []
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def write_excludees
|
228
|
+
while ex = @excludees[:new].next
|
229
|
+
@result << ex[:element]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def del_old_excludees
|
234
|
+
while ex = @excludees[:old].next
|
235
|
+
if ex[:type] == :disjunction
|
236
|
+
@dels << ex[:element]
|
237
|
+
else
|
238
|
+
write_dels
|
239
|
+
@result << ex[:element]
|
132
240
|
end
|
133
|
-
|
134
|
-
|
135
|
-
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def add_new_excludees
|
245
|
+
while ex = @excludees[:new].next
|
246
|
+
if ex[:type] == :disjunction
|
247
|
+
@adds << ex[:element]
|
248
|
+
else
|
249
|
+
write_adds
|
250
|
+
@result << ex[:element]
|
136
251
|
end
|
137
252
|
end
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
253
|
+
end
|
254
|
+
|
255
|
+
def process_word word
|
256
|
+
process_element word.old_element, word.new_element, word.action
|
257
|
+
end
|
258
|
+
|
259
|
+
def process_element old_element, new_element, action
|
260
|
+
case action
|
261
|
+
when '-'
|
262
|
+
@dels << old_element
|
263
|
+
@excludees[:old].word_step
|
264
|
+
when '+'
|
265
|
+
@adds << new_element
|
266
|
+
@excludees[:new].word_step
|
267
|
+
when '!'
|
268
|
+
@dels << old_element
|
269
|
+
@adds << new_element
|
270
|
+
@excludees[:old].word_step
|
271
|
+
@excludees[:new].word_step
|
145
272
|
else
|
146
|
-
|
273
|
+
write_unchanged new_element
|
274
|
+
@excludees[:new].word_step
|
147
275
|
end
|
148
|
-
prev_action = chunk.action
|
149
276
|
end
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
!(prev_action == '-' and chunk.action == '!') and
|
166
|
-
!(prev_action == '!' and chunk.action == '+')
|
167
|
-
|
168
|
-
if dels.present?
|
169
|
-
res += deleted_chunk(dels)
|
170
|
-
dels = ''
|
171
|
-
end
|
172
|
-
if !adds.empty?
|
173
|
-
res += added_chunk(adds)
|
174
|
-
adds = ''
|
277
|
+
|
278
|
+
def separate_comparables_from_excludees text
|
279
|
+
# return two arrays, one with all words, one with pairs (index in word list, html_tag)
|
280
|
+
list = split_and_preprocess text
|
281
|
+
if @exclude_pattern
|
282
|
+
list.each_with_index.inject([[],[]]) do |res, pair|
|
283
|
+
element, index = pair
|
284
|
+
if element.match @disjunction_pattern
|
285
|
+
res[1] << {:chunk_index=>index, :element=>element, :type=>:disjunction}
|
286
|
+
elsif element.match @exclude_pattern
|
287
|
+
res[1] << {:chunk_index=>index, :element=>element, :type=>:excludee}
|
288
|
+
else
|
289
|
+
res[0] << element
|
290
|
+
end
|
291
|
+
res
|
175
292
|
end
|
176
|
-
end
|
177
|
-
|
178
|
-
case chunk.action
|
179
|
-
when '-' then dels += chunk.old_element
|
180
|
-
when '+' then adds += chunk.new_element
|
181
|
-
when '!'
|
182
|
-
dels += chunk.old_element
|
183
|
-
adds += chunk.new_element
|
184
293
|
else
|
185
|
-
|
294
|
+
[list, []]
|
186
295
|
end
|
187
|
-
prev_action = chunk.action
|
188
296
|
end
|
189
|
-
res += deleted_chunk(dels) if dels.present?
|
190
|
-
res += added_chunk(adds) if adds.present?
|
191
|
-
res
|
192
|
-
end
|
193
|
-
|
194
|
-
def complete_diffy_diff
|
195
|
-
new_diffy.to_s(:html)
|
196
|
-
end
|
197
297
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
def fast_diff
|
202
|
-
lines = { :deleted => [], :added=>[], :unchanged=>[], :eof=>[] }
|
203
|
-
prev_action = nil
|
204
|
-
res = ''
|
205
|
-
inspect = false
|
206
|
-
new_diffy.each_chunk do |line|
|
207
|
-
action = case line
|
208
|
-
when /^\+/ then :added
|
209
|
-
when /^-/ then :deleted
|
210
|
-
when /^ / then :unchanged
|
211
|
-
else
|
212
|
-
next
|
213
|
-
end
|
214
|
-
lines[action] << line.sub(/^./,'')
|
215
|
-
if action == :added and prev_action == :deleted
|
216
|
-
inspect = true
|
298
|
+
def split_and_preprocess text
|
299
|
+
splitted = split_to_list_of_words(text).select do |s|
|
300
|
+
s.size > 0 and (!@reject_pattern or !s.match @reject_pattern)
|
217
301
|
end
|
302
|
+
@preprocess ? splitted.map {|s| @preprocess.call(s) } : splitted
|
303
|
+
end
|
218
304
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
lines[prev_action].clear
|
305
|
+
def split_to_list_of_words text
|
306
|
+
split_regex = /(#{@splitters.join '|'})/
|
307
|
+
text.split(split_regex)
|
308
|
+
end
|
309
|
+
|
310
|
+
def preprocess text
|
311
|
+
if @preprocess
|
312
|
+
@preprocess.call(text)
|
313
|
+
else
|
314
|
+
text
|
230
315
|
end
|
231
|
-
prev_action = action
|
232
316
|
end
|
233
317
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
318
|
+
def postprocess text
|
319
|
+
if @postprocess
|
320
|
+
@postprocess.call(text)
|
321
|
+
else
|
322
|
+
text
|
323
|
+
end
|
240
324
|
end
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
325
|
+
|
326
|
+
|
327
|
+
class Summary
|
328
|
+
def initialize opts
|
329
|
+
opts ||= {}
|
330
|
+
@remaining_chars = opts[:length] || 50
|
331
|
+
@joint = opts[:joint] || '...'
|
332
|
+
|
333
|
+
@summary = nil
|
334
|
+
@chunks = []
|
335
|
+
end
|
336
|
+
|
337
|
+
def result
|
338
|
+
@summary ||= render_chunks
|
339
|
+
end
|
340
|
+
|
341
|
+
def add text
|
342
|
+
add_chunk text, :added
|
343
|
+
end
|
344
|
+
|
345
|
+
def delete text
|
346
|
+
add_chunk text, :deleted
|
347
|
+
end
|
348
|
+
|
349
|
+
def omit
|
350
|
+
if @chunks.empty? or @chunks.last[:action] != :ellipsis
|
351
|
+
add_chunk @joint, :ellipsis
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
private
|
356
|
+
|
357
|
+
def add_chunk text, action
|
358
|
+
if @remaining_chars > 0
|
359
|
+
@chunks << {:action => action, :text=>text}
|
360
|
+
@remaining_chars -= text.size
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def render_chunks
|
365
|
+
truncate_overlap
|
366
|
+
@chunks.map do |chunk|
|
367
|
+
Card::Diff.render_chunk chunk[:action], chunk[:text]
|
368
|
+
end.join
|
369
|
+
end
|
370
|
+
|
371
|
+
def truncate_overlap
|
372
|
+
if @remaining_chars < 0
|
373
|
+
if @chunks.last[:action] == :ellipsis
|
374
|
+
@chunks.pop
|
375
|
+
@remaining_chars += @joint.size
|
376
|
+
end
|
377
|
+
|
378
|
+
index = @chunks.size - 1
|
379
|
+
while @remaining_chars < @joint.size and index >= 0
|
380
|
+
if @remaining_chars + @chunks[index][:text].size == @joint.size # d
|
381
|
+
@chunks.pop
|
382
|
+
if index-1 >= 0
|
383
|
+
if @chunks[index-1][:action] == :added
|
384
|
+
@chunks << {:action => :ellipsis, :text=>@joint}
|
385
|
+
elsif @chunks[index-1][:action] == :deleted
|
386
|
+
@chunks << {:action => :added, :text=>@joint}
|
387
|
+
end
|
388
|
+
end
|
389
|
+
break
|
390
|
+
elsif @remaining_chars + @chunks[index][:text].size > @joint.size
|
391
|
+
@chunks[index][:text] = @chunks[index][:text][0..(@remaining_chars-@joint.size-1)]
|
392
|
+
@chunks[index][:text] += @joint
|
393
|
+
break
|
394
|
+
else
|
395
|
+
@remaining_chars += @chunks[index][:text].size
|
396
|
+
@chunks.delete_at(index)
|
397
|
+
end
|
398
|
+
index -= 1
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
end
|
404
|
+
|
405
|
+
class ExcludeeIterator
|
406
|
+
def initialize list
|
407
|
+
@list = list
|
408
|
+
@index = 0
|
409
|
+
@chunk_index = 0
|
410
|
+
end
|
411
|
+
|
412
|
+
def word_step
|
413
|
+
@chunk_index += 1
|
414
|
+
end
|
415
|
+
|
416
|
+
def next
|
417
|
+
if @index < @list.size and @list[@index][:chunk_index] == @chunk_index
|
418
|
+
res = @list[@index]
|
419
|
+
@index += 1
|
420
|
+
@chunk_index +=1
|
421
|
+
res
|
255
422
|
end
|
256
|
-
last_action = change.action
|
257
423
|
end
|
258
|
-
res
|
259
424
|
end
|
260
|
-
|
261
|
-
|
262
|
-
def new_diffy
|
263
|
-
::Diffy::Diff.new(@old_version, @new_version)
|
264
|
-
end
|
265
|
-
|
266
|
-
def new_lcs
|
267
|
-
::Diff::LCS.diff(@old_version,@new_version)
|
425
|
+
|
268
426
|
end
|
269
427
|
end
|
270
428
|
end
|
429
|
+
|