exegesis 0.0.2

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.
Files changed (47) hide show
  1. data/.gitignore +5 -0
  2. data/.rvmrc +2 -0
  3. data/.yardopts +5 -0
  4. data/CHANGELOG +10 -0
  5. data/Gemfile +20 -0
  6. data/LICENSE +22 -0
  7. data/NOTES.md +214 -0
  8. data/README.md +44 -0
  9. data/Rakefile +11 -0
  10. data/TODO.md +63 -0
  11. data/bin/exegesis +15 -0
  12. data/exegesis.gemspec +23 -0
  13. data/lib/exegesis/base_directory.rb +29 -0
  14. data/lib/exegesis/directory.rb +33 -0
  15. data/lib/exegesis/file_searcher.rb +48 -0
  16. data/lib/exegesis/file_system_entity.rb +52 -0
  17. data/lib/exegesis/flyweight.rb +130 -0
  18. data/lib/exegesis/registerable.rb +58 -0
  19. data/lib/exegesis/source_file.rb +45 -0
  20. data/lib/exegesis/version.rb +4 -0
  21. data/lib/exegesis.rb +48 -0
  22. data/spec/fake_project/AUTHORS +1 -0
  23. data/spec/fake_project/Rakefile +14 -0
  24. data/spec/fake_project/config.yml +6 -0
  25. data/spec/fake_project/src/grafton.c +25 -0
  26. data/spec/fake_project/src/node.c +23 -0
  27. data/spec/fake_project/src/node.h +19 -0
  28. data/spec/fake_project/test/example2_test.c +29 -0
  29. data/spec/fake_project/test/example_test.c +12 -0
  30. data/spec/fake_project/test/test_helper.h +19 -0
  31. data/spec/helpers/delegates.rb +49 -0
  32. data/spec/helpers/set_helpers.rb +27 -0
  33. data/spec/helpers/the.rb +15 -0
  34. data/spec/helpers/they.rb +15 -0
  35. data/spec/helpers/topic.rb +82 -0
  36. data/spec/integration/flyweight_registerable_spec.rb +78 -0
  37. data/spec/integration/visitor_spec.rb +86 -0
  38. data/spec/integration_spec_helper.rb +1 -0
  39. data/spec/spec_helper.rb +36 -0
  40. data/spec/unit/base_directory_spec.rb +44 -0
  41. data/spec/unit/directory_spec.rb +44 -0
  42. data/spec/unit/file_searcher_spec.rb +82 -0
  43. data/spec/unit/flyweight_spec.rb +137 -0
  44. data/spec/unit/helpers_spec.rb +3 -0
  45. data/spec/unit/source_file_spec.rb +127 -0
  46. data/spec/unit_spec_helper.rb +1 -0
  47. metadata +158 -0
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ pkg
2
+ .rbx
3
+ .yardoc
4
+ doc/
5
+ Gemfile.lock
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ RVM_STRING="rvm use rbx-head@exegesis --create --verbose"
2
+ [ -e './.rvmrc_local' ] && source './.rvmrc_local' || $RVM_STRING
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --no-private
2
+ --embed-mixins
3
+ -
4
+ README.md
5
+ LICENSE
data/CHANGELOG ADDED
@@ -0,0 +1,10 @@
1
+
2
+ * 0.0.2
3
+ Large amount of functionality in place, including BaseDirectories,
4
+ Directories, SourceFiles, and Visitors. Internal Architectural stuff also
5
+ working (objects are stored in flyweights so there are no duplicates and
6
+ everything stays synced.
7
+
8
+ * 0.0.1
9
+
10
+ Initial Release
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in exegesis.gemspec
4
+ gemspec
5
+
6
+ group :documentation do
7
+ gem 'rdoc'
8
+ gem 'yard'
9
+ gem 'kramdown'
10
+ end
11
+
12
+ group :development do
13
+ gem 'pry'
14
+ end
15
+
16
+ group :test do
17
+ gem 'rspec'
18
+ gem 'rspec-spies', '2.1.0'
19
+ gem 'bahia'
20
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Joe Fredette
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/NOTES.md ADDED
@@ -0,0 +1,214 @@
1
+ # Basic structure
2
+
3
+ ## Implementation
4
+
5
+ * SourceFiles and Directories should be members of a Flyweight, and creation
6
+ should try to do a lookup first. Perhaps Celluloid can be used to do this,
7
+ with directories potentially acting as supervisors.
8
+
9
+ ## Architecture
10
+
11
+ A suite of raketasks (potentially hidden behind a 'exegesis' script. Adhereing
12
+ to the following model:
13
+
14
+ class Project
15
+ HAS_MANY BaseDirectories #as a series of methods in a subclass
16
+ HAS_MANY SourceFiles
17
+
18
+ Responsibilities:
19
+ this represents the root directory of a project. It will be subclassed
20
+ into project-style specific class. Eg, a RailsProject IS_A Project, it's
21
+ BaseDirectories are, perhaps, `spec`, `app`, `lib`, and `config`. A
22
+ GemProject might have `lib`, `spec`, and `bin`, a C project might have
23
+ `test`, `src`, `vendor`, `obj`, etc.
24
+
25
+ It also provides methods like `#build_skeleton!(dir)` which take a
26
+ location in the filesystem and build the structure specified by the
27
+ project.
28
+
29
+ It also serves as a point from which we may start a `#visit` to all of
30
+ it's subdirs and files.
31
+
32
+ NB:
33
+ It might be interesting to have Projects be able to embed other projects.
34
+ This could be useful for representing, eg, tests in a C project as a
35
+ subproject with it's own file-structure and stuff. So that a CProject is
36
+ now a pair of two underlying `CSourceProject`, each of which has a `src`,
37
+ `obj`, and `vendor`, which in the one project represents the testing
38
+ setup, and the other represents the application setup. Possible problem
39
+ might be trying to get dependencies to work across projects.
40
+
41
+ ----------------------------------------------------------------------------
42
+
43
+ class BaseDirectory IS_A Directory
44
+ HAS_A Root
45
+ HAS_MANY Directories
46
+ HAS_MANY SourceFiles
47
+
48
+ Responsibilities:
49
+ Expose properties of the root directory, Spawn the initial set of
50
+ directories which will recursively find all the files in the project
51
+
52
+ The class will also wrap some of the SourceFile classmacros, like #join and
53
+ #expand_path and stuff. Think along the lines of Rails.root and the like.
54
+
55
+ Collaborators:
56
+ Directory
57
+ SourceFile
58
+
59
+ ----------------------------------------------------------------------------
60
+
61
+ class Directory
62
+ HAS_MANY SourceFiles
63
+ HAS_MANY Directories (Children)
64
+ HAS_A Directory (Parent)
65
+
66
+ Responsibilities:
67
+ Finds all the files and directories at a given level of the project
68
+ structure, allows for recursive informing of children -- ideally in
69
+ parallel.
70
+
71
+ Collaborators:
72
+ SourceFile
73
+ Directory
74
+
75
+ ----------------------------------------------------------------------------
76
+
77
+ class SourceFile
78
+ HAS_A Extension
79
+ HAS_A Path
80
+ HAS_A Base Name
81
+ HAS_MANY SourceFiles (Dependencies)
82
+
83
+ Responsibilities:
84
+ Represents a sourcefile on disk, providing access to it's file-system
85
+ related information as well as internal information based on the language.
86
+
87
+ Notes:
88
+ This will likely work w/ an inheritance heirarchy for each programming
89
+ language. Mostly it will be one-level deep, but in the case where a
90
+ subsequent language forms a superset/subset, deeper inheritance may occur
91
+ (similarly we might have a module for shared subsets, etc).
92
+
93
+ Collaborators:
94
+ SourceFile
95
+ SourceFileFactory -- to build the appropriate subclass based on file extenstion
96
+
97
+ ----------------------------------------------------------------------------
98
+
99
+ class SourceFileFactory
100
+ #snip
101
+
102
+ Responsibilities:
103
+ Provide an extensible way to build SourceFile instances appropriate to the
104
+ language the source of a given file is in.
105
+
106
+ Collaborators:
107
+ LanguageIdentifier
108
+ SourceFile
109
+
110
+ ----------------------------------------------------------------------------
111
+
112
+ class LanguageIdentifier
113
+ #snip
114
+
115
+ Responsibilities:
116
+ A command object to identify the language of a given sourcefile
117
+
118
+ Collaborators:
119
+ SourceFileFactory
120
+
121
+ ----------------------------------------------------------------------------
122
+
123
+ class FileSearcher
124
+
125
+ Responsibilities:
126
+ Encapsulates an API for looking through a single directory of files,
127
+ sorting them into directories/files/whatever, and providing those path
128
+ lists on demand
129
+
130
+ NB:
131
+ The aim is to isolate the minimum API with this class, so that alternative
132
+ source backends could potentially be written, eg -- a backend for
133
+ distributed sourcetrees, or w/ files in Riak or S3 or whereever
134
+
135
+ Collaborators:
136
+ Used By:
137
+ Project, Directory
138
+ Uses:
139
+ Some system-level class like Dir, FileList, or Find
140
+
141
+
142
+ # Tools
143
+
144
+ Would be nice to model these guys as actors, especially so that we and make use
145
+ of that nice async stuff for a little map-reduce-y action. Celluloid? (DCell? :])
146
+
147
+ # #visit
148
+
149
+ This is an interface which would take some kind of Visitor class, which defines
150
+ up to `n` methods (on for each model type). This method would iterate over all
151
+ of it's contents (directories/files/whatever) and apply to each the appropriate
152
+ method from the visitor, with the instance as an argument. The visitor can
153
+ potentially recursively call #visit on directories, which should also support
154
+ this interface
155
+
156
+ # DSL
157
+
158
+ w/ the restructure to use the `Project->[BaseDirs]` change, it would be useful to
159
+ have a DSL to specify a project in a simpler way than manually defining a new
160
+ subclass of `Project`. To wit, I rather like:
161
+
162
+ project 'rails' do
163
+ src_dir 'app'
164
+ src_dir 'lib'
165
+ src_dir 'db'
166
+
167
+ test_dir 'spec'
168
+ test_dir 'features'
169
+
170
+ config_dir 'config'
171
+
172
+ file 'Gemfile'
173
+ file 'Rakefile'
174
+
175
+ #etc
176
+ end
177
+
178
+ This would define an appropriate `Project` subclass, called 'RailsProject', with methods pointing to
179
+ the various subdirs.
180
+
181
+ # Features
182
+
183
+ ## Core Features (Must Haves)
184
+
185
+ * Automatic Building w/ dependency discovery
186
+ - manual configuration of dependencies also allowed
187
+ - parallel builds by default
188
+ * Automatic Test Suite w/ boilerplate autogenerated
189
+ - Integrates with specific tools to do this, eg, Check or CUnit, etc.
190
+ * Handles at least C, C++, and maybe Go and Rust
191
+
192
+ ## Added Features (Should Haves)
193
+
194
+ * Automatic, file-system-event based recompiles.
195
+ * Integration with other Unit testing systems
196
+
197
+ ## Neat Features (Could Haves)
198
+
199
+ * Statistics on code base (LOC, Complexity metrics, etc)
200
+ * Plugin API
201
+
202
+ ## Far out Features (Want-to Haves)
203
+
204
+ * Language-aware search indexing (with ElasticSearch backend?)
205
+
206
+ ## Random features
207
+
208
+ * Automatically generate git helper stuff
209
+ - spawn a repo
210
+ - build the project skeleton
211
+ - set up gitignore
212
+ - set up hooks (run tests, etc)
213
+ - Set up standard docs (README, AUTHORS, LICENSE, etc)
214
+
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ ** Exegesis is a pre-alpha tool. It is not recommended for public consumption **
2
+
3
+ It'll be 0.1 or better when it's ready.
4
+
5
+ # Exegesis
6
+
7
+ Exegesis is a tool for automating many parts of the development of C projects.
8
+ Following a convention-over-configuration model.
9
+
10
+ It provides tools to:
11
+
12
+ * Create skeleton projects
13
+ * Build
14
+ * Testing
15
+ * Packaging
16
+
17
+ Though C is presently the only supported language, it is the aim of Exegesis to
18
+ support tooling for other compiled languages.
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'exegesis'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install exegesis
33
+
34
+ ## Usage
35
+
36
+ TODO: Write usage instructions here
37
+
38
+ ## Contributing
39
+
40
+ 1. Fork it
41
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
42
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
43
+ 4. Push to the branch (`git push origin my-new-feature`)
44
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/setup'
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc 'run specs'
7
+ RSpec::Core::RakeTask.new do |task|
8
+ task.rspec_opts = ["-c", "-f progress"]
9
+ end
10
+
11
+ task :default => :spec
data/TODO.md ADDED
@@ -0,0 +1,63 @@
1
+ 1. Projects
2
+ 1.1. Composite projects
3
+
4
+ 2. Dependency Heirarchy (#visit should respect it)
5
+ 2.1. Explicit-ordering
6
+ That is, an explicit sort based on a list of key/list pairs, the key being
7
+ the object that depends on the others being processed. We should have a
8
+ generic visitor class that can consume such a list and respect that
9
+ dependency structure when doing it's visitation.
10
+
11
+ 2.2. Driven by a real example
12
+ Write a nontrivial (but simple) app in C/Go/Typescript/whatever, try to
13
+ use Exegesis to do dependency discovery, set up rake-tasks for
14
+ compilation, etc.
15
+
16
+ 3. FileUtil methods
17
+ 3.1 Directories should respond to stuff like `#mkdir`, `#ls`, etc. we should
18
+ have `#cd` return a BaseDirectory for the given path, perhaps. SourceFiles
19
+ should respond to `#stat`, etc. Basically load up the filesystem as methods.
20
+
21
+ 4. Break up FileSystemEntity into several modules
22
+ it's a bit of a hodge-podge at the moment
23
+
24
+ 5. Search
25
+ Need to be able to search for files in-project and on-path (the latter being
26
+ for referencing system-librarys). Exegesis, in the long run, is going to
27
+ calculate at least the following tasks:
28
+
29
+ * <rake task> ~ <equivalent make-related task>
30
+
31
+ * configure ~ ./configure
32
+ * default ~ make
33
+ * all ~ make all
34
+ * install ~ make install
35
+ * clean ~ make clean
36
+ * distclean ~ make distclean
37
+
38
+ In particular, configure needs to be able to calculate external dependencies
39
+ and use pkg-config to check for them (in the cases where pkg-config makes
40
+ sense). Ideally with minimal effort it could automatially scour something like
41
+ CPAN for C projects (CCAN?) or w/e and find where you have to go to install
42
+ those dependencies.
43
+
44
+ In any case, we need the ability to reference files in-context, so that an
45
+ `#include 'foo/bar'` in the src directory knows to reference `src/foo/bar`
46
+ and not `obj/foo/bar` in it's dependency setup. It would be good to also
47
+ implement `find` in terms of exegesis.
48
+
49
+ 6. Celluloid
50
+ It would be very powerful to be able to have each file act independently,
51
+ especially in the case of `#visit`, because we can asynchronously spawn
52
+ visitors to do their work, and as long as the visitor can manage concurrent
53
+ access, the directories/files it visits have no shared state with anything,
54
+ and thus should be pretty easy to fit into a multi-threaded, link-free
55
+ framework like exegesis.
56
+
57
+ ------ Things worth doing later ------
58
+
59
+ 1. Model symlinks
60
+ 2. build a visitor that outputs a `dot` graph of the folder heirarchy. bonus
61
+ points for outputting a DAG when symlinks are built
62
+ 3. Performance testing.
63
+
data/bin/exegesis ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake'
4
+ require 'exegesis'
5
+
6
+ Rake.application.init('exegesis')
7
+
8
+ task :version do
9
+ puts "Exegesis, version #{Exegesis::VERSION}"
10
+ end
11
+
12
+ Rake.application.top_level
13
+
14
+
15
+
data/exegesis.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/exegesis/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Joe Fredette"]
6
+ gem.email = ["jfredett@gmail.com"]
7
+ gem.description = %q{A tool for automatically compiling C projects}
8
+ gem.summary = %q{Exegesis is a tool for automating many parts of the development of C projects.
9
+ Following a convention-over-configuration model, it provides tools to create
10
+ skeleton projects, as well as providing tools to automate building, testing, and
11
+ packaging.}
12
+ gem.homepage = ""
13
+
14
+ gem.add_dependency "rake"
15
+ gem.add_dependency "celluloid"
16
+
17
+ gem.files = `git ls-files`.split($\)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.name = "exegesis"
21
+ gem.require_paths = ["lib"]
22
+ gem.version = Exegesis::VERSION
23
+ end
@@ -0,0 +1,29 @@
1
+ # class BaseDirectory IS_A Directory
2
+ # HAS_A Root
3
+ # HAS_MANY Directories
4
+ # HAS_MANY SourceFiles (files)
5
+ #
6
+ # Responsibilities:
7
+ # Expose properties of the root directory, Spawn the initial set of
8
+ # directories which will recursively find all the files in the project
9
+ #
10
+ # The class will also wrap some of the SourceFile macros, like #join and
11
+ # #expand_path and stuff. Think along the lines of Rails.root and the like.
12
+ #
13
+ # Collaborators:
14
+ # [Directory]
15
+ # [SourceFile]
16
+ class BaseDirectory < Directory
17
+ # @param root [String] the root path of the project
18
+ # @param searcher [FileSearcher] an object used to search for files in a given
19
+ # directory
20
+ def initialize(root, searcher = FileSearcher)
21
+ super(nil, root, searcher)
22
+ end
23
+
24
+ # The path to the root of the project
25
+ def path
26
+ name
27
+ end
28
+ alias root path
29
+ end
@@ -0,0 +1,33 @@
1
+ # class Directory
2
+ # HAS_MANY SourceFiles
3
+ # HAS_MANY Directories (Children)
4
+ # HAS_A Directory (Parent)
5
+ #
6
+ # Responsibilities:
7
+ # Finds all the files and directories at a given level of the project
8
+ # structure, allows for recursive informing of children -- ideally in
9
+ # parallel.
10
+ #
11
+ # Collaborators:
12
+ # SourceFile
13
+ # Directory
14
+ class Directory
15
+ include FileSystemEntity
16
+
17
+ delegate [:directories, :files] => :searcher
18
+ alias children directories
19
+
20
+ private
21
+
22
+ attr_reader :searcher
23
+
24
+ # @param parent [Directory] the root directory or project of the project
25
+ # @param name [String] the name of the directory
26
+ # @param searcher [FileSearcher] an object used to search for files in a given
27
+ # directory
28
+ def initialize(parent, name, searcher = FileSearcher)
29
+ @parent = parent
30
+ @name = name
31
+ @searcher = searcher.new(self)
32
+ end
33
+ end
@@ -0,0 +1,48 @@
1
+ # class FileSearcher
2
+ #
3
+ # Responsibilities:
4
+ # Encapsulates an API for looking through a single directory of files,
5
+ # sorting them into directories/files/whatever, and providing those path
6
+ # lists on demand
7
+ #
8
+ # NB:
9
+ # The aim is to isolate the minimum API with this class, so that alternative
10
+ # source backends could potentially be written, eg -- a backend for
11
+ # distributed sourcetrees, or w/ files in Riak or S3 or whereever
12
+ #
13
+ # Collaborators:
14
+ # Used By:
15
+ # Project, Directory
16
+ # Uses:
17
+ # Some system-level class like Dir, FileList, or Find
18
+ class FileSearcher
19
+ # Create a new FileSearcher on the given path
20
+ #
21
+ # @param parent [Directory] the parent directory to search downward from
22
+ def initialize(parent)
23
+ @parent = parent
24
+ end
25
+
26
+ #All of the directories in the given path
27
+ def directories
28
+ content.select { |s| File.directory?(s) }.map { |s| Directory.create(parent, File.basename(s)) }
29
+ end
30
+
31
+ #All of the files in the given path
32
+ def files
33
+ content.select { |s| File.file?(s) }.map { |s| SourceFile.create(parent, File.basename(s)) }
34
+ end
35
+
36
+ #All of the content from the given path
37
+ def content
38
+ Dir[File.join(parent.path, '*')]
39
+ end
40
+
41
+ def inspect
42
+ "FileSearcher(#{parent.path.inspect})"
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :parent
48
+ end
@@ -0,0 +1,52 @@
1
+ # A collection of shared methods for Directories and SourceFiles
2
+ module FileSystemEntity
3
+ def self.included(base)
4
+ base.send(:include , Registerable)
5
+ base.send(:extend , Forwardable)
6
+ base.send(:include , Methods)
7
+ end
8
+
9
+ module Methods
10
+ def path
11
+ File.join(parent.path, name)
12
+ end
13
+
14
+ def inspect
15
+ "#{self.class.inspect}(#{path.inspect})"
16
+ end
17
+
18
+ attr_reader :parent, :name
19
+ alias container parent
20
+
21
+ def basename
22
+ File.basename(name, ext)
23
+ end
24
+
25
+ def ext
26
+ @ext || ""
27
+ end
28
+ alias extension ext
29
+
30
+
31
+ def visit(visitor)
32
+ visitor.on_enter if visitor.respond_to? :on_enter
33
+
34
+ visitor.call(self.class, self)
35
+
36
+ if respond_to?(:directories)
37
+ directories.each do |dir|
38
+ dir.visit(visitor)
39
+ end
40
+ end
41
+
42
+ if respond_to?(:files)
43
+ files.each do |file|
44
+ file.visit(visitor)
45
+ end
46
+ end
47
+
48
+ visitor.on_exit if visitor.respond_to? :on_exit
49
+ end
50
+ end
51
+ end
52
+