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.
- 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
|
+
|