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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.tm_properties +2 -1
  3. data/VERSION +1 -1
  4. data/app/controllers/card_controller.rb +4 -3
  5. data/db/bootstrap/card_acts.yml +1 -1
  6. data/db/bootstrap/cards.yml +1028 -1028
  7. data/db/migrate_core_cards/20140629222005_add_email_cards.rb +2 -25
  8. data/db/migrate_core_cards/20141204061304_watchers_to_following.rb +38 -0
  9. data/db/version_core_cards.txt +1 -1
  10. data/features/history.feature +21 -0
  11. data/features/step_definitions/history_steps.rb +3 -0
  12. data/features/step_definitions/wagn_steps.rb +10 -1
  13. data/lib/card/act.rb +2 -2
  14. data/lib/card/action.rb +9 -9
  15. data/lib/card/diff.rb +370 -211
  16. data/lib/card/exceptions.rb +2 -0
  17. data/lib/card/format.rb +5 -3
  18. data/lib/card/query.rb +4 -4
  19. data/lib/card/query/card_spec.rb +69 -51
  20. data/lib/card/set.rb +3 -2
  21. data/lib/wagn.rb +15 -3
  22. data/lib/wagn/all.rb +1 -3
  23. data/lib/wagn/application.rb +10 -1
  24. data/lib/wagn/generators/wagn/templates/Gemfile +6 -1
  25. data/lib/wagn/log.rb +69 -0
  26. data/lib/wagn/tasks/wagn.rake +1 -1
  27. data/mod/01_core/set/all/collection.rb +8 -5
  28. data/mod/01_core/set/all/content.rb +1 -1
  29. data/mod/01_core/set/all/fetch.rb +34 -32
  30. data/mod/01_core/set/all/notify.rb +2 -2
  31. data/mod/01_core/set/all/phases.rb +5 -0
  32. data/mod/01_core/set/all/trash.rb +1 -1
  33. data/mod/02_basic_types/set/type/pointer.rb +4 -0
  34. data/mod/03_machines/set/type/coffee_script.rb +10 -0
  35. data/mod/03_machines/set/type/css.rb +9 -0
  36. data/mod/03_machines/set/type/java_script.rb +5 -0
  37. data/mod/03_machines/set/type/scss.rb +8 -2
  38. data/mod/05_standard/set/all/history.rb +10 -1
  39. data/mod/05_standard/set/type/html.rb +4 -0
  40. data/mod/05_standard/spec/set/all/base_spec.rb +2 -0
  41. data/mod/05_standard/spec/set/all/history_spec.rb +12 -0
  42. data/mod/06_email/set/all/email_html.rb +0 -4
  43. data/mod/06_email/set/type/email_template.rb +6 -2
  44. data/spec/lib/card/diff_spec.rb +207 -107
  45. data/spec/lib/card/query_spec.rb +14 -13
  46. data/test/fixtures/card_actions.yml +21 -0
  47. data/test/fixtures/card_acts.yml +103 -85
  48. data/test/fixtures/card_changes.yml +53 -23
  49. data/test/fixtures/cards.yml +1349 -1331
  50. data/test/seed.rb +5 -1
  51. metadata +8 -3
  52. 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
@@ -1 +1 @@
1
- 20141120120605
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
+
@@ -0,0 +1,3 @@
1
+ When /^I expand act (\d+)$/ do |index|
2
+ all("a.arrow-right")[-index.to_i].click
3
+ end
@@ -150,7 +150,8 @@ end
150
150
 
151
151
  Then /debug/ do
152
152
  if RUBY_VERSION =~ /^2/
153
- byebug
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
@@ -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 = 0 OR draft IS null) OR actor_id = :current_user_id)'
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 = 0 OR draft IS null) )'
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
@@ -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::DiffBuilder.new(old_values[:name],new_values[:name]).complete
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::DiffBuilder.new(old_values[:cardtype],new_values[:cardtype]).complete
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], :compare_html=>false)
187
+ Card::Diff::DiffBuilder.new(old_values[:content], new_values[:content], opts || card.diff_args)
188
188
  end
189
189
  end
190
190
 
@@ -1,12 +1,12 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module Card::Diff
3
3
 
4
- def diff_complete(a, b)
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 diff_summary(a, b)
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
- @old_version, @new_version = old_version, new_version
23
- @opts = opts
24
- @new_version ||= ''
25
- if !opts[:compare_html]
26
- @old_version.gsub! /<[^>]*>/,'' if @old_version
27
- @new_version.gsub! /<[^>]*>/,''
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
- @old_version = CGI::escapeHTML(@old_version) if @old_version
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
- complete and @dels > 0
59
+ @dels_cnt > 0
41
60
  end
