i18n_checker 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 23733a725ec850046a4c27be2c3b0456a24d2497
4
- data.tar.gz: 17f373338bc76782d3eaa2ae29ccef8dd0b409c9
3
+ metadata.gz: 0b3dfa7d91d83ae12f4fac222f03b586b233ff51
4
+ data.tar.gz: 05deb6f41ac172a7c94d72391b248acef04b41e8
5
5
  SHA512:
6
- metadata.gz: 7c8d90d81e4df2de391d6a73cf5467ab254b44552c18a1e893b58ac269cf1f3f6cca7a84ae01a0f6aaf254355f4960a07dbb3a463495d56787974d8399249fc6
7
- data.tar.gz: 0b4e66c6b101937433c3e57ecd90d9f95340df3c815d6febcdf16c2705fb718e0ebd9c7e3c2838470ca4ed3d10fa416000c8bbd1b1f9b5c8f38225567f9e81fc
6
+ metadata.gz: e3f14b54918d9781c5767ee4c879dbb4cf25e2786dea4ed4951cbbb8bb4c9a205e328700f4f0daa519d61d02b50381abd52498679e55c205a190146037f970b5
7
+ data.tar.gz: 7b13bdb096146bb7dca8a9e575f83f736002f5e078669c6d64232a4d466ade9c54e61f1fb8468ceea4f40dbe568333c2f9c3bbe9b7ca417fe761e6e8b096fe82
data/Gemfile.lock CHANGED
@@ -1,14 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- i18n_checker (0.1.3)
4
+ i18n_checker (0.2.0)
5
5
  colorator (~> 1.1.0)
6
6
  haml_parser (~> 0.4)
7
+ parser
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
10
11
  specs:
11
12
  ansi (1.5.0)
13
+ ast (2.3.0)
12
14
  colorator (1.1.0)
13
15
  coveralls (0.8.19)
14
16
  json (>= 1.8, < 3)
@@ -21,6 +23,8 @@ GEM
21
23
  haml_parser (0.4.1)
22
24
  hirb (0.7.3)
23
25
  json (2.0.3)
26
+ parser (2.4.0.0)
27
+ ast (~> 2.2)
24
28
  rake (10.5.0)
25
29
  rspec (3.5.0)
26
30
  rspec-core (~> 3.5.0)
data/README.md CHANGED
@@ -7,7 +7,10 @@
7
7
 
8
8
  This gem provides a **Rake task** to check translation file mistakes and translated text references from template files etc.
9
9
 
10
- In the current version you can check the translation text reference from Haml's template file.
10
+ Current version supports Ruby source code, Haml template file.
11
+
12
+ * Ruby source
13
+ * Haml template
11
14
 
12
15
  ## Basic usage
13
16
 
@@ -17,8 +20,9 @@ Add the following tasks to your **Rakefile**.
17
20
  require 'i18n_checker/rake_task'
18
21
 
19
22
  I18nChecker::RakeTask.new do |task|
20
- task.locale_file_paths = FileList['spec/fixtures/locales/**'] # locale file paths
21
- task.haml_template_paths = FileList['spec/fixtures/*'] # haml template paths
23
+ task.template_paths = FileList['app/views/*'] # haml templates
24
+ task.locale_file_paths = FileList['config/locales/*'] # locale file paths
25
+ paths
22
26
  end
