exegesis 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+