content_spinning 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79295ec814e234e885f05f6a026177cc49384acf
4
- data.tar.gz: b0cee4376b527d0b7a1d1ef0373ff51e385fb8b4
3
+ metadata.gz: c13ac04efa5b45c6a59cf3bbea6a47ea9bace674
4
+ data.tar.gz: 32b6b31adbc605a54d3b7673250f3a307c3cc167
5
5
  SHA512:
6
- metadata.gz: 3b44c72fddeafdd9fda092eb44c38f2a30547f974be9771ebf7fe2e92085ebdaaf6ba25991ce213bd4eb1e1c9db4f619d787ff5986b9e2b26528fce5dd5b0381
7
- data.tar.gz: 22b09a4e78ccc4686a9387fe5b4313f35abf5d92bf7fe8ff490b2d6569424db83dac152770d66f05c7719ad518a027fc846bd10df7006656abad6e54787036bf
6
+ metadata.gz: f91381ee1a24e4218b403be1ce88cb2201e456077d110e7db07157af5163783d66a46906c13bdff0fb24badce6b3347809e0da338615c3fe7b03a8babb2827d7
7
+ data.tar.gz: e95ff3c4eb4435172f871ad9ce119c3fb915bb0cc1cdb0141d81a339475008046aac51042a37ed36e2d9f53e0fabb1a7475fc982b1f53883d440ac0bcd0c2036
@@ -2,7 +2,7 @@ module ContentSpinning
2
2
 
3
3
  module Version
4
4
 
5
- STRING = "0.1.0".freeze
5
+ STRING = "0.2.0".freeze
6
6
 
7
7
  end
8
8
 
@@ -2,84 +2,114 @@ require "content_spinning/core_ext/string"
2
2
 
3
3
  module ContentSpinning
4
4
 
5
+ SPIN_BEGIN_FOR_LEVEL = Hash.new { |h, level| h[level] = "__SPIN_BEGIN_#{level}__" }
6
+ SPIN_END_FOR_LEVEL = Hash.new { |h, level| h[level] = "__SPIN_END_#{level}__" }
7
+ SPIN_OR_FOR_LEVEL = Hash.new { |h, level| h[level] = "__SPIN_OR_#{level}__" }
8
+
9
+ PARTITIONNER_REGEXP_FOR_LEVEL = Hash.new do |h, level|
10
+ h[level] = /#{SPIN_BEGIN_FOR_LEVEL[level]}.+?#{SPIN_END_FOR_LEVEL[level]}/
11
+ end
12
+
5
13
  class << self
6
14
 
7
15
  def spin(text)
8
- text = text.dup
9
- text = clean(text)
10
- result = parse(text)
16
+ result = parse(clean(text))
17
+
18
+ contents = if result[:max_level] == 0
19
+ [result[:parsed]]
20
+ else
21
+ spin_a_level([result[:parsed]], level: result[:max_level])
22
+ end
23
+
24
+ contents.reject!(&:empty?)
25
+ contents.uniq!
11
26
 
12
- spin_all_level(result[:parsed], result[:max_level])
27
+ contents
13
28
  end
14
29
 
30
+ EMPTY_SPIN_REGEXP = /\{\|*\}/
31
+ ONE_CHOICE_SPIN_REGEXP = /\{([^\{\}\|]+)\}/
32
+
15
33
  def clean(text)
34
+ cleaned = text.dup
35
+
16
36
  loop do
17
- text_before_run = text.dup
37
+ text_before_run = cleaned.dup
18
38
 
19
39
  # Strip empty spin
20
- text.gsub!(/\{\|*\}/, "")
40
+ cleaned.gsub!(EMPTY_SPIN_REGEXP, "")
21
41
 
22
42
  # Remove spin with only one choice
23
- text.gsub!(/\{([^\{\}\|]+)\}/, '\1')
43
+ cleaned.gsub!(ONE_CHOICE_SPIN_REGEXP, '\1')
24
44
 
25
- break if text == text_before_run
45
+ break if cleaned == text_before_run
26
46
  end
27
47
 
28
- text
48
+ cleaned
29
49
  end
30
50
 
31
- def parse(text, level = 1)
32
- return { parsed: text, max_level: level - 1 } unless text.include?("{")
51
+ INNER_SPIN_REGEXP = /\{([^\{\}]+)\}/
33
52
 
34
- text.gsub!(/\{([^\{\}]+)\}/) do |match|
35
- match.gsub!(/\{/, "__SPIN_BEGIN_#{level}__")
36
- match.gsub!(/\}/, "__SPIN_END_#{level}__")
37
- match.gsub!(/\|/, "__SPIN_OR_#{level}__")
38
- end
53
+ def parse(text)
54
+ parsed = text.dup
39
55
 
40
- parse(text, level + 1)
41
- end
56
+ level = 0
57
+ loop do
58
+ level += 1
42
59
 
43
- def spin_a_level(text_or_array, level)
44
- content_array = text_or_array.is_a?(Array) ? text_or_array : [text_or_array]
60
+ modification_happened = parsed.gsub!(INNER_SPIN_REGEXP) do |match|
61
+ match.sub!("{", SPIN_BEGIN_FOR_LEVEL[level])
62
+ match.sub!("}", SPIN_END_FOR_LEVEL[level])
63
+ match.gsub!("|", SPIN_OR_FOR_LEVEL[level])
64
+ end
45
65
 
46
- spin_begin = "__SPIN_BEGIN_#{level}__"
47
- spin_end = "__SPIN_END_#{level}__"
48
- spin_or = "__SPIN_OR_#{level}__"
66
+ break unless modification_happened
67
+ end
68
+
69
+ { parsed: parsed, max_level: level - 1 }
70
+ end
49
71
 
50
- content_array.map! do |text|
51
- if text.include?(spin_begin)
52
- # Spin a first one
53
- before, vary, after = text.partition(Regexp.new(spin_begin + ".+?" + spin_end))
54
- vary.gsub!(Regexp.union(spin_begin, spin_end), "")
72
+ def spin_a_level(contents, level:)
73
+ contents.flat_map do |text|
74
+ parts = extract_parts(text, level: level)
55
75
 
56
- varies = vary.split(Regexp.new(spin_or), -1)
57
- varies.map! { |choice| before + choice + after }
76
+ parts.map! do |part|
77
+ spin_a_level(part, level: level - 1)
78
+ end if level >= 2
58
79
 
59
- # Continue spinning the level if there are other same level spin or just return
60
- if after.include?(spin_begin)
61
- spin_a_level(varies, level).flatten
62
- else
63
- varies
80
+ if parts.length > 1
81
+ parts[0].product(*parts[1..-1]).tap do |products|
82
+ products.map!(&:join)
64
83
  end
65
84
  else
66
- text
85
+ parts[0]
67
86
  end
68
87
  end
69
-
70
- content_array.flatten
71
88
  end
72
89
 
73
- def spin_all_level(text_or_array, from_level)
74
- content_array = text_or_array.is_a?(Array) ? text_or_array : [text_or_array]
90
+ private
75
91
 
76
- if from_level > 0
77
- (1..from_level).reverse_each do |level|
78
- content_array = spin_a_level(content_array, level)
79
- end
92
+ def extract_parts(text, level:)
93
+ parts = []
94
+
95
+ loop do
96
+ before, spin, after = text.partition(PARTITIONNER_REGEXP_FOR_LEVEL[level])
97
+
98
+ # Before
99
+ parts << [before] if before != ""
100
+
101
+ break if spin == ""
102
+
103
+ # Let's vary
104
+ spin.sub!(SPIN_BEGIN_FOR_LEVEL[level], "")
105
+ spin.sub!(SPIN_END_FOR_LEVEL[level], "")
106
+ parts << spin.split(SPIN_OR_FOR_LEVEL[level], -1)
107
+
108
+ # After
109
+ text = after
80
110
  end
81
111
 
82
- content_array.delete_if(&:empty?).uniq
112
+ parts
83
113
  end
84
114
 
