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