23
27
  ```
24
28
 
data/Rakefile CHANGED
@@ -5,8 +5,8 @@ require 'i18n_checker/rake_task'
5
5
  RSpec::Core::RakeTask.new(:spec)
6
6
 
7
7
  I18nChecker::RakeTask.new do |task|
8
+ task.template_paths = FileList['spec/fixtures/haml/*', 'spec/fixtures/ruby/*']
8
9
  task.locale_file_paths = FileList['spec/fixtures/locales/**']
9
- task.haml_template_paths = FileList['spec/fixtures/*']
10
10
  end
11
11
 
12
12
  task :default => :spec
data/i18n-checker.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = I18nChecker::VERSION
9
9
  spec.authors = ["holyshared"]
10
10
  spec.email = ["holy.shared.design@gmail.com"]
11
- spec.summary = "i18n checker for haml"
12
- spec.description = "It parses the haml template and checks the validity of the language file"
11
+ spec.summary = "Verification of translation of Ruby source and Haml template"
12
+ spec.description = "Verification of translation of Ruby source and Haml template"
13
13
  spec.homepage = "https://github.com/holyshared/i18n-checker"
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ['lib']
21
21
  spec.add_dependency 'haml_parser', '~> 0.4'
22
22
  spec.add_dependency 'colorator', '~> 1.1.0'
23
+ spec.add_dependency 'parser'
23
24
  spec.add_development_dependency 'bundler','~> 1.14'
24
25
  spec.add_development_dependency 'rake', '~> 10.0'
25
26
  spec.add_development_dependency 'rspec'
@@ -0,0 +1,11 @@
1
+ module I18nChecker
2
+ module Collectible
3
+ def collect(file)
4
+ end
5
+
6
+ def collect_all(files)
7
+ texts = files.map { |file| collect(file) }
8
+ texts.reduce { |texts, n| texts.concat(n) }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ require 'haml_parser'
2
+ require 'haml_parser/parser'
3
+ require 'i18n_checker/collectible'
4
+ require 'i18n_checker/locale/texts'
5
+
6
+ module I18nChecker
7
+ module Locale
8
+ module Collector
9
+ class Haml
10
+ include I18nChecker::Collectible
11
+
12
+ def collect(template_file)
13
+ template = read_template_file(template_file)
14
+ parser = HamlParser::Parser.new(filename: template_file)
15
+ I18nChecker::Locale::Texts.new(collect_locale_texts(parser.call(template)))
16
+ end
17
+
18
+ private
19
+
20
+ def read_template_file(template_file)
21
+ ::File.open(template_file, &:read)
22
+ end
23
+
24
+ def collect_locale_texts(ast)
25
+ locale_texts = []
26
+ locale_texts << collect_locale_text(ast)
27
+ return unless ast.respond_to?(:children)
28
+ locale_texts << ast.children.map { |child| collect_locale_texts(child) }
29
+ locale_texts.flatten.compact
30
+ end
31
+
32
+ def collect_locale_text(ast)
33
+ return locale_text_from_script(ast) if ast.kind_of?(HamlParser::Ast::Script)
34
+ return unless ast.respond_to?(:oneline_child)
35
+ return unless ast.oneline_child.kind_of?(HamlParser::Ast::Script)
36
+ locale_text_from_script(ast.oneline_child)
37
+ end
38
+
39
+ def locale_text_from_script(script_node)
40
+ return unless translate_script = script_node.script.match(/^t\(\'+(.+)\'+\)$/)
41
+ I18nChecker::Locale::Text.new(
42
+ file: script_node.filename,
43
+ line: script_node.lineno,
44
+ column: 0, # FIXME: collect from source file
45
+ text: translate_script[1]
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ require 'parser'
2
+ require 'parser/current'
3
+ require 'i18n_checker/collectible'
4
+ require 'i18n_checker/locale/texts'
5
+ require 'i18n_checker/locale/text_processor'
6
+
7
+ module I18nChecker
8
+ module Locale
9
+ module Collector
10
+ class Ruby
11
+ include I18nChecker::Collectible
12
+
13
+ def collect(source_file)
14
+ I18nChecker::Locale::Texts.new(process(source_file))
15
+ end
16
+
17
+ private
18
+
19
+ def buffer_of(source_file)
20
+ source_buffer = Parser::Source::Buffer.new('(string)')
21
+ source_buffer.source = ::File.read(source_file)
22
+ source_buffer
23
+ end
24
+
25
+ def parse_source(source_file)
26
+ parser = Parser::CurrentRuby.new
27
+ parser.parse(buffer_of(source_file))
28
+ end
29
+
30
+ def process(source_file)
31
+ processor = I18nChecker::Locale::TextProcessor.new(file: source_file)
32
+ processor.process(parse_source(source_file))
33
+ processor.locale_texts
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,2 @@
1
+ require 'i18n_checker/locale/collector/haml'
2
+ require 'i18n_checker/locale/collector/ruby'
@@ -2,7 +2,7 @@ require 'yaml'
2
2
 
3
3
  module I18nChecker
4
4
  module Locale
5
- class LocaleFile
5
+ class File
6
6
  attr_reader :lang, :locale_texts
7
7
 
8
8
  class << self
@@ -10,7 +10,7 @@ module I18nChecker
10
10
  new(YAML.load(s))
11
11
  end
12
12
  def load_yaml_file(yaml_file)
13
- load_yaml(File.open(yaml_file, &:read))
13
+ load_yaml(::File.open(yaml_file, &:read))
14
14
  end
15
15
  end
16
16
 
@@ -1,18 +1,21 @@
1
+ require 'forwardable'
2
+
1
3
  module I18nChecker
2
4
  module Locale
3
- class LocaleFiles
5
+ class Files
6
+ extend Forwardable
7
+
4
8
  include Enumerable
5
9
 
10
+ def_delegators :locale_files, :size, :each
11
+
6
12
  def initialize(locale_files = [])
7
13
  @locale_files = locale_files
8
14
  end
9
15
 
10
- def each(&block)
11
- locale_files.each(&block)
12
- end
13
-
14
16
  def delete_if(&block)
15
17
  locale_files.delete_if(&block)
18
+ self
16
19
  end
17
20
 
18
21
  private
@@ -0,0 +1,14 @@
1
+ module I18nChecker
2
+ module Locale
3
+ class Text
4
+ attr_reader :file, :text, :line, :column
5
+
6
+ def initialize(file:, text:, line:, column:)
7
+ @file = file
8
+ @text = text
9
+ @line = line
10
+ @column = column
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ require 'parser'
2
+ require 'parser/current'
3
+
4
+ module I18nChecker
5
+ module Locale
6
+ class TextProcessor < Parser::AST::Processor
7
+ attr_reader :file, :locale_texts
8
+
9
+ def initialize(file:, locale_texts: [])
10
+ @file = file
11
+ @locale_texts = locale_texts
12
+ end
13
+
14
+ def on_send(node)
15
+ receiver_node, method_name, *arg_nodes = *node
16
+ return super(node) unless method_name == :t
17
+ arg_node = arg_nodes.first
18
+ locale_texts << I18nChecker::Locale::Text.new(
19
+ file: file,
20
+ line: arg_node.loc.line,
21
+ column: arg_node.loc.column + 1,
22
+ text: arg_node.children.first
23
+ )
24
+ super(node)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,21 +1,29 @@
1
+ require 'forwardable'
2
+ require 'i18n_checker/locale/text'
3
+
1
4
  module I18nChecker
2
- module Haml
3
- class LocaleTexts
5
+ module Locale
6
+ class Texts
7
+ extend Forwardable
8
+
4
9
  include Enumerable
5
10
 
11
+ def_delegators :texts, :size, :each
12
+
6
13
  def initialize(texts = [])
7
14
  @texts = texts
8
15
  end
9
16
 
10
- def each(&block)
11
- texts.each(&block)
12
- end
13
-
14
17
  def concat(texts)
15
18
  @texts.concat(texts.to_a)
16
19
  self
17
20
  end
18
21
 
22
+ def uniq!
23
+ @texts.uniq!
24
+ self
25
+ end
26
+
19
27
  def ==(other)
20
28
  @texts == other.to_a
21
29
  end
@@ -30,3 +38,4 @@ module I18nChecker
30
38
  end
31
39
  end
32
40
  end
41
+
@@ -1,13 +1,34 @@
1
1
  require "i18n_checker/locale/key_path"
2
- require "i18n_checker/locale/locale_file"
3
- require "i18n_checker/locale/locale_files"
2
+ require "i18n_checker/locale/file"
3
+ require "i18n_checker/locale/files"
4
+ require "i18n_checker/locale/collector"
4
5
 
5
6
  module I18nChecker
6
7
  module Locale
7
8
  module Methods
8
9
  def load_of(locale_files)
9
- loaded_locale_files = locale_files.map { |locale_file| I18nChecker::Locale::LocaleFile.load_yaml_file(locale_file) }
10
- LocaleFiles.new(loaded_locale_files)
10
+ loaded_locale_files = locale_files.map { |locale_file| I18nChecker::Locale::File.load_yaml_file(locale_file) }
11
+ Files.new(loaded_locale_files)
12
+ end
13
+
14
+ def texts_of(resources)
15
+ files = resources.delete_if { |resource| ::File.directory?(resource) }.to_a
16
+ grouped_files = files.group_by { |file| ::File.extname(file) }
17
+ grouped_locale_texts = grouped_files.map do |k, v|
18
+ text_collector_of(k).collect_all(v)
19
+ end
20
+ grouped_locale_texts.reduce { |locale_texts, n| locale_texts.concat(n) }.uniq!
21
+ end
22
+
23
+ def text_collector_of(file_extname)
24
+ case file_extname
25
+ when '.haml'
26
+ I18nChecker::Locale::Collector::Haml.new
27
+ when '.rb'
28
+ I18nChecker::Locale::Collector::Ruby.new
29
+ else
30
+ raise StandardError, "not support #{extname} file" # FIXME: throw custom error!!
31
+ end
11
32
  end
12
33
  end
13
34
  extend Methods
@@ -4,13 +4,14 @@ require "i18n_checker/detector/detected_result"
4
4
 
5
5
  module I18nChecker
6
6
  class LocaleTextNotFoundChecker
7
- def initialize(locale_texts: [], reporter:)
7
+ def initialize(locale_texts: [], locale_files: [], reporter:)
8
8
  @reporter = reporter
9
9
  @locale_texts = locale_texts
10
+ @locale_files = locale_files
10
11
  end
11
12
 
12
- def check(locale_files = [])
13
- not_found_detector = I18nChecker::Detector::LocaleTextNotFound.new(locale_files)
13
+ def check
14
+ not_found_detector = I18nChecker::Detector::LocaleTextNotFound.new(@locale_files)
14
15
  not_found_result = @locale_texts.detect(not_found_detector)
15
16
  @reporter.report not_found_result
16
17
  end
@@ -1,21 +1,20 @@
1
1
  require 'rake'
2
2
  require 'rake/tasklib'
3
3
  require 'i18n_checker/locale'
4
- require 'i18n_checker/haml'
5
4
  require 'i18n_checker/reporter'
6
5
  require "i18n_checker/locale_text_not_found_checker"
7
6
 
8
7
  module I18nChecker
9
8
  class RakeTask < ::Rake::TaskLib
10
9
  attr_accessor :name
10
+ attr_accessor :template_paths
11
11
  attr_accessor :locale_file_paths
12
- attr_accessor :haml_template_paths
13
12
  attr_accessor :logger
14
13
 
15
14
  def initialize(name = :locale_check)
16
15
  @name = name
17
- @haml_template_paths = FileList['./app/views/*']
18
- @locale_file_paths = FileList['./locales/*']
16
+ @template_paths = FileList['app/views/*']
17
+ @locale_file_paths = FileList['config/locales/*']
19
18
  @logger = Logger.new(STDOUT)
20
19
  @logger.formatter = proc {|severity, datetime, progname, message|
21
20
  "#{message}\n"
@@ -33,14 +32,15 @@ module I18nChecker
33
32
 
34
33
  def run_task
35
34
  reporter = I18nChecker::Reporter::DefaultReporter.new(logger: logger)
35
+ locale_texts = I18nChecker::Locale.texts_of(template_paths)
36
36
  locale_files = I18nChecker::Locale.load_of(locale_file_paths)
37
- locale_texts = I18nChecker::Haml.collect_locale_text_of(haml_template_paths)
38
37
 
39
38
  checker = I18nChecker::LocaleTextNotFoundChecker.new(
40
39
  reporter: reporter,
41
- locale_texts: locale_texts
40
+ locale_texts: locale_texts,
41
+ locale_files: locale_files
42
42
  )
43
- checker.check(locale_files)
43
+ checker.check
44
44
  end
45
45
  end
46
46
  end
@@ -1,3 +1,3 @@
1
1
  module I18nChecker
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/i18n_checker.rb CHANGED
@@ -1,12 +1,15 @@
1
1
  require "i18n_checker/version"
2
2
  require "i18n_checker/rake_task"
3
- require "i18n_checker/haml/locale_text"
4
- require "i18n_checker/haml/locale_texts"
5
- require "i18n_checker/haml/locale_text_collector"
3
+ require "i18n_checker/collectible"
6
4
  require "i18n_checker/locale"
5
+ require "i18n_checker/locale/text_processor"
6
+ require "i18n_checker/locale/collector/haml"
7
+ require "i18n_checker/locale/collector/ruby"
8
+ require "i18n_checker/locale/text"
9
+ require "i18n_checker/locale/texts"
7
10
  require "i18n_checker/locale/key_path"
8
- require "i18n_checker/locale/locale_file"
9
- require "i18n_checker/locale/locale_files"
11
+ require "i18n_checker/locale/file"
12
+ require "i18n_checker/locale/files"
10
13
  require "i18n_checker/detector/locale_text_not_found"
11
14
  require "i18n_checker/detector/locale_text_result"
12
15
  require "i18n_checker/detector/detected_result"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n_checker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - holyshared
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-11 00:00:00.000000000 Z
11
+ date: 2017-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: haml_parser
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: parser
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -122,7 +136,7 @@ dependencies:
122
136
  - - ">="
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
125
- description: It parses the haml template and checks the validity of the language file
139
+ description: Verification of translation of Ruby source and Haml template
126
140
  email:
127
141
  - holy.shared.design@gmail.com
128
142
  executables: []
@@ -140,20 +154,21 @@ files:
140
154
  - README.md
141
155
  - Rakefile
142
156
  - i18n-checker.gemspec
143
- - lib/.DS_Store
144
157
  - lib/i18n_checker.rb
145
- - lib/i18n_checker/.DS_Store
158
+ - lib/i18n_checker/collectible.rb
146
159
  - lib/i18n_checker/detector/detected_result.rb
147
160
  - lib/i18n_checker/detector/locale_text_not_found.rb
148
161
  - lib/i18n_checker/detector/locale_text_result.rb
149
- - lib/i18n_checker/haml.rb
150
- - lib/i18n_checker/haml/locale_text.rb
151
- - lib/i18n_checker/haml/locale_text_collector.rb
152
- - lib/i18n_checker/haml/locale_texts.rb
153
162
  - lib/i18n_checker/locale.rb
163
+ - lib/i18n_checker/locale/collector.rb
164
+ - lib/i18n_checker/locale/collector/haml.rb
165
+ - lib/i18n_checker/locale/collector/ruby.rb
166
+ - lib/i18n_checker/locale/file.rb
167
+ - lib/i18n_checker/locale/files.rb
154
168
  - lib/i18n_checker/locale/key_path.rb
155
- - lib/i18n_checker/locale/locale_file.rb
156
- - lib/i18n_checker/locale/locale_files.rb
169
+ - lib/i18n_checker/locale/text.rb
170
+ - lib/i18n_checker/locale/text_processor.rb
171
+ - lib/i18n_checker/locale/texts.rb
157
172
  - lib/i18n_checker/locale_text_not_found_checker.rb
158
173
  - lib/i18n_checker/rake_task.rb
159
174
  - lib/i18n_checker/reporter.rb
@@ -180,8 +195,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
195
  version: '0'
181
196
  requirements: []
182
197
  rubyforge_project:
183
- rubygems_version: 2.5.2
198
+ rubygems_version: 2.6.8
184
199
  signing_key:
185
200
  specification_version: 4
186
- summary: i18n checker for haml
201
+ summary: Verification of translation of Ruby source and Haml template
187
202
  test_files: []
data/lib/.DS_Store DELETED
Binary file
Binary file
@@ -1,13 +0,0 @@
1
- module I18nChecker
2
- module Haml
3
- class LocaleText
4
- attr_reader :file, :text, :line
5
-
6
- def initialize(file:, text:, line:)
7
- @file = file
8
- @text = text
9
- @line = line
10
- end
11
- end
12
- end
13
- end
@@ -1,44 +0,0 @@
1
- require 'haml_parser'
2
- require 'haml_parser/parser'
3
-
4
- module I18nChecker
5
- module Haml
6
- class LocaleTextCollector
7
- def collect(template_file)
8
- template = read_template_file(template_file)
9
- parser = HamlParser::Parser.new(filename: template_file)
10
- LocaleTexts.new(collect_locale_texts(parser.call(template)))
11
- end
12
-
13
- private
14
-
15
- def read_template_file(template_file)
16
- File.open(template_file, &:read)
17
- end
18
-
19
- def collect_locale_texts(ast)
20
- locale_texts = []
21
- locale_texts << collect_locale_text(ast)
22
- return unless ast.respond_to?(:children)
23
- locale_texts << ast.children.map { |child| collect_locale_texts(child) }
24
- locale_texts.flatten.compact
25
- end
26
-
27
- def collect_locale_text(ast)
28
- return locale_text_from_script(ast) if ast.kind_of?(HamlParser::Ast::Script)
29
- return unless ast.respond_to?(:oneline_child)
30
- return unless ast.oneline_child.kind_of?(HamlParser::Ast::Script)
31
- locale_text_from_script(ast.oneline_child)
32
- end
33
-
34
- def locale_text_from_script(script_node)
35
- return unless translate_script = script_node.script.match(/^t\(\'+(.+)\'+\)$/)
36
- I18nChecker::Haml::LocaleText.new(
37
- file: script_node.filename,
38
- line: script_node.lineno,
39
- text: translate_script[1]
40
- )
41
- end
42
- end
43
- end
44
- end
@@ -1,21 +0,0 @@
1
- require "i18n_checker/haml/locale_text"
2
- require "i18n_checker/haml/locale_texts"
3
- require "i18n_checker/haml/locale_text_collector"
4
-
5
- module I18nChecker
6
- module Haml
7
- module Methods
8
- def collect_locale_text_of(files)
9
- collector = I18nChecker::Haml::LocaleTextCollector.new
10
- locale_texts = I18nChecker::Haml::LocaleTexts.new
11
-
12
- haml_files = files.delete_if { |file| File.directory?(file) }.to_a
13
- haml_files.each do |haml_file|
14
- locale_texts.concat(collector.collect(haml_file))
15
- end
16
- locale_texts
17
- end
18
- end
19
- extend Methods
20
- end
21
- end