oulipo 0.1.2 → 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/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- oulipo (0.1.1)
4
+ oulipo (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -40,6 +40,37 @@ sentence = 'Big fjords vex quick waltz nymph.'
40
40
  Oulipo.pangram?(sentence) # => true
41
41
  ```
42
42
 
43
+ ## N+7
44
+
45
+ In N+7 (sometimes known as S+7), each noun in a text is replaced with the noun seven entries after it in a dictionary.
46
+
47
+ ```ruby
48
+ dictionary = Oulipo::WordList.load('big_list_of_nouns.txt')
49
+
50
+ play = <<-SHAKESPEARE
51
+
52
+ What, jealous Oberon! Fairies, skip hence:
53
+ I have forsworn his bed and company.
54
+
55
+ SHAKESPEARE
56
+
57
+ Oulipo.n_plus(7, play, dictionary) # => "What, jealous Oberon! Fallacies, skulk hence:
58
+ # I have forsworn his bedroom and compensation."
59
+
60
+ ```
61
+
62
+ Oulipo includes a handy `WordList` class for reading one-word-per-line dictionary files, but a dictionary can be any object that responds to `index(word)`, `length`, and `[index]`.
63
+
64
+ ```ruby
65
+ dictionary = %w{ iron gild mine gold ore paint cast lily }
66
+
67
+ king_john = 'To gild refined gold, to paint the lily'
68
+
69
+ Oulipo.n_plus(1, king_john, dictionary) # => 'To mine refined ore, to cast the iron'
70
+ ```
71
+
72
+ See also: Substitution.
73
+
43
74
  ## Univocalims
44
75
 
45
76
  A univocalism is a poem written using only one type of vowel.
@@ -84,7 +115,7 @@ poem = <<-WORDS
84
115
 
85
116
  WORDS
86
117
 
87
- Oulipo.snowball? poem # => true
118
+ Oulipo.snowball?(poem) # => true
88
119
  ```
89
120
 
90
121
  ## Alliteration
@@ -100,7 +131,40 @@ Normal alliteration's a little harsh, so you can give it a threshold, too.
100
131
  ```ruby
101
132
  phrase = 'quick queens quibble over quails'
102
133
 
103
- Oulipo.allitertivity(phrase) # => 0.8 (4/5 words start with 'q')
134
+ Oulipo.alliterativity(phrase) # => 0.8 (4/5 words start with 'q')
104
135
  Oulipo.alleration?(phrase, :threshold => 0.7) # => true
105
136
  Oulipo.alleration?(phrase, :threshold => 0.9) # => false
106
137
  ```
138
+
139
+ ## Analysis
140
+
141
+ Rudimentary analysis can be performed by using Oulipo's `Analysis` class.
142
+
143
+ ```ruby
144
+ line = 'A rose by any other name would smell as sweet'
145
+
146
+ word_sets = {
147
+ :nouns => %w{ bear name rose },
148
+ :adjectives => %w{ sweet }
149
+ }
150
+
151
+ analysis = Oulipo::Analysis.new(line, word_sets)
152
+
153
+ analysis.identified(:nouns) # => ['rose', 'name']
154
+ analysis.identified(:adjectives) # => ['sweet']
155
+ ```
156
+
157
+ ## Substitution
158
+
159
+ Substitution can be performed on an analysis.
160
+
161
+ Carrying on from the example above:
162
+
163
+ ```ruby
164
+ substitutor = Oulipo::Substitutor.new(analysis)
165
+ substitutor.replace(:nouns).increment(1) # => "A bear by any other rose would smell as sweet"
166
+ ```
167
+
168
+ ---
169
+
170
+ - Pete Nicholls ([@Aupajo](http://twitter.com/Aupajo))
@@ -0,0 +1,60 @@
1
+ module Oulipo
2
+ class Analysis
3
+ include Enumerable
4
+
5
+ attr_reader :original, :deconstruction, :word_lists
6
+
7
+ def initialize(text, options = {})
8
+ @word_lists = options
9
+ @original = text
10
+ deconstruct!
11
+ end
12
+
13
+ def identified(type = nil)
14
+ result = identified_tuples
15
+ result.select! { |tuple| tuple.last == type } if type
16
+ result.map(&:first)
17
+ end
18
+
19
+ private
20
+
21
+ def is_tuple?(object)
22
+ object.is_a?(Array)
23
+ end
24
+
25
+ def is_plain?(object)
26
+ object.is_a?(String)
27
+ end
28
+
29
+ def identified_tuples
30
+ deconstruction.select { |segment| is_tuple?(segment) }
31
+ end
32
+
33
+ def identify(wordish)
34
+ return wordish if wordish.strip.empty?
35
+
36
+ word_lists.each do |type, list|
37
+ return [wordish, type] if list.index(wordish.downcase)
38
+ end
39
+
40
+ wordish
41
+ end
42
+
43
+ def deconstruct!
44
+ chunks = original.split(/\b/)
45
+
46
+ @deconstruction = []
47
+
48
+ chunks.each do |wordish|
49
+ analysis = identify(wordish)
50
+
51
+ if is_plain?(analysis) and is_plain?(deconstruction.last)
52
+ deconstruction.last << analysis
53
+ else
54
+ deconstruction << analysis
55
+ end
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,51 @@
1
+ module Oulipo
2
+ class Substitutor
3
+
4
+ def initialize(analysis)
5
+ @analysis = analysis
6
+ end
7
+
8
+ def replace(type)
9
+ @type = type
10
+ self
11
+ end
12
+
13
+ def increment(num)
14
+ raise 'Increment must be called in conjunction with replace' if !@type
15
+
16
+ result = ''
17
+
18
+ @analysis.deconstruction.each do |segment|
19
+ if segment.is_a?(Array) # tuple found
20
+ if segment.last == @type # ready to replace
21
+ result << find_successor(segment.first, num)
22
+ else
23
+ result << segment.first
24
+ end
25
+ else
26
+ result << segment
27
+ end
28
+ end
29
+
30
+ result
31
+ end
32
+
33
+ private
34
+
35
+ def word_list
36
+ @analysis.word_lists[@type]
37
+ end
38
+
39
+ def find_successor(word, num)
40
+ # Find the current index and the limit
41
+ current_index = word_list.index(word.downcase)
42
+ limit = word_list.length
43
+
44
+ # Calculate the successor's index, wrapping if we need to
45
+ successor = (current_index + num) % limit
46
+
47
+ word_list[successor]
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,34 @@
1
+ module Oulipo
2
+ class WordList
3
+ def initialize
4
+ @words = []
5
+ end
6
+
7
+ def self.load(file)
8
+ dict = self.new
9
+ file = File.open(file) if file.is_a?(String)
10
+
11
+ file.each_line do |word|
12
+ dict.push(word.strip)
13
+ end
14
+
15
+ dict
16
+ end
17
+
18
+ def index(word)
19
+ @words.index(word)
20
+ end
21
+
22
+ def length
23
+ @words.length
24
+ end
25
+
26
+ def [](index)
27
+ @words[index]
28
+ end
29
+
30
+ def push(word)
31
+ @words.push(word)
32
+ end
33
+ end
34
+ end
data/lib/oulipo.rb CHANGED
@@ -1,4 +1,8 @@
1
- class Oulipo
1
+ require 'oulipo/word_list'
2
+ require 'oulipo/analysis'
3
+ require 'oulipo/substitutor'
4
+
5
+ module Oulipo
2
6
  ALPHABET = 'a'..'z'
3
7
  VOWELS = %w{ a e i o u }
4
8
 
@@ -66,4 +70,10 @@ class Oulipo
66
70
  most_used_count = leading_letter_counts.max_by { |kv| kv.last }.pop
67
71
  most_used_count.to_f / words.length
68
72
  end
73
+
74
+ def self.n_plus(places, text, word_list)
75
+ analysis = Analysis.new(text, :nouns => word_list)
76
+ substitutor = Substitutor.new(analysis)
77
+ substitutor.replace(:nouns).increment(places)
78
+ end
69
79
  end
data/oulipo.gemspec CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path('../lib', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "oulipo"
6
- s.version = "0.1.2"
6
+ s.version = "0.2.0"
7
7
  s.date = Time.now.strftime('%Y-%m-%d')
8
8
  s.homepage = "http://github.com/Aupajo/oulipo"
9
9
  s.email = "pete@metanation.com"
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Oulipo::Analysis do
4
+ include Oulipo
5
+
6
+ let(:folksong) { "O'er the moor and among the heather" }
7
+
8
+ let(:word_lists) { { :nouns => %w{ moor heather },
9
+ :prepositions => %w{ among } } }
10
+
11
+ let(:analysis) { Analysis.new(folksong, word_lists) }
12
+
13
+ it "can identify words" do
14
+ analysis.identified.sort.should == %w{ among heather moor }
15
+ analysis.identified(:nouns).should == %w{ moor heather }
16
+ analysis.identified(:prepositions).should == %w{ among }
17
+ end
18
+
19
+ it "can return the original string" do
20
+ analysis.original.should == "O'er the moor and among the heather"
21
+ end
22
+
23
+ it "can return the raw deconstruction" do
24
+ deconstruction = ["O'er the ", ['moor', :nouns], ' and ', ['among', :prepositions], ' the ', ['heather', :nouns]]
25
+ analysis.deconstruction.should == deconstruction
26
+ end
27
+
28
+ it "can return the word lists it's using" do
29
+ analysis.word_lists.should == word_lists
30
+ end
31
+
32
+ end
@@ -0,0 +1,3 @@
1
+ apple
2
+ car
3
+ dog
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe "substitution" do
4
+
5
+ def analysis_with(phrase, options)
6
+ Oulipo::Analysis.new(phrase, options)
7
+ end
8
+
9
+ def substitutor_with(*args)
10
+ Oulipo::Substitutor.new analysis_with(*args)
11
+ end
12
+
13
+ let(:performer) { Oulipo }
14
+
15
+ let(:phrase) { 'The bear ate the badger' }
16
+ let(:noun_list) { %w{ badger bat bear } }
17
+ let(:substitutor) { substitutor_with(phrase, :nouns => noun_list) }
18
+
19
+ it "substitutes types" do
20
+ substitutor.replace(:nouns).increment(1).should == 'The badger ate the bat'
21
+ substitutor.replace(:nouns).increment(2).should == 'The bat ate the bear'
22
+ substitutor.replace(:nouns).increment(6).should == 'The bear ate the badger'
23
+ end
24
+
25
+ it "raises an error if increment is called before replace" do
26
+ lambda { substitutor.increment(3) }.should raise_error
27
+ end
28
+
29
+ it "can be accessed from Oulipo with n_plus" do
30
+ performer.n_plus(1, phrase, noun_list).should == 'The badger ate the bat'
31
+ performer.n_plus(2, phrase, noun_list).should == 'The bat ate the bear'
32
+ performer.n_plus(6, phrase, noun_list).should == 'The bear ate the badger'
33
+ end
34
+
35
+ it "handles unused nouns" do
36
+ nouns = %w{ badger bear bat ball balustrade }
37
+ substitutor = substitutor_with(phrase, :nouns => nouns)
38
+ substitutor.replace(:nouns).increment(4).should == 'The badger ate the balustrade'
39
+ end
40
+
41
+ it "matches case" do
42
+ substitutor = substitutor_with('The BEAR ate the BadgeR', :nouns => noun_list)
43
+ substitutor.replace(:nouns).increment(1).should == 'The badger ate the bat'
44
+ end
45
+
46
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Oulipo::WordList do
4
+
5
+ let(:word_list) { Oulipo::WordList.new }
6
+
7
+ it "acts like an array" do
8
+ word_list.length.should == 0
9
+ word_list.index('apple').should be_nil
10
+ word_list[0].should == nil
11
+
12
+ word_list.push('apple')
13
+
14
+ word_list.length.should == 1
15
+ word_list.index('apple').should == 0
16
+ word_list[0].should == 'apple'
17
+ end
18
+
19
+ it "loads from a file name" do
20
+ list = Oulipo::WordList.load(File.dirname(__FILE__) + '/fixtures/word_list.txt')
21
+ list.length.should == 3
22
+ list.index('car').should == 1
23
+ list[2].should == 'dog'
24
+ end
25
+
26
+ it "loads from a file" do
27
+ file = File.open(File.dirname(__FILE__) + '/fixtures/word_list.txt')
28
+ list = Oulipo::WordList.load(file)
29
+ list.length.should == 3
30
+ list.index('car').should == 1
31
+ list[2].should == 'dog'
32
+ end
33
+
34
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: oulipo
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.2
5
+ version: 0.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Pete Nicholls
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-07-28 00:00:00 +12:00
13
+ date: 2011-07-29 00:00:00 +12:00
14
14
  default_executable:
15
15
  dependencies: []
16
16
 
@@ -29,13 +29,20 @@ files:
29
29
  - README.md
30
30
  - Rakefile
31
31
  - lib/oulipo.rb
32
+ - lib/oulipo/analysis.rb
33
+ - lib/oulipo/substitutor.rb
34
+ - lib/oulipo/word_list.rb
32
35
  - oulipo.gemspec
33
36
  - spec/alliteration_spec.rb
37
+ - spec/analysis_spec.rb
34
38
  - spec/chaterisms_spec.rb
39
+ - spec/fixtures/word_list.txt
35
40
  - spec/lipograms_spec.rb
36
41
  - spec/palindromes_spec.rb
37
42
  - spec/spec_helper.rb
43
+ - spec/substitution_spec.rb
38
44
  - spec/univocalisms_spec.rb
45
+ - spec/word_list_spec.rb
39
46
  has_rdoc: true
40
47
  homepage: http://github.com/Aupajo/oulipo
41
48
  licenses: []
@@ -66,8 +73,12 @@ specification_version: 3
66
73
  summary: Constrained writing with Ruby.
67
74
  test_files:
68
75
  - spec/alliteration_spec.rb
76
+ - spec/analysis_spec.rb
69
77
  - spec/chaterisms_spec.rb
78
+ - spec/fixtures/word_list.txt
70
79
  - spec/lipograms_spec.rb
71
80
  - spec/palindromes_spec.rb
72
81
  - spec/spec_helper.rb
82
+ - spec/substitution_spec.rb
73
83
  - spec/univocalisms_spec.rb
84
+ - spec/word_list_spec.rb