61
+
42
62
  def green?
43
- complete and @adds > 0
63
+ @adds_cnt > 0
44
64
  end
45
65
 
46
- def summary max_length = 50, joint = '...'
47
- @summary ||= begin
48
- if @old_version
49
- last_position = 0
50
- remaining_chars = max_length
51
- res = ''
52
- new_aggregated_lcs.each do |change|
53
- if change[:position] > last_position
54
- res += joint
55
- end
56
- res += render_chunk change[:action], change[:text][0..remaining_chars], false
57
- remaining_chars -= change[:text].size
58
- if remaining_chars < 0 # no more space left
59
- res += joint
60
- break
61
- end
62
- last_position = change[:position]
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
- res
116
+ text = postprocess list.join
117
+ @result = added_chunk text
118
+ @summary.add text
65
119
  else
66
- res = @new_version[0..max_length]
67
- res += joint if @new_version.size > max_length
68
- added_chunk(res, false)
120
+ init_diff old_text, new_text
121
+ run_diff
69
122
  end
70
123
  end
71
- end
72
-
73
- def complete
74
- @complete ||= begin
75
- clear_stats
76
- if @old_version
77
- if @old_version.size < 1000
78
- better_complete_lcs_diff
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
- fast_diff
181
+ write_excludees
81
182
  end
82
- else
83
- added_chunk(@new_version)
84
- end
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
- def render_chunk action, text, count=true
107
- case action
108
- when '+' then added_chunk(text,count)
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
- def better_complete_lcs_diff old_v=@old_version, new_v=@new_version
118
- old_v = old_v.split(' ')
119
- new_v = new_v.split(' ')
120
- res = ''
121
- dels = []
122
- adds = []
123
- prev_action = nil
124
- ::Diff::LCS.traverse_balanced(old_v, new_v) do |chunk|
125
- if prev_action and prev_action != chunk.action and
126
- !(prev_action == '-' and chunk.action == '!') and
127
- !(prev_action == '!' and chunk.action == '+')
128
-
129
- if dels.present?
130
- res << deleted_chunk(dels.join(' '))
131
- dels = []
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
- if !adds.empty?
134
- res << added_chunk(adds.join(' '))
135
- adds = []
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
- case chunk.action
140
- when '-' then dels << chunk.old_element
141
- when '+' then adds << chunk.new_element
142
- when '!'
143
- dels << chunk.old_element
144
- adds << chunk.new_element
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
- res += ' ' + chunk.new_element
273
+ write_unchanged new_element
274
+ @excludees[:new].word_step
147
275
  end
148
- prev_action = chunk.action
149
276
  end
150
- res += deleted_chunk(dels.join(' ')) if dels.present?
151
- res += added_chunk(adds.join(' ')) if adds.present?
152
- res
153
- end
154
-
155
-
156
-
157
- def complete_lcs_diff old_v=@old_version, new_v=@new_version
158
- last_position = 0
159
- res = ''
160
- dels = ''
161
- adds = ''
162
- prev_action = nil
163
- ::Diff::LCS.traverse_balanced(old_v, new_v) do |chunk|
164
- if prev_action and prev_action != chunk.action and
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
- res += chunk.new_element
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
- # combines diffy and lcs:
199
- # find with diffy line changes
200
- # whenever added lines follow immediately after deleted lines compare them with lcs
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
- if inspect
220
- if action != :added
221
- res += better_complete_lcs_diff lines[:deleted].join, lines[:added].join
222
- inspect = false
223
- lines[:deleted].clear
224
- lines[:added].clear
225
- end
226
- elsif prev_action and action != prev_action
227
- text = lines[prev_action].join
228
- res += render_chunk prev_action, text
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
- res += if inspect
235
- better_complete_lcs_diff lines[:deleted].join, lines[:added].join
236
- elsif lines[prev_action].present?
237
- render_chunk prev_action, lines[prev_action].join
238
- else
239
- ''
318
+ def postprocess text
319
+ if @postprocess
320
+ @postprocess.call(text)
321
+ else
322
+ text
323
+ end
240
324
  end
241
- end
242
-
243
-
244
- def new_aggregated_lcs
245
- new_lcs.inject([]) do |res, change_block|
246
- last_action = nil
247
- change_block.each do |change|
248
- if change.action != last_action
249
- res << { :position => change.position,
250
- :action => change.action,
251
- :text => change.element
252
- }
253
- else
254
- res.last[:text] += change.element
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
- end
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
+