hierogloss 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,55 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <%
6
+ # This code from Kramdown's default template.
7
+ extend ::Kramdown::Utils::Html
8
+ title = 'Hierogloss'
9
+ h = @converter.root.children.find {|c| c.type == :header}
10
+ if h
11
+ collector = lambda {|c| c.children.collect {|cc| cc.type == :text ? escape_html(cc.value, :text) : collector.call(cc)}.join('')}
12
+ title = collector.call(h)
13
+ end
14
+ %>
15
+ <title><%= title %></title>
16
+ <meta name="generator" content="hierogloss kramdown <%= ::Kramdown::VERSION %>" />
17
+
18
+ <style type="text/css">
19
+ @font-face {
20
+ font-family: 'WebGardiner';
21
+ src: url('fonts/Gardiner.eot');
22
+ src: local('☺'), url('fonts/Gardiner.woff') format('woff'), url('fonts/Gardiner.ttf') format('truetype'), url('fonts/Gardiner.svg') format('svg');
23
+ font-weight: normal;
24
+ font-style: normal;
25
+ }
26
+
27
+ .hgls-h, .hgls-l {
28
+ font-family: "WebGardiner", "Aegyptus", "Gardiner", serif;
29
+ }
30
+ .hgls-l { font-style: normal; font-size: 18pt; }
31
+ .hgls-h { font-size: 24px; }
32
+
33
+ .hgls-gloss td { padding-right: 18px; }
34
+ .hgls-gloss {
35
+ margin-left: 18px;
36
+ page-break-inside: avoid;
37
+ }
38
+ p.hgls-gloss {
39
+ margin-top: 0;
40
+ }
41
+ table.hgls-gloss a:link, table.hgls-gloss a:visited {
42
+ color: black;
43
+ text-decoration: none;
44
+ }
45
+ table.hgls-gloss a:hover {
46
+ text-decoration: underline;
47
+ }
48
+ </style>
49
+ </head>
50
+ <body>
51
+
52
+ <%= @body %>
53
+
54
+ </body>
55
+ </html>
@@ -0,0 +1,13 @@
1
+ # Disjunction in Middle Egyptian
2
+
3
+ This example is based on one in Allen's excellent [Middle Egyptian: An
4
+ Introduction to the Language and Culture of Hieroglyphs][allen].
5
+
6
+ H: π“Šƒπ“€€π“€ | π“Šƒπ“π“ | 𓂋𓏀π“Šͺπ“…±
7
+ L: s | s.t | r-pw
8
+ G: man | woman | whichever
9
+ T: either [a] man or [a] woman
10
+
11
+ You can emphasize an "or" by following the options with {r-pw}.
12
+
13
+ [allen]: http://www.amazon.com/Middle-Egyptian-Introduction-Language-Hieroglyphs/dp/0521741440
Binary file
@@ -0,0 +1,11 @@
1
+ H: 𓃂 | π“ π“ˆ–π“Ώπ“…±π“Š΅π“π“Šͺ | π“Ήπ“ˆ– | π“Ž›π“Œπ“
2
+ L: wab | mnTw-Htp | ir(w)~n | Hnw.t
3
+ G: prΓͺtre | Mentouhotep | nΓ© de | Henout
4
+
5
+ H: 𓃂 | π“‚‹π“ˆ–π“†‘π“‹΄π“ˆ–π“ƒ€π“…± | 𓐙𓉻
6
+ L: wab | rnfsnbw | mAa(-xrw)
7
+ G: prΓͺtre | Renef-senebou | juste de voix
8
+
9
+ H: 𓅭𓏏𓆑 | π“Ž›π“Œπ“ | π“Ήπ“ˆ– | π“‹žπ“ˆ’π“₯𓂝𓂝𓏏 | 𓐙𓉻
10
+ L: sA.t=f | Hnw.t | ir(w) n | nbw(.t)-aat | mAa(.t-xrw)
11
+ G: fille=sa | Henout | nΓ©e de | Neboutaat | juste de voix
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hierogloss/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hierogloss"
8
+ spec.version = Hierogloss::VERSION
9
+ spec.authors = ["Eric Kidd"]
10
+ spec.email = ["git@randomhacks.net"]
11
+ spec.description = %q{Extends the Markdown parser Kramdown to support hieroglyphs, inline multi-column glosses, and output to BBCode for use on forums. Includes an executable for processing files and a webfont version of the Gardiner signs.}
12
+ spec.summary = %q{Markdown extensions for hieroglyphic glosses and BBCode}
13
+ spec.homepage = "https://github.com/emk/hierogloss"
14
+ spec.license = "Public domain + other open source licenses"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "kramdown", "~> 1.3"
22
+ spec.add_development_dependency "prawn", "~> 0.14.0"
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "minitest"
26
+ end
@@ -0,0 +1,39 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Hierogloss
4
+ #:nodoc: Internal dictionary-related utilities. APIs are not stable.
5
+ module Dictionary
6
+ DATA_DIR = File.join(File.dirname(__FILE__), '..', '..', 'data')
7
+ MDC_MAPPING_PATH = File.join(DATA_DIR, "Unicode-MdC-Mapping-v1.utf8")
8
+
9
+ GARDINER = {}
10
+ File.open(MDC_MAPPING_PATH, "r:bom|utf-8") do |f|
11
+ f.each_line do |l|
12
+ l.chomp!
13
+ sign, hex, codes, remarks = l.split(/\t/, 4)
14
+ for code in codes.split(/ /)
15
+ next unless code =~ /\A[A-Z][0-9]+\z/
16
+ GARDINER[sign] = code
17
+ end
18
+ end
19
+ end
20
+ "𓄿𓇋𓏭𓂝𓅱𓏲𓃀π“Šͺπ“†‘π“…“π“ˆ–π“‚‹π“‰”π“Ž›π“π“„‘π“Šƒπ“‹΄π“ˆ™π“ˆŽπ“Ž‘π“ŽΌπ“π“Ώπ“‚§π“†“".each_char do |c|
21
+ GARDINER.delete(c)
22
+ end
23
+
24
+ # Try to kick things into shape for hierogl.ch.
25
+ def self.headword(word)
26
+ hw = word
27
+ hw.gsub!(/[()]/, '')
28
+ hw.sub!(/=.*\z/, '')
29
+ hw.sub!(/\.w?t\z/, 't')
30
+ hw.sub!(/\..*\z/, '')
31
+ hw
32
+ end
33
+
34
+ # Given a Unicode hieroglyph, get the corresponding Gardiner sign.
35
+ def self.gardiner(sign)
36
+ GARDINER[sign]
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,174 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ require 'hierogloss/dictionary'
5
+
6
+ module Hierogloss
7
+ #:nodoc:
8
+ class Row
9
+ attr_reader :raw_cells
10
+
11
+ def initialize(row_text)
12
+ @raw_cells = row_text.split(/\|/).map {|c| c.strip }
13
+ end
14
+
15
+ def span?
16
+ false
17
+ end
18
+
19
+ def attributes
20
+ attrs = {}
21
+ attrs['class'] = class_attr if class_attr
22
+ attrs
23
+ end
24
+
25
+ def class_attr
26
+ nil
27
+ end
28
+
29
+ def to_kramdown
30
+ attrs = attributes
31
+ tr = Kramdown::Element.new(:tr, nil, attrs)
32
+ raw_cells.each do |c|
33
+ td = Kramdown::Element.new(:td)
34
+ children = cell_to_kramdown(c)
35
+ if children.kind_of?(Array)
36
+ td.children.concat(children)
37
+ else
38
+ td.children << children
39
+ end
40
+ tr.children << td
41
+ end
42
+ tr
43
+ end
44
+
45
+ def cell_to_kramdown(cell)
46
+ Kramdown::Element.new(:text, cell)
47
+ end
48
+
49
+ def search_link(query, text)
50
+ base_url = "http://www.hierogl.ch/hiero/Sp%C3%A9cial:Recherche"
51
+ escaped_query = CGI.escape(query)
52
+ url = "#{base_url}?search=#{escaped_query}&go=Lire"
53
+
54
+ link = Kramdown::Element.new(:a, nil, {'href' => url})
55
+ link.children << Kramdown::Element.new(:text, text)
56
+ link
57
+ end
58
+ end
59
+
60
+ #:nodoc:
61
+ class HieroglyphRow < Row
62
+ def class_attr
63
+ 'hgls-h'
64
+ end
65
+
66
+ def cell_to_kramdown(cell)
67
+ cell.chars.map do |c|
68
+ gardiner = Dictionary.gardiner(c)
69
+ if !gardiner.nil?
70
+ search_link("Signe:#{gardiner}", c)
71
+ else
72
+ Kramdown::Element.new(:text, c)
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ #:nodoc:
79
+ class TransliterationRow < Row
80
+ JR_TRANSLITERATION = {
81
+ "A" => "ꜣ",
82
+ "i" => "j",
83
+ "a" => "κœ₯",
84
+ "H" => "αΈ₯",
85
+ "x" => "αΈ«",
86
+ "X" => "αΊ–",
87
+ "S" => "Ε‘",
88
+ "q" => "αΈ³",
89
+ "K" => "αΈ³",
90
+ "T" => "αΉ―",
91
+ "D" => "ḏ"
92
+ }
93
+
94
+ def self.fancy(tl)
95
+ tl.chars.map {|c| JR_TRANSLITERATION[c] || c }.join
96
+ end
97
+
98
+ def class_attr
99
+ 'hgls-l'
100
+ end
101
+
102
+ def cell_to_kramdown(cell)
103
+ fancy = self.class.fancy(cell)
104
+ search_link(Dictionary.headword(cell), fancy)
105
+ end
106
+ end
107
+
108
+ #:nodoc:
109
+ class TranslationRow
110
+ attr_reader :text
111
+
112
+ def initialize(row_text)
113
+ @text = row_text.strip
114
+ end
115
+
116
+ def span?
117
+ true
118
+ end
119
+
120
+ def class_attr
121
+ 'hgls-t'
122
+ end
123
+
124
+ def to_kramdown
125
+ em = Kramdown::Element.new(:em, nil)
126
+ em.children << Kramdown::Element.new(:text, text)
127
+ em
128
+ end
129
+ end
130
+
131
+ #:nodoc:
132
+ class Gloss
133
+ attr_reader :rows
134
+
135
+ def initialize(text)
136
+ @rows = text.lines.map {|l| l.chomp }.map do |row|
137
+ row =~ /\A(\w+):(.*)\z/
138
+ raise "C'est quoi, [[#{row}]]\?" unless $1 && $2
139
+ type =
140
+ case $1
141
+ when "H" then HieroglyphRow
142
+ when "L" then TransliterationRow
143
+ when "T" then TranslationRow
144
+ else Row
145
+ end
146
+ type.new($2)
147
+ end
148
+ end
149
+
150
+ def to_kramdown
151
+ result = []
152
+ # Neither Kramdown nor BBCode support rowspans, so we'll just cheat
153
+ # for now.
154
+ rows.chunk {|r| r.span? }.each do |spans, rows|
155
+ if spans
156
+ rows.each do |r|
157
+ class_attr = "hgls-gloss #{r.class_attr}"
158
+ p = Kramdown::Element.new(:p, nil, 'class' => class_attr)
159
+ p.children << r.to_kramdown
160
+ result << p
161
+ end
162
+ else
163
+ table = Kramdown::Element.new(:table, nil, { 'class' => 'hgls-gloss' },
164
+ alignment: [])
165
+ tbody = Kramdown::Element.new(:tbody)
166
+ table.children << tbody
167
+ rows.each {|r| tbody.children << r.to_kramdown }
168
+ result << table
169
+ end
170
+ end
171
+ result
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,3 @@
1
+ module Hierogloss
2
+ VERSION = "0.0.1"
3
+ end
data/lib/hierogloss.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'kramdown'
2
+ require "hierogloss/version"
3
+ require "hierogloss/dictionary"
4
+ require "hierogloss/gloss"
5
+ require "kramdown/parser/hierogloss"
6
+ require "kramdown/converter/bbcode"
7
+ # No guarantess of backwards compatibility for this one.
8
+ require "kramdown/converter/htlal"
9
+
10
+ # Most of our internal APIs are undocumented at this point, but you
11
+ # can use this gem via Kramdown's APIs.
12
+ #
13
+ # Kramdown::Document.new(ARGF.read, input: 'hierogloss').to_html
14
+ #
15
+ # Or if you want to post on an online forum, try:
16
+ #
17
+ # Kramdown::Document.new(ARGF.read, input: 'hierogloss').to_bbcode
18
+ #
19
+ # Note that the BBCode converter does not yet support all available
20
+ # Markdown constructs. You're welcome to try other kramdown backends;
21
+ # some of them may more-or-less work.
22
+ module Hierogloss
23
+ # Nothing to do here yet.
24
+ end
@@ -0,0 +1,113 @@
1
+ module Kramdown
2
+ module Converter
3
+ # Outputs some of the most common Markdown elements as BBCode.
4
+ # Everything in this class is internal.
5
+ class Bbcode < Base
6
+ include ::Kramdown::Utils::Html
7
+
8
+ DISPATCHER = Hash.new {|h,k| h[k] = "convert_#{k}"} #:nodoc:
9
+
10
+ def initialize(root, options)
11
+ super
12
+ @stack = []
13
+ end
14
+
15
+ def convert(el, opts = {})
16
+ send(DISPATCHER[el.type], el, opts)
17
+ end
18
+
19
+ def inner(el, opts)
20
+ @stack.push([el, opts])
21
+ result = el.children.map do |inner_el|
22
+ convert(inner_el, options)
23
+ end
24
+ @stack.pop
25
+ result
26
+ end
27
+
28
+ def convert_header(el, opts)
29
+ tag("b", nil, inner(el, opts)) + ["\n"]
30
+ end
31
+
32
+ def convert_p(el, opts)
33
+ inner(el, opts) + ["\n"]
34
+ end
35
+
36
+ def convert_blank(el, opts)
37
+ "\n"
38
+ end
39
+
40
+ def convert_text(el, opts)
41
+ # Wouldn't it be nice if we could escape BBCode?
42
+ el.value.gsub(/\s+/, ' ') # Ignore newlines in raw text.
43
+ end
44
+
45
+ def convert_em(el, opts)
46
+ tag("i", nil, inner(el, opts))
47
+ end
48
+
49
+ def convert_strong(el, opts)
50
+ tag("b", nil, inner(el, opts))
51
+ end
52
+
53
+ def convert_a(el, opts)
54
+ tag("url", el.attr['href'], inner(el, opts))
55
+ end
56
+
57
+ def convert_smart_quote(el, opts)
58
+ entity_to_str(smart_quote_entity(el))
59
+ end
60
+
61
+ def convert_table(el, opts)
62
+ tag("table", nil, ["\n"] + inner(el, opts))
63
+ end
64
+
65
+ def convert_tbody(el, opts)
66
+ inner(el, opts)
67
+ end
68
+
69
+ def convert_tr(el, opts)
70
+ tag("tr", nil, inner(el, opts)) + ["\n"]
71
+ end
72
+
73
+ def convert_td(el, opts)
74
+ if @stack.last.first.attr['class'] == 'hgls-h'
75
+ tag("td", nil, tag("size", "24", inner(el, opts)))
76
+ else
77
+ tag("td", nil, inner(el, opts))
78
+ end
79
+ end
80
+
81
+ def convert_blockquote(el, opts)
82
+ # No newline because BBCode will add one itself. We also run
83
+ # results_to_text so we can clean up trailing newlines.
84
+ tag("quote", nil, results_to_text(inner(el, opts)))
85
+ end
86
+
87
+ def convert_codeblock(el, opts)
88
+ # No newline because BBCode will add one itself.
89
+ tag("code", nil, el.value.sub(/\n\z/, ''))
90
+ end
91
+
92
+ def convert_img(el, opts)
93
+ tag("img", nil, el.attr['src'])
94
+ end
95
+
96
+ def convert_root(el, opts)
97
+ results_to_text(inner(el, opts))
98
+ end
99
+
100
+ def tag(name, arg, content)
101
+ if arg
102
+ ["[#{name}=#{arg}]", content, "[/#{name}]"]
103
+ else
104
+ ["[#{name}]", content, "[/#{name}]"]
105
+ end
106
+ end
107
+
108
+ def results_to_text(results)
109
+ results.flatten.compact.join.sub(/\n+\z/, '')
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,34 @@
1
+ module Kramdown
2
+ module Converter
3
+ # Outputs some of the most common Markdown elements in a stripped down
4
+ # BBCode dialect without table support or reliable font size control.
5
+ # Everything in this class is internal.
6
+ class Htlal < Bbcode
7
+ include ::Kramdown::Utils::Html
8
+
9
+ def convert_table(el, opts)
10
+ inner(el, opts)
11
+ end
12
+
13
+ def convert_tbody(el, opts)
14
+ inner(el, opts)
15
+ end
16
+
17
+ def convert_tr(el, opts)
18
+ spaced = []
19
+ inner(el, opts).each do |td|
20
+ spaced << td << " | "
21
+ end
22
+ spaced.pop
23
+ spaced << "\n"
24
+ spaced
25
+ end
26
+
27
+ def convert_td(el, opts)
28
+ # We'd like to make hieroglyph cells bigger, but that doesn't play
29
+ # nicely with links.
30
+ inner(el, opts)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,37 @@
1
+ module Kramdown
2
+ module Parser
3
+ # Parses an extended Kramdown syntax with support for inline glosses.
4
+ # Everything in this class is internal.
5
+ class Hierogloss < ::Kramdown::Parser::Kramdown
6
+ def initialize(source, options)
7
+ super
8
+ @span_parsers.unshift(:translit)
9
+ @block_parsers.unshift(:gloss)
10
+ end
11
+
12
+ TRANSLIT_START = /{.*?}/
13
+
14
+ def parse_translit
15
+ @src.pos += @src.matched_size
16
+ mdc = @src.matched[1..-2]
17
+ em = Element.new(:em, nil, 'class' => 'hgls-l')
18
+ em.children <<
19
+ Element.new(:text, ::Hierogloss::TransliterationRow.fancy(mdc))
20
+ @tree.children << em
21
+ end
22
+ define_parser(:translit, TRANSLIT_START, '{')
23
+
24
+ GLOSS_START = /^(H|L|G|T):/
25
+ GLOSS_MATCH = /((^(H|L|G|T):.*)\r?\n)*/
26
+
27
+ def parse_gloss
28
+ start_line_number = @src.current_line_number
29
+ data = @src.scan(self.class::GLOSS_MATCH)
30
+ @tree.children.concat(::Hierogloss::Gloss.new(data).to_kramdown)
31
+ true
32
+ end
33
+
34
+ define_parser(:gloss, GLOSS_START)
35
+ end
36
+ end
37
+ end
data/src/Gardiner.ttf ADDED
Binary file
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'hierogloss'
3
+
4
+ require 'minitest/autorun'
@@ -0,0 +1,50 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest_helper'
3
+
4
+ class TestDictionary < Minitest::Test
5
+ def assert_hw(headword, word)
6
+ assert_equal(headword, Hierogloss::Dictionary.headword(word))
7
+ end
8
+
9
+ def test_should_leave_simple_headwords_alone
10
+ assert_hw("xr", "xr")
11
+ assert_hw("Hr", "Hr")
12
+ end
13
+
14
+ def test_should_strip_parens
15
+ assert_hw("ny", "n(y)")
16
+ assert_hw("mAa-Hrw", "mAa(-Hrw)")
17
+ end
18
+
19
+ def test_should_strip_clitics
20
+ assert_hw("ir", "ir=n")
21
+ assert_hw("im", "im=s")
22
+ end
23
+
24
+ def test_should_remove_dot_before_t
25
+ assert_hw("Sspt", "Ssp.t")
26
+ assert_hw("Hmt", "Hm.t")
27
+ end
28
+
29
+ def test_should_strip_plurals
30
+ assert_hw("Sspt", "Ssp.wt")
31
+ assert_hw("hrw", "hrw.w")
32
+ end
33
+
34
+ def test_should_strip_verb_endings
35
+ assert_hw("ir", "ir.n=f")
36
+ assert_hw("Dd", "Dd.n")
37
+ end
38
+
39
+ def test_should_provide_gardiner_signs_for_most_signs
40
+ assert_equal("A1", Hierogloss::Dictionary.gardiner("π“€€"))
41
+ assert_equal("D4", Hierogloss::Dictionary.gardiner("𓁹"))
42
+ end
43
+
44
+ def test_should_not_provide_gardiner_signs_for_uniliterals
45
+ # Let's not link these common characters.
46
+ "𓄿𓇋𓏭𓂝𓅱𓏲𓃀π“Šͺπ“†‘π“…“π“ˆ–π“‚‹π“‰”π“Ž›π“π“„‘π“Šƒπ“‹΄π“ˆ™π“ˆŽπ“Ž‘π“ŽΌπ“π“Ώπ“‚§π“†“".each_char do |c|
47
+ assert_nil(Hierogloss::Dictionary.gardiner(c), "should not translate #{c}")
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,43 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest_helper'
3
+
4
+ class TestGloss < MiniTest::Test
5
+ include Hierogloss
6
+
7
+ def setup
8
+ input = <<EOD
9
+ H: π“Šƒπ“€€π“€ | π“Šƒπ“π“
10
+ L: s | s.t
11
+ G: homme | femme
12
+ T: l'homme et la femme
13
+ EOD
14
+ @gloss = Hierogloss::Gloss.new(input)
15
+ end
16
+
17
+ def assert_row(type, raw_cells, row)
18
+ assert_instance_of(type, row)
19
+ refute(row.span?)
20
+ assert_equal(raw_cells, row.raw_cells)
21
+ end
22
+
23
+ def test_should_parse_gloss_into_appropriate_rows
24
+ assert_equal(4, @gloss.rows.length)
25
+ assert_row(HieroglyphRow, ["π“Šƒπ“€€π“€", "π“Šƒπ“π“"], @gloss.rows[0])
26
+ assert_row(TransliterationRow, ["s", "s.t"], @gloss.rows[1])
27
+ assert_row(Row, ["homme", "femme"], @gloss.rows[2])
28
+
29
+ # Translations don't have cells.
30
+ assert_instance_of(TranslationRow, @gloss.rows[3])
31
+ assert(@gloss.rows[3].span?)
32
+ assert_equal("l'homme et la femme", @gloss.rows[3].text)
33
+ end
34
+
35
+ def test_should_be_convertible_to_a_list_of_kramdown_elements
36
+ # We don't actually care what's in there; we just want to render
37
+ # something plausible.
38
+ kramdown = @gloss.to_kramdown
39
+ assert_instance_of(Array, kramdown)
40
+ assert(kramdown.length > 0)
41
+ kramdown.each {|k| assert_instance_of(Kramdown::Element, k) }
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ require 'minitest_helper'
2
+
3
+ class TestHierogloss < MiniTest::Test
4
+ def test_that_it_has_a_version_number
5
+ refute_nil ::Hierogloss::VERSION
6
+ end
7
+ end