content_spinning 0.2.0 → 0.3.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: c13ac04efa5b45c6a59cf3bbea6a47ea9bace674
4
- data.tar.gz: 32b6b31adbc605a54d3b7673250f3a307c3cc167
3
+ metadata.gz: 725d38ecb0875c5f125823a80e16105d65875327
4
+ data.tar.gz: feaff568e498f301547e05d5472e7ff4dd9ea4f0
5
5
  SHA512:
6
- metadata.gz: f91381ee1a24e4218b403be1ce88cb2201e456077d110e7db07157af5163783d66a46906c13bdff0fb24badce6b3347809e0da338615c3fe7b03a8babb2827d7
7
- data.tar.gz: e95ff3c4eb4435172f871ad9ce119c3fb915bb0cc1cdb0141d81a339475008046aac51042a37ed36e2d9f53e0fabb1a7475fc982b1f53883d440ac0bcd0c2036
6
+ metadata.gz: 1a1c3bfcd8dc80d0690326333d197e6f4ddae3f19e09abad7d318dddcece9c42be5e4d1448bef2eec17ac59335b6ae2de892fa548ea2bc89107952641d2ebd36
7
+ data.tar.gz: 51550db06f73c46870dfaedc4e90bf913b5cd8f4fbb0b2064d3b6e3bca77b7bf8480633a51f12325d3b4d5d12f2d774a9314a8afebf6966c4d5c52b69715dc57
data/README.md CHANGED
@@ -8,7 +8,7 @@ It manages nested spinning.
8
8
  ```ruby
9
9
  "Hi {there|you}! I'm {efficient|productive}.".spin
10
10
  # or
11
- ContentSpinning.spin "Hi {there|you}! I'm {efficient|productive}."
11
+ ContentSpinning.spin("Hi {there|you}! I'm {efficient|productive}.")
12
12
  ```
13
13
 
14
14
  returns this array :
@@ -28,6 +28,56 @@ returns this array :
28
28
  gem install content_spinning
