typogrowth 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: 8b9cc2df68d09b41d6e66c2f23267d983b3de918
4
+ data.tar.gz: e941117481606671b6f2761e587d2f52d7ed7561
5
+ !binary "U0hBNTEy":
6
+ metadata.gz: 3a46dceca2914e0efb637b78f54d850e8426f860127aeb6d50da8f79b5c0a45a6c4ffd358632ad9578050513aa92b35cc2bee2429ad34b1b9540d6f86f39b9b3
7
+ data.tar.gz: 23d646481747e78b23e29ae2360854aeb3b3849a773f535d5426dae39f94c85ce81cb392a024cae8f9bbd9d90f7ca40e16d2ce0af765687e1349f2df65a85cd0
data/.document ADDED
@@ -0,0 +1,11 @@
1
+ # .document is used by rdoc and yard to know how to generate documentation
2
+ # for example, it can be used to control how rdoc gets built when you do `gem install foo`
3
+
4
+ README.rdoc
5
+ lib/**/*.rb
6
+ bin/*
7
+
8
+ # Files below this - are treated as 'extra files', and aren't parsed for ruby code
9
+ -
10
+ features/**/*.feature
11
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ /nbproject/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in typogrowth.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Alexei Matyushkin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Typogrowth
2
+
3
+ [![Build Status](https://travis-ci.org/mudasobwa/typogrowth.png)](https://travis-ci.org/mudasobwa/typogrowth)
4
+ [![Gemnasium](https://gemnasium.com/mudasobwa/typogrowth.png?travis)](https://gemnasium.com/mudasobwa/typogrowth)
5
+
6
+ Typogrowth is the simple gem, providing easy way to make string
7
+ typographically correct. It introduce the class method:
8
+
9
+ ```ruby
10
+ Typogrowth.parse string, lang = nil
11
+ ```
12
+
13
+ as well as it monkeypatches `String` class with `typo` method.
14
+ If language is omitted, it uses `I18n.locale`. Also `:default`
15
+ may be specified as language setting (which is english, in fact.)
16
+
17
+ To modify the succession of quotation signs (as well as all the
18
+ others options,) feel free to change `config/typogrowth.yaml`.
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'typogrowth'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install typogrowth
33
+
34
+ ## Usage
35
+
36
+ ```ruby
37
+ s = 'And God said "Baz heard "Bar" once" , and there was light.'
38
+ puts s.typo
39
+ # ⇒ And God said “Baz heard ‘Bar’ once,” and there was light.
40
+ puts Typogrowth.parse(s)
41
+ # ⇒ And God said “Baz heard ‘Bar’ once,” and there was light.
42
+
43
+ s = 'И Бог сказал: "Я - слышу "Бум" и "Бам" где-то там" , и стало светло.'
44
+ puts s.typo('ru') # Explicit locale specification may be omitted
45
+ # while running under ru_RU.UTF-8 locale
46
+ # ⇒ И Бог сказал: «Я — слышу „Бум“ и „Бам“ где-то там», и стало светло.
47
+
48
+ s = 'And God said "Oslo coordinates are: 59°57′N 10°45′E" and there was light.'
49
+ s.typo!
50
+ # ⇒ And God said “Oslo coordinates are: 59°57′N 10°45′E” and there was light.
51
+ puts s
52
+ # ⇒ And God said “Oslo coordinates are: 59°57′N 10°45′E” and there was light.
53
+ ```
54
+
55
+ ## Contributing
56
+
57
+ 1. Fork it
58
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
59
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
60
+ 4. Push to the branch (`git push origin my-new-feature`)
61
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:examples) do |examples|
5
+ examples.rspec_opts = '-Ispec'
6
+ end
7
+
8
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
9
+ spec.rspec_opts = '-Ispec'
10
+ spec.rcov = true
11
+ end
12
+
13
+ require 'cucumber/rake/task'
14
+ Cucumber::Rake::Task.new(:features)
15
+
16
+ task :default => :features
17
+
18
+ require 'yard'
19
+ YARD::Rake::YardocTask.new
data/bin/typo ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'optparse'
7
+ require_relative '../lib/typogrowth'
8
+
9
+ options = {}
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: #{$0} FILE|STRING"
12
+
13
+ # Bowl result?
14
+ opts.on("-o", "--out FILE",
15
+ "Output file name for result") do |outfile|
16
+ options[:outfile] = outfile
17
+ end
18
+
19
+ # No argument, shows at tail. This will print an options summary.
20
+ opts.on_tail("-h", "--help", "Show this message") do
21
+ puts opts
22
+ exit
23
+ end
24
+ end.parse!
25
+
26
+ raise "Run `#{$0} --help` for execution examples. Exiting…" if ARGV.size.zero?
27
+
28
+ file_or_string = ARGV.first
29
+ file_or_string = File.read(file_or_string) if File.exist?(file_or_string)
30
+
31
+ @result = Typogrowth::parse file_or_string
32
+
33
+ options[:outfile].nil? ?
34
+ puts(@result) :
35
+ File.write(options[:outfile], @result)
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ Given(/^the input string is "(.*?)"$/) do |str|
4
+ @content = str
5
+ end
6
+
7
+ When(/^input string is processed with Typogrowl’s typography parser$/) do
8
+ @content.gsub! /\\+"/, '"'
9
+ @typo = Typogrowth.parse @content
10
+ end
11
+
12
+ When(/^input string is processed with Typogrowl’s typography parser with lang "(.*?)"$/) do |lang|
13
+ @content.gsub! /\\+"/, '"'
14
+ @typo = Typogrowth.parse @content, lang
15
+ end
16
+
17
+ When(/^input string is modified inplace with typo!$/) do
18
+ @typoed = @content.dup
19
+ @typoed.typo!
20
+ end
21
+
22
+ Then(/^neither single nor double quotes are left in the string$/) do
23
+ @typo.scan(/"|'/).count.should == 0
24
+ end
25
+
26
+ Then(/^the typoed result should equal to "(.*?)"$/) do |str|
27
+ @typo.should == str
28
+ end
29
+
30
+ Then(/^the call to string’s typo should equal to "(.*?)"$/) do |str|
31
+ @content.typo.should == str
32
+ end
33
+
34
+ Then(/^the call to string’s typo with lang "(.*?)" should equal to "(.*?)"$/) do |lang, str|
35
+ @content.typo('ru').should == str
36
+ end
37
+
38
+ Then(/^typoed result should equal to "(.*?)"$/) do |str|
39
+ @typoed.should == str
40
+ end
@@ -0,0 +1,5 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'typogrowth'
4
+
5
+ require 'rspec/expectations'
@@ -0,0 +1,54 @@
1
+ Feature: Text is to be typographed (spacing and pubctuation are to be sanitized)
2
+
3
+ # Known bug: see last example in first scenario outline
4
+ Scenario Outline: Quotes and punctuation in English
5
+ Given the input string is <input>
6
+ When input string is processed with Typogrowl’s typography parser
7
+ Then the typoed result should equal to <output>
8
+ And the call to string’s typo should equal to <output>
9
+ And neither single nor double quotes are left in the string
10
+
11
+ Examples:
12
+ | input | output |
13
+ | "And God said \"∇×(∇×F) = ∇(∇·F) − ∇2F\" and there was light." | "And God said “∇×(∇×F) = ∇(∇·F) − ∇2F” and there was light." |
14
+ | "And God said \"I--heard \"Booh \"Bah\" Booh\" and \"Bam\" in heaven\" and there was light." | "And God said “I—heard ‘Booh “Bah” Booh’ and ‘Bam’ in heaven” and there was light." |
15
+ | "And God said \"I - heard \"Booh Bah Booh\" and \"Bam\" in heaven\" and there was light." | "And God said “I—heard ‘Booh Bah Booh’ and ‘Bam’ in heaven” and there was light." |
16
+ | "And God said \"Oslo coordinates are: 59°57′N 10°45′E\" and there was light." | "And God said “Oslo coordinates are: 59°57′N 10°45′E” and there was light." |
17
+ | "And God said \"That's a 6.3\" man, he sees sunsets at 10°20'30\" E.\" and there was light." | "And God said “That’s a 6.3″ man, he sees sunsets at 10°20′30″ E.” and there was light." |
18
+ | "And God said \"Foo\" , and there was light." | "And God said “Foo,” and there was light." |
19
+ | "And God said \"Baz heard 'Foos' Bar' once\" , and there was light." | "And God said “Baz heard ‘Foos’ Bar’ once,” and there was light." |
20
+ | "And God, loving ellipsis, said.... And..." | "And God, loving ellipsis, said… And…" |
21
+
22
+ Scenario Outline: Quotes and punctuation in Russian
23
+ Given the input string is <input>
24
+ When input string is processed with Typogrowl’s typography parser with lang "ru"
25
+ Then the typoed result should equal to <output>
26
+ And the call to string’s typo with lang "ru" should equal to <output>
27
+ And neither single nor double quotes are left in the string
28
+
29
+ Examples:
30
+ | input | output |
31
+ | "И Бог сказал: \"Я - слышу \"Бум\" и \"Бам\" где-то там\" , и стало светло." | "И Бог сказал: «Я — слышу „Бум“ и „Бам“ где-то там», и стало светло." |
32
+ | "И Бог сказал: \"Я - слышу \"Бум \"и\" Бам\" где-то там\" , и стало светло." | "И Бог сказал: «Я — слышу „Бум «и» Бам“ где-то там», и стало светло." |
33
+ | "Строка со ссылкой: http://wikipedia.org (ссылка)." | "Строка со ссылкой: http://wikipedia.org (ссылка)." |
34
+
35
+ Scenario Outline: Spacing before/after punctuation
36
+ Given the input string is <input>
37
+ When input string is processed with Typogrowl’s typography parser
38
+ Then the typoed result should equal to <output>
39
+ And the call to string’s typo should equal to <output>
40
+
41
+ Examples:
42
+ | input | output |
43
+ | "It’s raining.Pity." | "It’s raining. Pity." |
44
+ | "It’s raining . Pity." | "It’s raining. Pity." |
45
+ | "It’s raining .Pity." | "It’s raining. Pity." |
46
+ | "Link http://wikipedia.org here." | "Link http://wikipedia.org here." |
47
+ | "Here is http://wikipedia.org. See?." | "Here is http://wikipedia.org. See?." |
48
+ | "Here is exclamation ellipsis!.." | "Here is exclamation ellipsis!.." |
49
+ | "Here is exclamation ellipsis! . ." | "Here is exclamation ellipsis!.." |
50
+
51
+ Scenario: Inplace string modification
52
+ Given the input string is "Foo 'Bar' Baz"
53
+ When input string is modified inplace with typo!
54
+ Then typoed result should equal to "Foo “Bar” Baz"
@@ -0,0 +1,132 @@
1
+ :quotes :
2
+ :punctuation :
3
+ :re : '(?<quote>''|"|\))\s*(?<punct>[.,!?]+)'
4
+ :default :
5
+ - '\k<punct>\k<quote>'
6
+ :ru :
7
+ - '\k<quote>\k<punct>'
8
+
9
+ # That's a 6.3" man, he sees sunsets at 10°20'30" E.
10
+ # ⇑
11
+ :inch :
12
+ :pattern : '"'
13
+ :re : '(?:\p{Space}[\.,\p{Digit}]+)(")(?=\p{Space}|\Z)'
14
+ :default :
15
+ - '″'
16
+ # That's a 6.3" man, he sees sunsets at 10°20'30" E.
17
+ # ⇑
18
+ :seconds :
19
+ :pattern : '"'
20
+ :re : '(?:\p{Space}[°''’′\p{Digit}]+)(")(?=\p{Space}|\p{Alpha}|\Z)'
21
+ :default :
22
+ - '″'
23
+
24
+ # That's a 6.3" man, he sees sunsets at 10°20'30" E.
25
+ # ⇑
26
+ :minutes :
27
+ :pattern : ''''
28
+ :re : '(\p{Space}[°\p{Digit}]+)('')(?=\p{Space}|\p{Alnum}|\Z)'
29
+ :default :
30
+ - '′'
31
+
32
+ # That's a 6.3" man, he sees sunsets at 10°20'30" E.
33
+ # ⇑
34
+ :apostrophe_pre :
35
+ :pattern : ''''
36
+ :re : '(?<=\p{Alpha})('')(?=\p{Alpha})'
37
+ :default :
38
+ - '♻'
39
+ # And God said 'Foos' game is over'.
40
+ # ⇑
41
+ :apostrophe_squeez :
42
+ :pattern : ''''
43
+ :re : '(?<=s)('')(?=\s)'
44
+ :default :
45
+ - '♻'
46
+ # And God said "∇×(∇×F) = ∇(∇·F) − ∇2F" and there was light.
47
+ # ⇑
48
+ :left :
49
+ :original : '''"'
50
+ :re : '(?<=\p{Space}|\A)(?<m>"|'')(?=\p{Graph}|\Z)'
51
+ :compliant : 'right'
52
+ # Nested quotation marks are chosen if it’s an even occurence
53
+ :default :
54
+ - '“'
55
+ - '‘'
56
+ # - '〈'
57
+ :ru :
58
+ - '«'
59
+ - '„'
60
+ :us :
61
+ - '‘'
62
+ - '“'
63
+ # And God said "∇×(∇×F) = ∇(∇·F) − ∇2F" and there was light.
64
+ # ⇑
65
+ :right :
66
+ :slave : true
67
+ :original : '''"'
68
+ :re : '(?<=\p{L}|\p{M}|\p{P})(?<m>"|'')(?=\p{Space}|\p{P}|\Z)'
69
+ :compliant : 'left'
70
+ # Nested quotation marks are chosen if it’s an odd occurence
71
+ :default :
72
+ - '”'
73
+ - '’'
74
+ # - '〉'
75
+ :ru :
76
+ - '»'
77
+ - '“'
78
+ :us :
79
+ - '’'
80
+ - '”'
81
+
82
+ # That's a 6.3" man, he sees sunsets at 10°20'30" E.
83
+ # ⇑
84
+ :apostrophe_post :
85
+ :pattern : '♻'
86
+ :re : '♻'
87
+ :default :
88
+ - '’'
89
+
90
+ :alone_single :
91
+ :re : '('')'
92
+ :alert: true
93
+ :default :
94
+ - '’'
95
+
96
+ :alone_double :
97
+ :re : '(")'
98
+ :alert: true
99
+ :default :
100
+ - '”'
101
+
102
+ :others :
103
+ :ellipsis :
104
+ :re : '\.{3,}'
105
+ :default :
106
+ - '…'
107
+ :mdash :
108
+ :re : '\s*(?<dash> - |--)\s*'
109
+ :default :
110
+ - '—'
111
+ :ru :
112
+ - ' — '
113
+ :us :
114
+ - ' – '
115
+
116
+ :punctuation :
117
+ :opening_orphan :
118
+ :re : '([(¿¡§#№]|\p{Sc})(?:\s)*'
119
+ :default :
120
+ - '\1'
121
+ :closing_orphan :
122
+ :re : '(?:\s)*([.,:;!?)])'
123
+ :default :
124
+ - '\1'
125
+ :closing_clamped :
126
+ :re : '([.,:;!?)])(?=\p{Lu})'
127
+ :default :
128
+ - '\1 '
129
+ :fixup :
130
+ :re : '([!?.]+)(?:\s*)(\.+)'
131
+ :default :
132
+ - '\1\2'
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ require 'i18n'
4
+ require_relative '../typogrowth'
5
+
6
+ class String
7
+ # Typographyes the string and returns a result
8
+ # See Typogrowth::Parser#parse
9
+ def typo lang = nil
10
+ Typogrowth::Parser.parse(self, lang ? lang : I18n.locale)
11
+ end
12
+ # Typographyes the string inplace
13
+ # See Typogrowth::Parser#parse!
14
+ def typo! lang = nil
15
+ Typogrowth::Parser.parse!(self, lang ? lang : I18n.locale)
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Typogrowth
2
+ VERSION = "0.9.0"
3
+ end
data/lib/typogrowth.rb ADDED
@@ -0,0 +1,145 @@
1
+ # encoding: utf-8
2
+
3
+ require 'yaml'
4
+
5
+ require 'uri'
6
+ require 'base64'
7
+ require_relative 'typogrowth/version'
8
+ require_relative 'typogrowth/string'
9
+ require_relative 'utils/hash_recursive_merge'
10
+
11
+ #
12
+ # = String typographing with language support.
13
+ #
14
+ # Parses and corrects the typography in strings. It supports
15
+ # different language rules and user rules customization.
16
+ #
17
+ # The package also monkeypatches `String` class with both
18
+ # `typo` and `typo!` methods.
19
+ #
20
+ # Category:: Ruby
21
+ # Author:: Alexei Matyushkin <am@mudasobwa.ru>
22
+ # Copyright:: 2013 The Authors
23
+ # License:: MIT License
24
+ # Link:: http://rocket-science.ru/
25
+ # Source:: http://github.com/mudasobwa/typogrowth
26
+ #
27
+ module Typogrowth
28
+ # Internal exception class just to make the exception distinction possible
29
+ class MalformedRulesFile < Exception ; end
30
+
31
+ # Parses and corrects the typography in strings. It supports
32
+ # different language rules and easy user rules customization.
33
+ class Parser
34
+ attr_reader :yaml
35
+
36
+ #
37
+ # Recursively merges the initial settings with custom.
38
+ #
39
+ # To supply your own rules to processing:
40
+ #
41
+ # - create a +hash+ of additional rules in the same form as in the
42
+ # standard `typogrowth.yaml` file shipped with a project
43
+ # - merge the hash with the standard one using this function
44
+ #
45
+ # For instance, to add french rules one is to merge in the following yaml:
46
+ #
47
+ # :quotes :
48
+ # :punctuation :
49
+ # :fr : "\\k<quote>\\k<punct>"
50
+ # …
51
+ #
52
+ def self.merge custom
53
+ instance.yaml.rmerge!(custom)
54
+ end
55
+
56
+ #
57
+ # Inplace version of string typographying.
58
+ #
59
+ # Retrieves the string and changes all the typewriters quotes (doubles
60
+ # and sigles), to inches, minutes, seconds, proper quotation signs.
61
+ #
62
+ # While the input strings are e.g.
63
+ #
64
+ # And God said "Baz heard "Bar" once" , and there was light.
65
+ # That's a 6.3" man, he sees sunsets at 10°20'30" E.
66
+ #
67
+ # It will produce:
68
+ #
69
+ # And God said “Baz heard ‘Bar’ once,” and there was light.
70
+ # That’s a 6.3″ man, he sees sunsets at 10°20′30″ E.
71
+ #
72
+ # The utility also handles dashes as well.
73
+ #
74
+ # @param str [String] the string to be typographyed inplace
75
+ # @param lang the language to use rules for
76
+ #
77
+ def self.parse str, lang = :default
78
+ lang = lang.to_sym
79
+ str.split(/\R{2,}/).map { |para|
80
+ para.gsub(URI.regexp) { |m| "⚓#{Base64.encode64 m}⚓" }
81
+ instance.yaml.each { |k, values|
82
+ values.each { |k, v|
83
+ if !!v[:re]
84
+ v[lang] = v[:default] if (!v[lang] || v[lang].size.zero?)
85
+ raise MalformedRulesFile.new "Malformed rules file (no subst for #{v})" \
86
+ if !v[lang] || v[lang].size.zero?
87
+ substituted = !!v[:pattern] ?
88
+ para.gsub!(/#{v[:re]}/) { |m| m.gsub(/#{v[:pattern]}/, v[lang].first) } :
89
+ para.gsub!(/#{v[:re]}/, v[lang].first)
90
+ # logger.warn "Unsafe substitutions were made to source:\n# ⇒ #{para}"\
91
+ # if v[:alert] && substituted
92
+ if v[lang].size > 1
93
+ para.gsub!(/#{v[lang].first}/) { |m|
94
+ prev = $`
95
+ obsoletes = prev.count(v[lang].join)
96
+ compliants = values[v[:compliant].to_sym][lang] ||
97
+ values[v[:compliant].to_sym][:default]
98
+ obsoletes -= prev.count(compliants.join) \
99
+ if !!v[:compliant]
100
+ !!v[:slave] ?
101
+ obsoletes -= prev.count(v[:original]) + 1 :
102
+ obsoletes += prev.count(v[:original])
103
+
104
+ v[lang][obsoletes % v[lang].size]
105
+ }
106
+ end
107
+ end
108
+ }
109
+ }
110
+ para
111
+ }.join(%Q(
112
+
113
+ ))
114
+ .gsub(/⚓(.*)⚓/m) { |m| Base64.decode64 m }
115
+ end
116
+
117
+ # Out-of-place version of `String` typographing. See #parse!
118
+ def self.parse! str, lang = :default
119
+ str.replace self.parse(str, lang)
120
+ end
121
+ private
122
+ DEFAULT_SET = 'typogrowth'
123
+
124
+ def initialize file
125
+ @yaml = YAML.load_file "#{File.dirname(__FILE__)}/config/#{file}.yaml"
126
+ @yaml.delete(:placeholder)
127
+ end
128
+
129
+ @@instance = Parser.new(DEFAULT_SET)
130
+
131
+ def self.instance
132
+ @@instance
133
+ end
134
+
135
+ private_class_method :new
136
+ end
137
+
138
+ def self.parse str, lang = :default
139
+ Parser.parse str, lang
140
+ end
141
+ def self.parse! str, lang = :default
142
+ Parser.parse! str, lang
143
+ end
144
+ end
145
+
@@ -0,0 +1,73 @@
1
+ #
2
+ # = Hash Recursive Merge
3
+ #
4
+ # Merges a Ruby Hash recursively, Also known as deep merge.
5
+ # Recursive version of Hash#merge and Hash#merge!.
6
+ #
7
+ # Category:: Ruby
8
+ # Package:: Hash
9
+ # Author:: Simone Carletti <weppos@weppos.net>
10
+ # Copyright:: 2007-2008 The Authors
11
+ # License:: MIT License
12
+ # Link:: http://www.simonecarletti.com/
13
+ # Source:: http://gist.github.com/gists/6391/
14
+ #
15
+ module HashRecursiveMerge
16
+
17
+ #
18
+ # Recursive version of Hash#merge!
19
+ #
20
+ # Adds the contents of +other_hash+ to +hsh+,
21
+ # merging entries in +hsh+ with duplicate keys with those from +other_hash+.
22
+ #
23
+ # Compared with Hash#merge!, this method supports nested hashes.
24
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
25
+ # it merges and returns the values from both arrays.
26
+ #
27
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
28
+ # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
29
+ # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
30
+ #
31
+ # Simply using Hash#merge! would return
32
+ #
33
+ # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
34
+ #
35
+ def rmerge!(other_hash)
36
+ merge!(other_hash) do |key, oldval, newval|
37
+ oldval.class == self.class ? oldval.rmerge!(newval) : newval
38
+ end
39
+ end
40
+
41
+ #
42
+ # Recursive version of Hash#merge
43
+ #
44
+ # Compared with Hash#merge!, this method supports nested hashes.
45
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
46
+ # it merges and returns the values from both arrays.
47
+ #
48
+ # Compared with Hash#merge, this method provides a different approch
49
+ # for merging nasted hashes.
50
+ # If the value of a given key is an Hash and both +other_hash+ abd +hsh
51
+ # includes the same key, the value is merged instead replaced with
52
+ # +other_hash+ value.
53
+ #
54
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
55
+ # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
56
+ # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
57
+ #
58
+ # Simply using Hash#merge would return
59
+ #
60
+ # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
61
+ #
62
+ def rmerge(other_hash)
63
+ r = {}
64
+ merge(other_hash) do |key, oldval, newval|
65
+ r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ class Hash
72
+ include HashRecursiveMerge
73
+ end
@@ -0,0 +1,11 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'typogrowth'
4
+
5
+ # Requires supporting files with custom matchers and macros, etc,
6
+ # in ./support/ and its subdirectories.
7
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
8
+
9
+ RSpec.configure do |config|
10
+
11
+ end
data/spec/test.input ADDED
@@ -0,0 +1,24 @@
1
+ And God said "∇×(∇×F) = ∇(∇·F) − ∇2F" and there was light.
2
+ And God said “∇×(∇×F) = ∇(∇·F) − ∇2F” and there was light.
3
+ And God said "I--heard "Booh "Bah" Booh" and "Bam" in heaven" and there was light.
4
+ And God said “I—heard ‘Booh “Bah” Booh’ and ‘Bam’ in heaven” and there was light.
5
+ And God said "I - heard "Booh Bah Booh" and "Bam" in heaven" and there was light.
6
+ And God said “I—heard ‘Booh Bah Booh’ and ‘Bam’ in heaven” and there was light.
7
+ And God said "Oslo coordinates are: 59°57′N 10°45′E" and there was light.
8
+ And God said “Oslo coordinates are: 59°57′N 10°45′E” and there was light.
9
+ And God said "That's a 6.3" man, he sees sunsets at 10°20'30" E." and there was light.
10
+ And God said “That’s a 6.3″ man, he sees sunsets at 10°20′30″ E.” and there was light.
11
+ And God said "Foo" , and there was light.
12
+ And God said “Foo,” and there was light.
13
+ And God said "Baz heard 'Foos' Bar' once" , and there was light.
14
+ And God said “Baz heard ‘Foos’ Bar’ once,” and there was light.
15
+ And God, loving ellipsis, said.... And...
16
+ And God, loving ellipsis, said… And…
17
+
18
+ An easier way to solve the problem requires understanding that regular expressions can be written as finite state machines, and vice versa.
19
+
20
+ Let's start by building the state machine which matches multiples of 3. We know that a number is a multiple of 3, if and only if the sums of the digits are a multiple of 3 (divisibility by three proof).
21
+
22
+ The state machine is going to process the input and keep track of the sum of the digits. We only need the sum modulo 3, so we'll have a pretty simple state machine with 3 states: state A (starting state), state B (we are off by 1), state C (we are off by 2).
23
+
24
+ When we are in state A, if we get a "0", "3", "6" or "9", we remain on state A. If we get a "1", "4" or "7" we move to state B. If we get a "2", "5" or "8" we move to state C.
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Typogrowth do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,38 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require 'typogrowth/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'typogrowth'
6
+ s.version = Typogrowth::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.date = '2013-10-06'
9
+ s.authors = ['Alexei Matyushkin']
10
+ s.email = 'am@mudasobwa.ru'
11
+ s.homepage = 'http://github.com/mudasobwa/typogrowth'
12
+ s.license = 'LICENSE'
13
+ s.summary = %Q{Simple library to produce typographyed texts}
14
+ s.description = %Q{Gem provides string monkeypatches to typograph strings}
15
+ s.extra_rdoc_files = [
16
+ 'LICENSE',
17
+ 'README.md',
18
+ ]
19
+
20
+ s.required_rubygems_version = Gem::Requirement.new('>= 1.3.7')
21
+ s.rubygems_version = '1.3.7'
22
+ s.specification_version = 3
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ['lib']
28
+
29
+ s.add_development_dependency 'rspec'
30
+ s.add_development_dependency 'rake'
31
+ s.add_development_dependency 'yard'
32
+ s.add_development_dependency 'cucumber'
33
+ s.add_development_dependency 'yard-cucumber'
34
+
35
+ s.add_dependency 'psych'
36
+ s.add_dependency 'i18n'
37
+ end
38
+
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typogrowth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexei Matyushkin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: cucumber
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard-cucumber
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: psych
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: i18n
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Gem provides string monkeypatches to typograph strings
112
+ email: am@mudasobwa.ru
113
+ executables:
114
+ - typo
115
+ extensions: []
116
+ extra_rdoc_files:
117
+ - LICENSE
118
+ - README.md
119
+ files:
120
+ - .document
121
+ - .gitignore
122
+ - .travis.yml
123
+ - Gemfile
124
+ - LICENSE
125
+ - README.md
126
+ - Rakefile
127
+ - bin/typo
128
+ - features/step_definitions/typogrowth_steps.rb
129
+ - features/support/env.rb
130
+ - features/typogrowth.feature
131
+ - lib/config/typogrowth.yaml
132
+ - lib/typogrowth.rb
133
+ - lib/typogrowth/string.rb
134
+ - lib/typogrowth/version.rb
135
+ - lib/utils/hash_recursive_merge.rb
136
+ - spec/spec_helper.rb
137
+ - spec/test.input
138
+ - spec/typogrowth_spec.rb
139
+ - typogrowth.gemspec
140
+ homepage: http://github.com/mudasobwa/typogrowth
141
+ licenses:
142
+ - LICENSE
143
+ metadata: {}
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ! '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: 1.3.7
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 2.0.2
161
+ signing_key:
162
+ specification_version: 3
163
+ summary: Simple library to produce typographyed texts
164
+ test_files: []
165
+ has_rdoc: