exegesis 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc +2 -0
- data/.yardopts +5 -0
- data/CHANGELOG +10 -0
- data/Gemfile +20 -0
- data/LICENSE +22 -0
- data/NOTES.md +214 -0
- data/README.md +44 -0
- data/Rakefile +11 -0
- data/TODO.md +63 -0
- data/bin/exegesis +15 -0
- data/exegesis.gemspec +23 -0
- data/lib/exegesis/base_directory.rb +29 -0
- data/lib/exegesis/directory.rb +33 -0
- data/lib/exegesis/file_searcher.rb +48 -0
- data/lib/exegesis/file_system_entity.rb +52 -0
- data/lib/exegesis/flyweight.rb +130 -0
- data/lib/exegesis/registerable.rb +58 -0
- data/lib/exegesis/source_file.rb +45 -0
- data/lib/exegesis/version.rb +4 -0
- data/lib/exegesis.rb +48 -0
- data/spec/fake_project/AUTHORS +1 -0
- data/spec/fake_project/Rakefile +14 -0
- data/spec/fake_project/config.yml +6 -0
- data/spec/fake_project/src/grafton.c +25 -0
- data/spec/fake_project/src/node.c +23 -0
- data/spec/fake_project/src/node.h +19 -0
- data/spec/fake_project/test/example2_test.c +29 -0
- data/spec/fake_project/test/example_test.c +12 -0
- data/spec/fake_project/test/test_helper.h +19 -0
- data/spec/helpers/delegates.rb +49 -0
- data/spec/helpers/set_helpers.rb +27 -0
- data/spec/helpers/the.rb +15 -0
- data/spec/helpers/they.rb +15 -0
- data/spec/helpers/topic.rb +82 -0
- data/spec/integration/flyweight_registerable_spec.rb +78 -0
- data/spec/integration/visitor_spec.rb +86 -0
- data/spec/integration_spec_helper.rb +1 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/unit/base_directory_spec.rb +44 -0
- data/spec/unit/directory_spec.rb +44 -0
- data/spec/unit/file_searcher_spec.rb +82 -0
- data/spec/unit/flyweight_spec.rb +137 -0
- data/spec/unit/helpers_spec.rb +3 -0
- data/spec/unit/source_file_spec.rb +127 -0
- data/spec/unit_spec_helper.rb +1 -0
- metadata +158 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
# class Flyweight
|
2
|
+
#
|
3
|
+
# Responsibilities:
|
4
|
+
# Provide a registry for an arbitrary number of instances which need to be
|
5
|
+
# accessed as a single instance.
|
6
|
+
#
|
7
|
+
# Notes:
|
8
|
+
#
|
9
|
+
# Collaborators:
|
10
|
+
# SourceFile
|
11
|
+
# Directory
|
12
|
+
# Project
|
13
|
+
class Flyweight
|
14
|
+
extend Forwardable
|
15
|
+
|
16
|
+
# Create an empty Flyweight with the given key-processing proc.
|
17
|
+
#
|
18
|
+
# @param key_processor [Proc] a proc which turns an instance into it's key.
|
19
|
+
def initialize(&key_processor)
|
20
|
+
clear!
|
21
|
+
|
22
|
+
if block_given?
|
23
|
+
@key_processor = key_processor
|
24
|
+
else
|
25
|
+
@key_processor = proc { |id| id }
|
26
|
+
end
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
# Register an instance in the flyweight. Throw an error if the key is already
|
32
|
+
# used.
|
33
|
+
#
|
34
|
+
# @param instance [Object] the instance to register in the flyweight
|
35
|
+
# @return [Object] the instance given
|
36
|
+
# @raise [AlreadyRegisteredError] when trying to register the same key twice
|
37
|
+
def register!(instance)
|
38
|
+
raise AlreadyRegisteredError if has_key?(instance)
|
39
|
+
register(instance)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Register an instance in the flyweight.
|
43
|
+
#
|
44
|
+
# @param instance [Object] the instance to register in the flyweight
|
45
|
+
# @return [Object] the instance given
|
46
|
+
def register(instance)
|
47
|
+
key = build_key(instance)
|
48
|
+
key_registry[key] = instance
|
49
|
+
end
|
50
|
+
|
51
|
+
# Remove an instance (by key or instance proper) from the flyweight. Throw an
|
52
|
+
# error if no such instance exists
|
53
|
+
#
|
54
|
+
# @param key_or_instance [Object] Either the key under which an instance is
|
55
|
+
# registered, or the instance itself.
|
56
|
+
# @return [Object] the instance deleted from the flyweight
|
57
|
+
# @raise [NoFlyweightEntryError] when trying to delete a key that isn't
|
58
|
+
# present in the flyweight
|
59
|
+
def unregister!(key_or_instance)
|
60
|
+
raise NoEntryError unless has_key?(key_or_instance)
|
61
|
+
unregister(key_or_instance)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Remove an instance from the flyweight
|
65
|
+
#
|
66
|
+
# @param key_or_instance [Object] Either the key under which an instance is
|
67
|
+
# registered, or the instance itself.
|
68
|
+
# @return [Object] the instance deleted from the flyweight
|
69
|
+
def unregister(key_or_instance)
|
70
|
+
proxy_across_keytypes(:delete, key_or_instance)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Whether the flyweight has the given key or instance registered
|
74
|
+
#
|
75
|
+
# @param key_or_instance [Object] Either the key under which an instance is
|
76
|
+
# registered, or the instance itself.
|
77
|
+
# @return [Boolean] True if the Flyweight has the key or instance, false
|
78
|
+
# otherwise
|
79
|
+
def has_key?(key_or_instance)
|
80
|
+
proxy_across_keytypes(:has_key?, key_or_instance)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Access the entry under the given key or instance
|
84
|
+
#
|
85
|
+
# NB. If, given an instance that would generate a matching key to an already
|
86
|
+
# registered instance, but perhaps with different data, you'll get back a
|
87
|
+
# reference to the _registered_ instance.
|
88
|
+
#
|
89
|
+
# @param key_or_instance [Object] The key or instance to access.
|
90
|
+
# @return [Object, NilClass] the instance desired, or nil if it doesn't exist
|
91
|
+
def [](key_or_instance)
|
92
|
+
proxy_across_keytypes(:[], key_or_instance)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Clear the Flyweight of all entries.
|
96
|
+
def clear!
|
97
|
+
@key_registry = {}
|
98
|
+
self
|
99
|
+
end
|
100
|
+
alias reset! clear!
|
101
|
+
|
102
|
+
def inspect
|
103
|
+
"Flyweight<#{object_id}, items=#{@key_registry.keys.count}>"
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Build a key from an instance via the key_processor
|
109
|
+
def build_key(instance)
|
110
|
+
@key_processor.call(instance)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Proxy a method, aim to use the raw key first, but if that doesn't work, use
|
114
|
+
# the build_key helper and assume you were given an instance.
|
115
|
+
#
|
116
|
+
# NB. This is a bit dirty, but since we don't know what types we're given, we
|
117
|
+
# can't distinguish between key's and instance's without forcing the user to
|
118
|
+
# specifically tell us.
|
119
|
+
def proxy_across_keytypes(method, key)
|
120
|
+
key_registry.send(method, key) || key_registry.send(method, build_key(key))
|
121
|
+
end
|
122
|
+
|
123
|
+
attr_accessor :key_registry
|
124
|
+
|
125
|
+
# An error raised when trying to use an already used key
|
126
|
+
class AlreadyRegisteredError < ArgumentError ; end
|
127
|
+
# An error raised when trying to remove an unused key
|
128
|
+
class NoEntryError < ArgumentError ; end
|
129
|
+
end
|
130
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Registerable
|
2
|
+
def self.included(base)
|
3
|
+
base.private_class_method :new, :allocate
|
4
|
+
base.instance_eval do
|
5
|
+
extend ClassMethods
|
6
|
+
include InstanceMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def create(*args)
|
12
|
+
retrieve(*args) || build(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def retrieve(*args)
|
16
|
+
path = build_path(*args.take(2))
|
17
|
+
registry[path] if registry.has_key?(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def build(*args)
|
21
|
+
new(*args).tap do |instance|
|
22
|
+
registry.register! instance
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def registry
|
27
|
+
@flyweight ||= Flyweight.new do |dir|
|
28
|
+
if dir.respond_to? :path
|
29
|
+
dir.path
|
30
|
+
else
|
31
|
+
dir
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear_registry!
|
37
|
+
registry.clear!
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_path(*args)
|
41
|
+
parent, name = *args
|
42
|
+
return parent if name.nil?
|
43
|
+
File.join(parent.path, name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module InstanceMethods
|
48
|
+
def path
|
49
|
+
build_path(parent, name)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def build_path(parent, child)
|
55
|
+
self.class.build_path(parent, child)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# class SourceFile
|
2
|
+
# HAS_A Extension
|
3
|
+
# HAS_A Path
|
4
|
+
# HAS_A Base Name
|
5
|
+
# HAS_MANY SourceFiles (Dependencies)
|
6
|
+
#
|
7
|
+
# Responsibilities:
|
8
|
+
# Represents a sourcefile on disk, providing access to it's file-system
|
9
|
+
# related information as well as internal information based on the language.
|
10
|
+
#
|
11
|
+
# Notes:
|
12
|
+
# This will likely work w/ an inheritance heirarchy for each programming
|
13
|
+
# language. Mostly it will be one-level deep, but in the case where a
|
14
|
+
# subsequent language forms a superset/subset, deeper inheritance may occur
|
15
|
+
# (similarly we might have a module for shared subsets, etc).
|
16
|
+
#
|
17
|
+
# Collaborators:
|
18
|
+
# SourceFile
|
19
|
+
# SourceFileFactory -- to build the appropriate subclass based on file extenstion
|
20
|
+
class SourceFile
|
21
|
+
include FileSystemEntity
|
22
|
+
|
23
|
+
def content
|
24
|
+
File.read(path)
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :dependencies
|
28
|
+
def depends_on(file)
|
29
|
+
raise InvalidDependency unless file.is_a?(SourceFile)
|
30
|
+
@dependencies << file
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def initialize(parent, name)
|
36
|
+
raise ArgumentError, "parent must be a directory" unless parent.is_a?(Directory)
|
37
|
+
|
38
|
+
@ext = File.extname(name)
|
39
|
+
@name = name
|
40
|
+
@parent = parent
|
41
|
+
@dependencies = []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class InvalidDependency < StandardError ; end
|
data/lib/exegesis.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
################################################################################
|
3
|
+
#load order matters here. These are sorted into load-levels Level-0 files must
|
4
|
+
#be loaded before Level-1's, etc. try to keep this sorted. any file after the
|
5
|
+
#line is 'load-whenever'
|
6
|
+
################################################################################
|
7
|
+
|
8
|
+
#Level 0
|
9
|
+
require 'rake'
|
10
|
+
require 'rake/ext/string'
|
11
|
+
|
12
|
+
#Level 1
|
13
|
+
require 'exegesis/flyweight'
|
14
|
+
|
15
|
+
#Level 2
|
16
|
+
require 'exegesis/registerable'
|
17
|
+
|
18
|
+
#Level 3
|
19
|
+
require 'exegesis/file_system_entity'
|
20
|
+
require 'exegesis/file_searcher'
|
21
|
+
|
22
|
+
#Level 4
|
23
|
+
require 'exegesis/directory'
|
24
|
+
require 'exegesis/source_file'
|
25
|
+
|
26
|
+
#Level 5
|
27
|
+
require 'exegesis/base_directory'
|
28
|
+
|
29
|
+
#-------------------------------------------------------------------------------
|
30
|
+
#load whenever
|
31
|
+
require 'exegesis/version'
|
32
|
+
|
33
|
+
################################################################################
|
34
|
+
|
35
|
+
|
36
|
+
# Exegesis is a tool for automating many parts of the development of C projects.
|
37
|
+
# Following a convention-over-configuration model.
|
38
|
+
#
|
39
|
+
# It provides tools to:
|
40
|
+
#
|
41
|
+
# * Create skeleton projects
|
42
|
+
# * Build
|
43
|
+
# * Testing
|
44
|
+
# * Packaging
|
45
|
+
#
|
46
|
+
# Though C is presently the only supported language, it is the aim of Exegesis to
|
47
|
+
# support tooling for other compiled languages.
|
48
|
+
module Exegesis; end
|
@@ -0,0 +1 @@
|
|
1
|
+
Joe Fredette
|
@@ -0,0 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* =====================================================================================
|
3
|
+
*
|
4
|
+
* Filename: grafton.c
|
5
|
+
*
|
6
|
+
* Description:
|
7
|
+
*
|
8
|
+
* Version: 1.0
|
9
|
+
* Created: 08/27/2012 09:09:38
|
10
|
+
* Revision: none
|
11
|
+
* Compiler: gcc
|
12
|
+
*
|
13
|
+
* Author: YOUR NAME (),
|
14
|
+
* Organization:
|
15
|
+
*
|
16
|
+
* =====================================================================================
|
17
|
+
*/
|
18
|
+
#include <stdlib.h>
|
19
|
+
#include <stdio.h>
|
20
|
+
#include "node.h"
|
21
|
+
|
22
|
+
int main() {
|
23
|
+
printf("hello world\n");
|
24
|
+
return foo();
|
25
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
/*
|
2
|
+
* =====================================================================================
|
3
|
+
*
|
4
|
+
* Filename: node.c
|
5
|
+
*
|
6
|
+
* Description:
|
7
|
+
*
|
8
|
+
* Version: 1.0
|
9
|
+
* Created: 08/27/2012 23:01:12
|
10
|
+
* Revision: none
|
11
|
+
* Compiler: gcc
|
12
|
+
*
|
13
|
+
* Author: YOUR NAME (),
|
14
|
+
* Organization:
|
15
|
+
*
|
16
|
+
* =====================================================================================
|
17
|
+
*/
|
18
|
+
#include <stdlib.h>
|
19
|
+
#include "node.h"
|
20
|
+
|
21
|
+
int foo() {
|
22
|
+
return 1;
|
23
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
/*
|
2
|
+
* =====================================================================================
|
3
|
+
*
|
4
|
+
* Filename: node.h
|
5
|
+
*
|
6
|
+
* Description:
|
7
|
+
*
|
8
|
+
* Version: 1.0
|
9
|
+
* Created: 08/27/2012 23:00:57
|
10
|
+
* Revision: none
|
11
|
+
* Compiler: gcc
|
12
|
+
*
|
13
|
+
* Author: YOUR NAME (),
|
14
|
+
* Organization:
|
15
|
+
*
|
16
|
+
* =====================================================================================
|
17
|
+
*/
|
18
|
+
|
19
|
+
int foo();
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/*
|
2
|
+
* =====================================================================================
|
3
|
+
*
|
4
|
+
* Filename: example.c
|
5
|
+
*
|
6
|
+
* Description:
|
7
|
+
*
|
8
|
+
* Version: 1.0
|
9
|
+
* Created: 08/27/2012 07:38:40
|
10
|
+
* Revision: none
|
11
|
+
* Compiler: gcc
|
12
|
+
*
|
13
|
+
* Author: YOUR NAME (),
|
14
|
+
* Organization:
|
15
|
+
*
|
16
|
+
* =====================================================================================
|
17
|
+
*/
|
18
|
+
/*#include "test_helper.h"*/
|
19
|
+
|
20
|
+
START_TEST (test_foo) {
|
21
|
+
fail_unless(1);
|
22
|
+
|
23
|
+
} END_TEST
|
24
|
+
|
25
|
+
START_TEST (test_bar) {
|
26
|
+
fail_unless(0);
|
27
|
+
|
28
|
+
} END_TEST
|
29
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
/*
|
2
|
+
* =====================================================================================
|
3
|
+
*
|
4
|
+
* Filename: test_helper.h
|
5
|
+
*
|
6
|
+
* Description: Helper file for writing tests
|
7
|
+
*
|
8
|
+
* Created: 08/25/2012 09:38:50
|
9
|
+
* Compiler: clang
|
10
|
+
*
|
11
|
+
* Author: Joe Fredette (jfredett@gmail.com),
|
12
|
+
*
|
13
|
+
* =====================================================================================
|
14
|
+
*/
|
15
|
+
|
16
|
+
#include <stdlib.h>
|
17
|
+
#include <check.h>
|
18
|
+
|
19
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rspec/expectations'
|
2
|
+
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
module RSpec
|
6
|
+
module Exegesis
|
7
|
+
module DSL
|
8
|
+
module Matchers
|
9
|
+
class DelegateMatcher
|
10
|
+
def initialize(message_to_receive)
|
11
|
+
@message_to_receive = message_to_receive
|
12
|
+
end
|
13
|
+
|
14
|
+
def to(target)
|
15
|
+
@target_object = target
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def when_calling(messag)
|
20
|
+
@message_to_send
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def matches?(target)
|
25
|
+
target_object.should_receive(message_to_receive)
|
26
|
+
target.send(message_to_send)
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def failure_message_for_should
|
31
|
+
"expected to receive ##{message_to_receive} on #{target_object} when sending ##{message_to_send}, but didn't"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
attr_reader :message_to_receive, :target_object
|
36
|
+
|
37
|
+
# default to delegating the message directly
|
38
|
+
def message_to_send
|
39
|
+
@message_to_send || @message_to_receive
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def delegate(message)
|
44
|
+
DelegateMatcher.new(message)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rspec/expectations'
|
2
|
+
|
3
|
+
RSpec::Matchers.define :be_a_superset_of do |expected|
|
4
|
+
match do |obj|
|
5
|
+
expected.should be_a_subset_of obj
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec::Matchers.define :be_a_subset_of do |expected|
|
10
|
+
match do |obj|
|
11
|
+
obj.each do |e|
|
12
|
+
expected.include?(e)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec::Matchers.define :contain do |expected|
|
18
|
+
match do |obj|
|
19
|
+
obj.include?(expected)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec::Matchers.define :exclude do |expected|
|
24
|
+
match do |obj|
|
25
|
+
not obj.include?(expected)
|
26
|
+
end
|
27
|
+
end
|
data/spec/helpers/the.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
__END__
|
2
|
+
module RSpec
|
3
|
+
module Exegesis
|
4
|
+
module DSL
|
5
|
+
module Macros
|
6
|
+
def topic(obj, &block)
|
7
|
+
if obj
|
8
|
+
__context_stack.reset!
|
9
|
+
__context_stack.push_context(obj) do
|
10
|
+
describe '' do
|
11
|
+
subject { __context_stack.current_subject }
|
12
|
+
block.call
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def consider(sym, &block)
|
19
|
+
raise "consider must be nested in a topic block" unless __context_stack_available?
|
20
|
+
|
21
|
+
__context_stack.push_context(sym) do
|
22
|
+
context "##{sym}" do
|
23
|
+
binding.pry
|
24
|
+
subject { __context_stack.current_subject }
|
25
|
+
block.call
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def __context_stack
|
31
|
+
@@__context_stack ||= ContextStack.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def __context_stack_available?
|
35
|
+
not (@@__context_stack.nil? || @@__context_stack.empty?)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'forwardable'
|
40
|
+
class ContextStack
|
41
|
+
extend Forwardable
|
42
|
+
def initialize
|
43
|
+
reset!
|
44
|
+
end
|
45
|
+
|
46
|
+
delegate [:<<, :push, :pop, :empty?] => :@stack
|
47
|
+
|
48
|
+
def push_context(ctx)
|
49
|
+
push(ctx)
|
50
|
+
yield
|
51
|
+
pop
|
52
|
+
end
|
53
|
+
|
54
|
+
def reset!
|
55
|
+
@stack = []
|
56
|
+
end
|
57
|
+
|
58
|
+
def current_subject
|
59
|
+
@stack.drop(1).reduce(send(@stack.first)) do |a,e|
|
60
|
+
a = a.send(e)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
__END__
|
69
|
+
|
70
|
+
topic :foo do | describe :foo do
|
71
|
+
| subject { send :foo }
|
72
|
+
| @topic = :foo
|
73
|
+
|
|
74
|
+
consider :bar do | context "##{:bar}" do
|
75
|
+
| subject { @topic.send(:bar) }
|
76
|
+
| @topic = :bar
|
77
|
+
|
|
78
|
+
it { ... } | it { ... }
|
79
|
+
|
|
80
|
+
end | end
|
81
|
+
end | end
|
82
|
+
|