29
29
  ```
30
30
 
31
+ ## Usage
32
+
33
+ ### All spins
34
+
35
+ Calculating the number of possibilities:
36
+
37
+ ```ruby
38
+ > ContentSpinning.new("Hi {there|you}! I'm {efficient|productive}.").count
39
+
40
+ 4
41
+ ```
42
+
43
+ Generating:
44
+
45
+ ```ruby
46
+ > ContentSpinning.new("Hi {there|you}! I'm {efficient|productive}.").spin
47
+
48
+ [
49
+ "Hi there! I'm efficient.",
50
+ "Hi there! I'm productive.",
51
+ "Hi you! I'm efficient.",
52
+ "Hi you! I'm productive."
53
+ ]
54
+ ```
55
+
56
+ Beware, spins being combinatory, generating all the spins could be quite long.
57
+
58
+ ### Partial spins
59
+
60
+ There is no guaranty of unicity among the results returned (this is random).
61
+ If you ask for a limit greater than the number of possibilities, this returns all the possibilities.
62
+
63
+ ```ruby
64
+ > ContentSpinning.new("Hi {there|you}! I'm {efficient|productive}.").spin(limit: 2)
65
+
66
+ [
67
+ "Hi there! I'm efficient.",
68
+ "Hi you! I'm productive."
69
+ ]
70
+
71
+ > ContentSpinning.new("Hi {there|you}! I'm {efficient|productive}.").spin(limit: 500)
72
+
73
+ [
74
+ "Hi there! I'm efficient.",
75
+ "Hi there! I'm productive.",
76
+ "Hi you! I'm efficient.",
77
+ "Hi you! I'm productive."
78
+ ]
79
+ ```
80
+
31
81
  ## Todo
32
82
 
33
83
  A few things to do :
@@ -1,117 +1,88 @@
1
1
  require "content_spinning/core_ext/string"
2
+ require "content_spinning/sentence"
3
+ require "content_spinning/spinner"
4
+ require "content_spinning/string"
2
5
 
3
- module ContentSpinning
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
6
+ class ContentSpinning
12
7
 
13
8
  class << self
14
9
 
15
- def spin(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!
26
-
27
- contents
10
+ def spin(source, limit: nil)
11
+ new(source).spin(limit: limit)
28
12
  end
29
13
 
30
- EMPTY_SPIN_REGEXP = /\{\|*\}/
31
- ONE_CHOICE_SPIN_REGEXP = /\{([^\{\}\|]+)\}/
32
-
33
- def clean(text)
34
- cleaned = text.dup
14
+ end
35
15
 
36
- loop do
37
- text_before_run = cleaned.dup
16
+ def initialize(source)
17
+ @source = source
18
+ end
38
19
 
39
- # Strip empty spin
40
- cleaned.gsub!(EMPTY_SPIN_REGEXP, "")
20
+ attr_reader :source
41
21
 
42
- # Remove spin with only one choice
43
- cleaned.gsub!(ONE_CHOICE_SPIN_REGEXP, '\1')
22
+ def count
23
+ parse.count
24
+ end
44
25
 
45
- break if cleaned == text_before_run
26
+ SPIN_END = "}"
27
+ SPIN_OR = "|"
28
+ SPIN_START = "{"
29
+
30
+ def parse
31
+ return @root if defined?(@root)
32
+
33
+ heap = [Sentence.new]
34
+
35
+ source.scan(/ [{}|] | [^{}|]+ /x).each do |part|
36
+ current = heap.last
37
+
38
+ if part == SPIN_START
39
+ spinner = ::ContentSpinning::Spinner.new
40
+ current << spinner
41
+ heap << spinner
42
+
43
+ sentence = ::ContentSpinning::Sentence.new
44
+ spinner << sentence
45
+ heap << sentence
46
+ elsif part == SPIN_OR
47
+ heap.pop
48
+ spinner = heap.last
49
+ sentence = ::ContentSpinning::Sentence.new
50
+ spinner << sentence
51
+ heap << sentence
52
+ elsif part == SPIN_END
53
+ heap.pop
54
+ heap.pop
55
+ else
56
+ current << ::ContentSpinning::String.new(part)
46
57
  end
47
-
48
- cleaned
49
58
  end
50
59
 
51
- INNER_SPIN_REGEXP = /\{([^\{\}]+)\}/
52
-
53
- def parse(text)
54
- parsed = text.dup
55
-
56
- level = 0
57
- loop do
58
- level += 1
59
-
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
65
-
66
- break unless modification_happened
67
- end
68
-
69
- { parsed: parsed, max_level: level - 1 }
70
- end
60
+ @root = heap.first.cleaned
61
+ end
71
62
 
72
- def spin_a_level(contents, level:)
73
- contents.flat_map do |text|
74
- parts = extract_parts(text, level: level)
75
-
76
- parts.map! do |part|
77
- spin_a_level(part, level: level - 1)
78
- end if level >= 2
79
-
80
- if parts.length > 1
81
- parts[0].product(*parts[1..-1]).tap do |products|
82
- products.map!(&:join)
83
- end
84
- else
85
- parts[0]
86
- end
87
- end
63
+ def spin(limit: nil)
64
+ if limit && limit < count
65
+ spin_with_limit(limit: limit)
66
+ else
67
+ spin_all
88
68
  end
69
+ end
89
70
 
90
- private
91
-
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)
71
+ def spin_all
72
+ parse.spin
73
+ end
107
74
 
108
- # After
109
- text = after
110
- end
75
+ def spin_with_limit(limit:)
76
+ parsed = parse
111
77
 
112
- parts
78
+ results = Array.new(limit)
79
+ results.map! do
80
+ parsed.random
113
81
  end
82
+ end
114
83
 
84
+ def to_source
85
+ parse.to_source
115
86
  end
116
87
 
117
88
  end
@@ -1,7 +1,7 @@
1
1
  class String
2
2
 
3
- def spin
4
- ContentSpinning.spin(self)
3
+ def spin(limit: nil)
4
+ ContentSpinning.spin(self, limit: limit)
5
5
  end
6
6
 
7
7
  end
@@ -0,0 +1,58 @@
1
+ class ContentSpinning
2
+
3
+ class Sentence < ::Array
4
+
5
+ def initialize(*items)
6
+ push(*items)
7
+ end
8
+
9
+ def cleaned
10
+ map!(&:cleaned)
11
+
12
+ case length
13
+ when 0
14
+ ::ContentSpinning::String.new("")
15
+ when 1
16
+ first
17
+ else
18
+ self
19
+ end
20
+ end
21
+
22
+ def count
23
+ case length
24
+ when 0
25
+ 1
26
+ else
27
+ map(&:count).inject(:*)
28
+ end
29
+ end
30
+
31
+ def inspect
32
+ "<Sentence [#{map(&:inspect).join(", ")}]>"
33
+ end
34
+
35
+ def random
36
+ map(&:random).join
37
+ end
38
+
39
+ def spin
40
+ spinned = map(&:spin)
41
+
42
+ case spinned.length
43
+ when 1
44
+ spinned[0]
45
+ else
46
+ spinned[0].product(*spinned[1..-1]).tap do |products|
47
+ products.map!(&:join)
48
+ end
49
+ end
50
+ end
51
+
52
+ def to_source
53
+ map(&:to_source).join
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,43 @@
1
+ class ContentSpinning
2
+
3
+ class Spinner < ::Array
4
+
5
+ def initialize(*items)
6
+ push(*items)
7
+ end
8
+
9
+ def cleaned
10
+ map!(&:cleaned)
11
+
12
+ uniq!
13
+
14
+ if length == 1
15
+ first
16
+ else
17
+ self
18
+ end
19
+ end
20
+
21
+ def count
22
+ map(&:count).inject(:+)
23
+ end
24
+
25
+ def inspect
26
+ "<Spinner {#{map(&:inspect).join(" | ")}}>"
27
+ end
28
+
29
+ def random
30
+ sample.random
31
+ end
32
+
33
+ def spin
34
+ flat_map(&:spin)
35
+ end
36
+
37
+ def to_source
38
+ "{#{map(&:to_source).join("|")}}"
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,35 @@
1
+ class ContentSpinning
2
+
3
+ class String < ::String
4
+
5
+ def initialize(text)
6
+ self.<<(text)
7
+ end
8
+
9
+ def cleaned
10
+ self
11
+ end
12
+
13
+ def count
14
+ 1
15
+ end
16
+
17
+ def inspect
18
+ "<String {#{inspect}}>"
19
+ end
20
+
21
+ def random
22
+ self
23
+ end
24
+
25
+ def spin
26
+ [self]
27
+ end
28
+
29
+ def to_source
30
+ self
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -1,8 +1,8 @@
1
- module ContentSpinning
1
+ class ContentSpinning
2
2
 
3
3
  module Version
4
4
 
5
- STRING = "0.2.0".freeze
5
+ STRING = "0.3.0".freeze
6
6
 
7
7
  end
8
8
 
@@ -7,7 +7,7 @@ describe String do
7
7
  end
8
8
 
9
9
  it "calls the spin function of ContentSpinning module with the string in argument" do
10
- expect(ContentSpinning).to receive(:spin).with("AaBb")
10
+ expect(ContentSpinning).to receive(:spin).with("AaBb", limit: nil)
11
11
  "AaBb".spin
12
12
  end
13
13
 
@@ -1,76 +1,118 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe ContentSpinning do
4
- describe "#clean" do
5
- it "returns the string if there is no spin" do
6
- expect(ContentSpinning.clean("AaBb")).to eq("AaBb")
4
+
5
+ describe "#count" do
6
+ it "returns an array" do
7
+ expect(ContentSpinning.new("AaBb").count).to eq(1)
7
8
  end
8
9
 
9
- it "strips empty spin" do
10
- expect(ContentSpinning.clean("a{}")).to eq("a")
11
- expect(ContentSpinning.clean("a{|}")).to eq("a")
12
- expect(ContentSpinning.clean("a{||}")).to eq("a")
13
- expect(ContentSpinning.clean("{}a{}")).to eq("a")
14
- expect(ContentSpinning.clean("{}a{|}")).to eq("a")
10
+ it "handles empty spin source" do
11
+ expect(ContentSpinning.new("").count).to eq(1)
12
+ end
13
+
14
+ it "strips empty choices" do
15
+ expect(ContentSpinning.new("a{}").count).to eq(1)
16
+ expect(ContentSpinning.new("a{|}").count).to eq(1)
17
+ expect(ContentSpinning.new("a{||}").count).to eq(1)
18
+ expect(ContentSpinning.new("{}a{}").count).to eq(1)
19
+ expect(ContentSpinning.new("{}a{|}").count).to eq(1)
20
+ end
21
+
22
+ it "keeps empty strings from the returned array" do
23
+ expect(ContentSpinning.new("{|a}").count).to eq(2)
24
+ expect(ContentSpinning.new("{a|}").count).to eq(2)
25
+ end
26
+
27
+ it "manages one spin" do
28
+ expect(ContentSpinning.new("{a}").count).to eq(1)
29
+ expect(ContentSpinning.new("a{b}").count).to eq(1)
30
+ expect(ContentSpinning.new("a{b}c").count).to eq(1)
31
+ end
32
+
33
+ it "manages two spin" do
34
+ expect(ContentSpinning.new("{a}{b}").count).to eq(1)
35
+ expect(ContentSpinning.new("a{b}{c}d").count).to eq(1)
36
+ end
15
37
 
16
- expect(ContentSpinning.clean("a{{}}")).to eq("a")
17
- expect(ContentSpinning.clean("a{{|}}")).to eq("a")
18
- expect(ContentSpinning.clean("a{{}|}")).to eq("a")
38
+ it "manages spin with an empty choices" do
39
+ expect(ContentSpinning.new("{|a}").count).to eq(2)
40
+ expect(ContentSpinning.new("{|a}b").count).to eq(2)
41
+ expect(ContentSpinning.new("{a|}b").count).to eq(2)
42
+ expect(ContentSpinning.new("{a|}{b|}").count).to eq(4)
43
+ expect(ContentSpinning.new("a{b|}{c|}d").count).to eq(4)
44
+ end
19
45
 
20
- expect(ContentSpinning.clean("a{a{}}")).to eq("aa")
46
+ it "manages spin with two choices" do
47
+ expect(ContentSpinning.new("{a|b}").count).to eq(2)
48
+ expect(ContentSpinning.new("{a|b}c").count).to eq(2)
49
+ expect(ContentSpinning.new("{a|b}{c|d}").count).to eq(4)
50
+ expect(ContentSpinning.new("{a|b}c{d|e}").count).to eq(4)
21
51
  end
22
52
 
23
- it "remove spin with no choice" do
24
- expect(ContentSpinning.clean("{a}")).to eq("a")
25
- expect(ContentSpinning.clean("a{b}")).to eq("ab")
26
- expect(ContentSpinning.clean("a{b}c")).to eq("abc")
27
- expect(ContentSpinning.clean("a{b}c{d}e")).to eq("abcde")
53
+ it "manages spin with three choices" do
54
+ expect(ContentSpinning.new("{a|b|c}").count).to eq(3)
55
+ expect(ContentSpinning.new("{a|b|c}d").count).to eq(3)
56
+ expect(ContentSpinning.new("{a|b|c}{d|e}").count).to eq(6)
57
+ end
28
58
 
29
- expect(ContentSpinning.clean("{{a}}")).to eq("a")
30
- expect(ContentSpinning.clean("a{{b}}")).to eq("ab")
31
- expect(ContentSpinning.clean("a{{b}}c")).to eq("abc")
32
- expect(ContentSpinning.clean("a{{b}}c{{d}}e")).to eq("abcde")
59
+ it "manages recursive spin" do
60
+ expect(ContentSpinning.new("{a{b|c}|d}").count).to eq(3)
61
+ expect(ContentSpinning.new("{{a|b}|c}").count).to eq(3)
62
+ expect(ContentSpinning.new("{a|{b|c}}").count).to eq(3)
63
+ expect(ContentSpinning.new("{a|{b|c}}{d|e}").count).to eq(6)
64
+ end
33
65
 
34
- expect(ContentSpinning.clean("{{{a}}}")).to eq("a")
66
+ it "manages recursive spin with empty choices" do
67
+ expect(ContentSpinning.new("{a|{b|{c|{d|e}}}}").count).to eq(5)
35
68
  end
36
69
 
37
- it "keep legitimate spin" do
38
- expect(ContentSpinning.clean("{a|b}")).to eq("{a|b}")
39
- expect(ContentSpinning.clean("a{b|c}")).to eq("a{b|c}")
40
- expect(ContentSpinning.clean("a{b|}")).to eq("a{b|}")
41
- expect(ContentSpinning.clean("{{a|b}|c}")).to eq("{{a|b}|c}")
70
+ it "manages duplicate choices" do
71
+ expect(ContentSpinning.new("{a|a}").count).to eq(1)
42
72
  end
43
73
  end
44
74
 
45
75
  describe "#parse" do
46
76
  it "returns the string if there is no spin" do
47
- expect(ContentSpinning.parse("AaBb")).to eq(max_level: 0, parsed: "AaBb")
77
+ expect(ContentSpinning.new("AaBb").parse).to eq("AaBb")
48
78
  end
49
79
 
50
80
  it "parses simple spin" do
51
- expect(ContentSpinning.parse("{a|b}")).to eq(max_level: 1, parsed: "__SPIN_BEGIN_1__a__SPIN_OR_1__b__SPIN_END_1__")
52
- expect(ContentSpinning.parse("a{b|c}")).to eq(max_level: 1, parsed: "a__SPIN_BEGIN_1__b__SPIN_OR_1__c__SPIN_END_1__")
53
- expect(ContentSpinning.parse("{a|b}c{d|e}")).to eq(max_level: 1, parsed: "__SPIN_BEGIN_1__a__SPIN_OR_1__b__SPIN_END_1__c__SPIN_BEGIN_1__d__SPIN_OR_1__e__SPIN_END_1__")
81
+ expect(ContentSpinning.new("{a|b}").parse).to eq(
82
+ ContentSpinning::Spinner.new("a", "b")
83
+ )
84
+ expect(ContentSpinning.new("a{b|c}").parse).to eq(
85
+ ContentSpinning::Sentence.new(
86
+ "a",
87
+ ContentSpinning::Spinner.new("b", "c")
88
+ )
89
+ )
90
+ expect(ContentSpinning.new("{a|b}c{d|e}").parse).to eq(
91
+ ContentSpinning::Sentence.new(
92
+ ContentSpinning::Spinner.new("a", "b"),
93
+ "c",
94
+ ContentSpinning::Spinner.new("d", "e")
95
+ )
96
+ )
54
97
  end
55
98
 
56
99
  it "manages recursive spin" do
57
- expect(ContentSpinning.parse("{{a|b}|c}")).to eq(max_level: 2, parsed: "__SPIN_BEGIN_2____SPIN_BEGIN_1__a__SPIN_OR_1__b__SPIN_END_1____SPIN_OR_2__c__SPIN_END_2__")
58
- expect(ContentSpinning.parse("{a|{b|c}}")).to eq(max_level: 2, parsed: "__SPIN_BEGIN_2__a__SPIN_OR_2____SPIN_BEGIN_1__b__SPIN_OR_1__c__SPIN_END_1____SPIN_END_2__")
100
+ expect(ContentSpinning.new("{{a|b}|c}").parse).to eq(
101
+ ContentSpinning::Spinner.new(ContentSpinning::Spinner.new("a", "b"), "c")
102
+ )
103
+ expect(ContentSpinning.new("{a|{b|c}}").parse).to eq(
104
+ ContentSpinning::Spinner.new("a", ContentSpinning::Spinner.new("b", "c"))
105
+ )
59
106
  end
60
107
  end
61
108
 
62
- describe "#spin" do
63
- it "calls the clean function of ContentSpinning module with the string in argument" do
64
- expect(ContentSpinning).to receive(:clean).with("AaBb").and_return("AaBb")
65
- ContentSpinning.spin("AaBb")
66
- end
67
-
109
+ describe ".spin" do
68
110
  it "returns an array" do
69
111
  expect(ContentSpinning.spin("AaBb")).to eq(["AaBb"])
70
112
  end
71
113
 
72
- it "returns an empty array if the string to spin is empty" do
73
- expect(ContentSpinning.spin("")).to eq([])
114
+ it "handles empty spin source" do
115
+ expect(ContentSpinning.spin("")).to eq([""])
74
116
  end
75
117
 
76
118
  it "strips empty choices" do
@@ -81,9 +123,9 @@ describe ContentSpinning do
81
123
  expect(ContentSpinning.spin("{}a{|}")).to eq(["a"])
82
124
  end
83
125
 
84
- it "strips empty strings from the returned array" do
85
- expect(ContentSpinning.spin("{|a}")).to eq(["a"])
86
- expect(ContentSpinning.spin("{a|}")).to eq(["a"])
126
+ it "keeps empty strings from the returned array" do
127
+ expect(ContentSpinning.spin("{|a}")).to eq(["", "a"])
128
+ expect(ContentSpinning.spin("{a|}")).to eq(["a", ""])
87
129
  end
88
130
 
89
131
  it "manages one spin" do
@@ -98,10 +140,10 @@ describe ContentSpinning do
98
140
  end
99
141
 
100
142
  it "manages spin with an empty choices" do
101
- expect(ContentSpinning.spin("{|a}")).to eq(["a"])
143
+ expect(ContentSpinning.spin("{|a}")).to eq(["", "a"])
102
144
  expect(ContentSpinning.spin("{|a}b")).to eq(%w(b ab))
103
145
  expect(ContentSpinning.spin("{a|}b")).to eq(%w(ab b))
104
- expect(ContentSpinning.spin("{a|}{b|}")).to eq(%w(ab a b))
146
+ expect(ContentSpinning.spin("{a|}{b|}")).to eq(%w(ab a b) + [""])
105
147
  expect(ContentSpinning.spin("a{b|}{c|}d")).to eq(%w(abcd abd acd ad))
106
148
  end
107
149
 
@@ -139,5 +181,65 @@ describe ContentSpinning do
139
181
  ContentSpinning.spin(source)
140
182
  }.not_to change { source }
141
183
  end
184
+
185
+ context 'with limit' do
186
+ before { @old_seed = Random.srand(2736) }
187
+ after { Random.srand(@old_seed) }
188
+
189
+ it "manages recursive spin with empty choices" do
190
+ expect(ContentSpinning.spin("{a|{b|{c|{d|e}}}}_z", limit: 2)).to eq(%w(d_z a_z))
191
+ end
192
+
193
+ it "manages recursive spin" do
194
+ expect(ContentSpinning.spin("{a|{b|c}}{d|e}_z", limit: 2)).to eq(%w(ce_z ad_z))
195
+ end
196
+ end
197
+ end
198
+
199
+ describe "#to_source" do
200
+ it "returns the string if there is no spin" do
201
+ expect(ContentSpinning.new("AaBb").to_source).to eq("AaBb")
202
+ end
203
+
204
+ it "strips empty spin" do
205
+ expect(ContentSpinning.new("a{}").to_source).to eq("a")
206
+ expect(ContentSpinning.new("a{|}").to_source).to eq("a")
207
+ expect(ContentSpinning.new("a{||}").to_source).to eq("a")
208
+ expect(ContentSpinning.new("{}a{}").to_source).to eq("a")
209
+ expect(ContentSpinning.new("{}a{|}").to_source).to eq("a")
210
+
211
+ expect(ContentSpinning.new("a{{}}").to_source).to eq("a")
212
+ expect(ContentSpinning.new("a{{|}}").to_source).to eq("a")
213
+ expect(ContentSpinning.new("a{{}|}").to_source).to eq("a")
214
+
215
+ expect(ContentSpinning.new("a{a{}}").to_source).to eq("aa")
216
+ end
217
+
218
+ it "removes spin with no choice" do
219
+ expect(ContentSpinning.new("{a}").to_source).to eq("a")
220
+ expect(ContentSpinning.new("a{b}").to_source).to eq("ab")
221
+ expect(ContentSpinning.new("a{b}c").to_source).to eq("abc")
222
+ expect(ContentSpinning.new("a{b}c{d}e").to_source).to eq("abcde")
223
+
224
+ expect(ContentSpinning.new("{{a}}").to_source).to eq("a")
225
+ expect(ContentSpinning.new("a{{b}}").to_source).to eq("ab")
226
+ expect(ContentSpinning.new("a{{b}}c").to_source).to eq("abc")
227
+ expect(ContentSpinning.new("a{{b}}c{{d}}e").to_source).to eq("abcde")
228
+
229
+ expect(ContentSpinning.new("{{{a}}}").to_source).to eq("a")
230
+ end
231
+
232
+ it "removes duplicate choices" do
233
+ expect(ContentSpinning.new("{a|a}").to_source).to eq("a")
234
+ expect(ContentSpinning.new("{a|a|b}").to_source).to eq("{a|b}")
235
+ expect(ContentSpinning.new("{a|b|a|b}").to_source).to eq("{a|b}")
236
+ end
237
+
238
+ it "keeps legitimate spin" do
239
+ expect(ContentSpinning.new("{a|b}").to_source).to eq("{a|b}")
240
+ expect(ContentSpinning.new("a{b|c}").to_source).to eq("a{b|c}")
241
+ expect(ContentSpinning.new("a{b|}").to_source).to eq("a{b|}")
242
+ expect(ContentSpinning.new("{{a|b}|c}").to_source).to eq("{{a|b}|c}")
243
+ end
142
244
  end
143
245
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: content_spinning
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxime Garcia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-15 00:00:00.000000000 Z
11
+ date: 2017-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -44,6 +44,9 @@ files:
44
44
  - README.md
45
45
  - lib/content_spinning.rb
46
46
  - lib/content_spinning/core_ext/string.rb
47
+ - lib/content_spinning/sentence.rb
48
+ - lib/content_spinning/spinner.rb
49
+ - lib/content_spinning/string.rb
47
50
  - lib/content_spinning/version.rb
48
51
  - spec/content_spinning/core_ext/string_spec.rb
49
52
  - spec/content_spinning_spec.rb
@@ -68,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
71
  version: 1.3.6
69
72
  requirements: []
70
73
  rubyforge_project:
71
- rubygems_version: 2.6.4
74
+ rubygems_version: 2.6.8
72
75
  signing_key:
73
76
  specification_version: 4
74
77
  summary: Content Spinning