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
@@ -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
@@ -0,0 +1,4 @@
1
+ module Exegesis
2
+ #@private
3
+ VERSION = "0.0.2"
4
+ 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,14 @@
1
+ $:.unshift './'
2
+ require 'build/all'
3
+
4
+ build do
5
+ binaries do
6
+ bin 'grafton'
7
+ end
8
+
9
+ dependencies
10
+
11
+ packages do
12
+ package "check"
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ ---
2
+ CC: clang
3
+ SRC: src/
4
+ OBJ: obj/
5
+ BIN: bin/
6
+ TEST: test/
@@ -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,12 @@
1
+ /*#include "test_helper.h"*/
2
+
3
+ START_TEST (test_foo) {
4
+ fail_unless(1);
5
+
6
+ } END_TEST
7
+
8
+ START_TEST (test_bar) {
9
+ fail_unless(0);
10
+
11
+ } END_TEST
12
+
@@ -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
@@ -0,0 +1,15 @@
1
+ module RSpec
2
+ module Exegesis
3
+ module DSL
4
+ module Macros
5
+ def the(sym, &block)
6
+ context sym do
7
+ subject { if sym.is_a? Class then sym else send sym end }
8
+ it &block
9
+ end
10
+ end
11
+ alias the_class the
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module RSpec
2
+ module Exegesis
3
+ module DSL
4
+ module Macros
5
+ def they_all(&block)
6
+ example do
7
+ subject.map do
8
+ instance_eval &block
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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
+