85
115
  end
@@ -37,6 +37,7 @@ describe ContentSpinning do
37
37
  it "keep legitimate spin" do
38
38
  expect(ContentSpinning.clean("{a|b}")).to eq("{a|b}")
39
39
  expect(ContentSpinning.clean("a{b|c}")).to eq("a{b|c}")
40
+ expect(ContentSpinning.clean("a{b|}")).to eq("a{b|}")
40
41
  expect(ContentSpinning.clean("{{a|b}|c}")).to eq("{{a|b}|c}")
41
42
  end
42
43
  end
@@ -89,19 +90,14 @@ describe ContentSpinning do
89
90
  expect(ContentSpinning.spin("{a}")).to eq(["a"])
90
91
  expect(ContentSpinning.spin("a{b}")).to eq(["ab"])
91
92
  expect(ContentSpinning.spin("a{b}c")).to eq(["abc"])
92
- expect(ContentSpinning.spin("{a}{b}")).to eq(["ab"])
93
- expect(ContentSpinning.spin("a{b}{c}d")).to eq(["abcd"])
94
93
  end
95
94
 
96
95
  it "manages two spin" do
97
- expect(ContentSpinning.spin("{a}")).to eq(["a"])
98
- expect(ContentSpinning.spin("a{b}")).to eq(["ab"])
99
- expect(ContentSpinning.spin("a{b}c")).to eq(["abc"])
100
96
  expect(ContentSpinning.spin("{a}{b}")).to eq(["ab"])
101
97
  expect(ContentSpinning.spin("a{b}{c}d")).to eq(["abcd"])
102
98
  end
103
99
 
104
- it "manages spin with an empty choice" do
100
+ it "manages spin with an empty choices" do
105
101
  expect(ContentSpinning.spin("{|a}")).to eq(["a"])
106
102
  expect(ContentSpinning.spin("{|a}b")).to eq(%w(b ab))
107
103
  expect(ContentSpinning.spin("{a|}b")).to eq(%w(ab b))
@@ -109,13 +105,14 @@ describe ContentSpinning do
109
105
  expect(ContentSpinning.spin("a{b|}{c|}d")).to eq(%w(abcd abd acd ad))
110
106
  end
111
107
 
112
- it "manages spin with two choice" do
108
+ it "manages spin with two choices" do
113
109
  expect(ContentSpinning.spin("{a|b}")).to eq(%w(a b))
114
110
  expect(ContentSpinning.spin("{a|b}c")).to eq(%w(ac bc))
115
111
  expect(ContentSpinning.spin("{a|b}{c|d}")).to eq(%w(ac ad bc bd))
112
+ expect(ContentSpinning.spin("{a|b}c{d|e}")).to eq(%w(acd ace bcd bce))
116
113
  end
117
114
 
118
- it "manages spin with three choice" do
115
+ it "manages spin with three choices" do
119
116
  expect(ContentSpinning.spin("{a|b|c}")).to eq(%w(a b c))
120
117
  expect(ContentSpinning.spin("{a|b|c}d")).to eq(%w(ad bd cd))
121
118
  expect(ContentSpinning.spin("{a|b|c}{d|e}")).to eq(%w(ad ae bd be cd ce))
@@ -125,6 +122,11 @@ describe ContentSpinning do
125
122
  expect(ContentSpinning.spin("{a{b|c}|d}")).to eq(%w(ab ac d))
126
123
  expect(ContentSpinning.spin("{{a|b}|c}")).to eq(%w(a b c))
127
124
  expect(ContentSpinning.spin("{a|{b|c}}")).to eq(%w(a b c))
125
+ expect(ContentSpinning.spin("{a|{b|c}}{d|e}")).to eq(%w(ad ae bd be cd ce))
126
+ end
127
+
128
+ it "manages recursive spin with empty choices" do
129
+ expect(ContentSpinning.spin("{a|{b|{c|{d|e}}}}")).to eq(%w(a b c d e))
128
130
  end
129
131
 
130
132
  it "does not return twice the same result" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: content_spinning
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxime Garcia