typogrowth 0.9.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 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: