rsanheim-beholder 0.5.5

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2009 Chad Humphries
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,65 @@
1
+ h1. Beholder
2
+
3
+ An ancient beholder that watches your treasure, and deals with thiefs.
4
+
5
+ h2. What does it do?
6
+
7
+ Think autotest, but powered by fseventd.
8
+
9
+ h2. Requirements
10
+
11
+ # OSX 10.5 or higher
12
+ # RubyCocoa
13
+ # fsevents gem
14
+
15
+
16
+
17
+ The default treasure map:
18
+
19
+ map_for(:default_dungeon) do |wizard|
20
+
21
+ wizard.keep_a_watchful_eye_for 'app', 'config', 'lib', 'examples'
22
+
23
+ wizard.prepare_spell_for /\/app\/(.*)\.rb/ do |spell_component|
24
+ ["examples/#{spell_component[1]}.rb"]
25
+ end
26
+
27
+ wizard.prepare_spell_for /\/lib\/(.*)\.rb/ do |spell_component|
28
+ ["examples/lib/#{spell_component[1]}_example.rb"]
29
+ end
30
+
31
+ wizard.prepare_spell_for /\/examples\/(.*)_example\.rb/ do |spell_component|
32
+ ["examples/#{spell_component[1]}_example.rb"]
33
+ end
34
+
35
+ wizard.prepare_spell_for /\/examples\/example_helper\.rb/ do |spell_component|
36
+ Dir["examples/**/*_example.rb"]
37
+ end
38
+
39
+ wizard.prepare_spell_for /\/config/ do
40
+ Dir["examples/**/*_example.rb"]
41
+ end
42
+
43
+ end
44
+
45
+
46
+ In your own treasure map (stored as treasure_map.rb, .treasure_map.rb, or config/treasure_map.rb) you could do:
47
+
48
+ map_for(:beholders_lair) do |wizard|
49
+
50
+ # Clear all watched paths => wizard.paths_to_watch.clear
51
+ # Add these paths to the paths to watch
52
+ wizard.keep_a_watchful_eye_for 'coverage'
53
+
54
+ # Forget all other treasure maps loaded
55
+ # wizard.clear_maps
56
+
57
+ # Add your own rules
58
+ # wizard.prepare_spell_for /\/foobar/ do
59
+ # Dir["examples/foobar/*_example.rb"]
60
+ # end
61
+
62
+ # You could set the list of all examples to be run after pressing ctrl-c once
63
+ # it defaults to any files in examples, spec, and test
64
+ wizard.all_examples = Dir['your/path/**/*_here.rb']
65
+ end
data/Rakefile ADDED
@@ -0,0 +1,66 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+ require 'micronaut/rake_task'
6
+
7
+ GEM = "beholder"
8
+ GEM_VERSION = "0.5.5"
9
+ AUTHOR = "Chad Humphries"
10
+ EMAIL = "chad@spicycode.com"
11
+ HOMEPAGE = "http://github.com/spicycode/beholder"
12
+ SUMMARY = "An ancient beholder that watches your treasure, and deals with thiefs"
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = GEM
16
+ s.version = GEM_VERSION
17
+ s.platform = Gem::Platform::RUBY
18
+ s.has_rdoc = true
19
+ s.extra_rdoc_files = ["README.textile", "LICENSE", 'TODO']
20
+ s.summary = SUMMARY
21
+ s.description = s.summary
22
+ s.author = AUTHOR
23
+ s.email = EMAIL
24
+ s.homepage = HOMEPAGE
25
+ s.add_dependency "fsevents"
26
+ s.bindir = 'bin'
27
+ s.default_executable = 'beholder'
28
+ s.executables = ["beholder"]
29
+ s.require_path = 'lib'
30
+ s.autorequire = GEM
31
+ s.files = %w(LICENSE README.textile Rakefile TODO) + Dir.glob("{lib,examples}/**/*")
32
+ end
33
+
34
+ Rake::GemPackageTask.new(spec) do |pkg|
35
+ pkg.gem_spec = spec
36
+ end
37
+
38
+ desc "install the gem locally"
39
+ task :install => [:package] do
40
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
41
+ end
42
+
43
+ desc "create a gemspec file"
44
+ task :make_gemspec do
45
+ File.open("#{GEM}.gemspec", "w") do |file|
46
+ file.puts spec.to_ruby
47
+ end
48
+ end
49
+
50
+ desc "Run all micronaut examples"
51
+ Micronaut::RakeTask.new :examples do |t|
52
+ t.pattern = "examples/**/*_example.rb"
53
+ end
54
+
55
+ namespace :examples do
56
+
57
+ desc "Run all micronaut examples using rcov"
58
+ Micronaut::RakeTask.new :coverage do |t|
59
+ t.pattern = "examples/**/*_example.rb"
60
+ t.rcov = true
61
+ t.rcov_opts = "--exclude \"examples/*,gems/*,db/*,/Library/Frameworks/*,/Library/Ruby/*,config/*\" --text-summary --sort coverage --no-validator-links"
62
+ end
63
+
64
+ end
65
+
66
+ task :default => 'examples:coverage'
data/TODO ADDED
@@ -0,0 +1 @@
1
+ TODO:
data/bin/beholder ADDED
@@ -0,0 +1,4 @@
1
+ #! /usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
3
+ require 'beholder'
4
+ Beholder.run
@@ -0,0 +1,17 @@
1
+ lib_path = File.expand_path(File.dirname(__FILE__) + "/../lib")
2
+ $LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path)
3
+
4
+ require 'beholder'
5
+ require 'rubygems'
6
+ require 'micronaut'
7
+ gem :rr, '=0.7.0'
8
+
9
+ def not_in_editor?
10
+ ['TM_MODE', 'EMACS', 'VIM'].all? { |k| !ENV.has_key?(k) }
11
+ end
12
+
13
+ Micronaut.configure do |c|
14
+ c.formatter = :documentation
15
+ c.mock_with :rr
16
+ c.color_enabled = not_in_editor?
17
+ end
@@ -0,0 +1,84 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../example_helper")
2
+
3
+ describe Beholder do
4
+
5
+ describe "when run is called" do
6
+
7
+ it "should create a new beholder" do
8
+ beholder = stub(Beholder.new) { prepare; start }
9
+ mock(Beholder).new { beholder }
10
+
11
+ Beholder.run
12
+ end
13
+
14
+ it "should prepare" do
15
+ beholder = Beholder.new
16
+ stub(beholder).start
17
+ mock(beholder).prepare
18
+ stub(Beholder).new { beholder }
19
+
20
+ Beholder.run
21
+ end
22
+
23
+ it "should start" do
24
+ beholder = Beholder.new
25
+ mock(beholder).start
26
+ stub(beholder).prepare
27
+ stub(Beholder).new { beholder }
28
+
29
+ Beholder.run
30
+ end
31
+
32
+ end
33
+
34
+ describe "when it notices file(s) changed" do
35
+
36
+ it "should identify what was changed" do
37
+ treasures = ['pot_o_gold']
38
+ beholder = Beholder.new
39
+ mock(beholder).identify_stolen_treasure('pot_o_gold') { nil }
40
+ beholder.something_changed treasures
41
+ end
42
+
43
+ it "should run tests for the file that changed" do
44
+ treasures = ['pot_o_gold']
45
+ beholder = Beholder.new
46
+ stub(beholder).identify_stolen_treasure('pot_o_gold') { 'x marks the spot' }
47
+ mock(beholder).run_tests(['x marks the spot'])
48
+ beholder.something_changed treasures
49
+ end
50
+
51
+ end
52
+
53
+ describe "when blinking it's eye" do
54
+
55
+ it "should forget about any interlopers" do
56
+ beholder = Beholder.new
57
+ beholder.instance_variable_set("@sent_an_int", true) # Not so hot, but I'm tired
58
+
59
+ beholder.sent_an_int.should be_true
60
+ beholder.blink
61
+ beholder.sent_an_int.should be_false
62
+ end
63
+
64
+ end
65
+
66
+ describe "when shutting down" do
67
+
68
+ it "should stop watching for changes" do
69
+ beholder = Beholder.new
70
+ stub(beholder).exit
71
+ stub(beholder).watcher { mock!.shutdown }
72
+ beholder.shutdown
73
+ end
74
+
75
+ it "should exit" do
76
+ beholder = Beholder.new
77
+ stub(beholder).watcher { stub!.shutdown }
78
+ mock(beholder).exit
79
+ beholder.shutdown
80
+ end
81
+
82
+ end
83
+
84
+ end
data/lib/beholder.rb ADDED
@@ -0,0 +1,165 @@
1
+ require 'rubygems'
2
+ gem :fsevents
3
+ require 'fsevents'
4
+
5
+ class Beholder
6
+
7
+ attr_reader :paths_to_watch, :sent_an_int, :mappings, :working_directory, :verbose
8
+ attr_reader :watcher, :treasure_maps, :possible_map_locations, :all_examples
9
+
10
+ def initialize
11
+ @paths_to_watch, @all_examples = [], []
12
+ @mappings, @treasure_maps = {}, {}
13
+ @sent_an_int = false
14
+ @working_directory = Dir.pwd
15
+ @verbose = ARGV.include?("-v") || ARGV.include?("--verbose")
16
+ @possible_map_locations = ["#{@working_directory}/.treasure_map.rb", "#{@working_directory}/treasure_map.rb", "#{@working_directory}/config/treasure_map.rb"]
17
+ end
18
+
19
+ def self.run
20
+ beholder = new
21
+ beholder.read_all_maps
22
+ beholder.set_all_examples if beholder.all_examples.empty?
23
+ beholder.prepare
24
+ beholder.start
25
+ end
26
+
27
+ def read_all_maps
28
+ read_default_map
29
+
30
+ possible_map_locations.each do |map_location|
31
+ if File.exist?(map_location)
32
+ say "Found a treasure map at #{map_location}"
33
+ instance_eval(File.readlines(map_location).join("\n"))
34
+ return
35
+ end
36
+ end
37
+ end
38
+
39
+ def prepare
40
+ trap 'INT' do
41
+ if @sent_an_int then
42
+ puts " A second INT? Ok, I get the message. Shutting down now."
43
+ shutdown
44
+ else
45
+ puts " Did you just send me an INT? Ugh. I'll quit for real if you do it again."
46
+ @sent_an_int = true
47
+ Kernel.sleep 1.5
48
+ run_tests all_examples
49
+ end
50
+ end
51
+ end
52
+
53
+ def start
54
+ say("Watching the following locations:\n #{paths_to_watch.join(", ")}")
55
+ @watcher = FSEvents::Stream.watch(paths_to_watch) do |treasure_chest|
56
+ something_changed(treasure_chest.modified_files)
57
+ puts "\n\nWaiting to hear from the disk since #{Time.now}"
58
+ end
59
+ @watcher.run
60
+ end
61
+
62
+ def read_default_map
63
+ map_for(:default) do |m|
64
+
65
+ m.watch 'lib', 'examples'
66
+
67
+ m.add_mapping %r%examples/(.*)_example\.rb% do |match|
68
+ ["examples/#{match[1]}_example.rb"]
69
+ end
70
+
71
+ m.add_mapping %r%examples/example_helper\.rb% do |match|
72
+ Dir["examples/**/*_example.rb"]
73
+ end
74
+
75
+ m.add_mapping %r%lib/(.*)\.rb% do |file|
76
+ ["examples/lib/#{match[1]}_example.rb"]
77
+ end
78
+
79
+ end
80
+ end
81
+
82
+ def map_for(map_name)
83
+ @treasure_maps[map_name] ||= []
84
+ @current_map = @treasure_maps[map_name]
85
+ yield self if block_given?
86
+ ensure
87
+ @current_map = nil
88
+ end
89
+
90
+ def add_mapping(arcane_enemy, &spell)
91
+ @current_map << [arcane_enemy, spell]
92
+ end
93
+
94
+ def clear_maps
95
+ @treasure_maps = {}
96
+ end
97
+
98
+ def set_all_examples
99
+ if paths_to_watch.include?('examples')
100
+ @all_examples += Dir['examples/**/*_example.rb']
101
+ end
102
+
103
+ if paths_to_watch.include?('test')
104
+ @all_examples += Dir['test/**/*_test.rb']
105
+ end
106
+
107
+ if paths_to_watch.include?('spec')
108
+ @all_examples += Dir['spec/**/*_spec.rb']
109
+ end
110
+ end
111
+
112
+ def watch(*paths)
113
+ @paths_to_watch.concat(paths)
114
+ end
115
+
116
+ def blink
117
+ @sent_an_int = false
118
+ end
119
+
120
+ def shutdown
121
+ watcher.shutdown
122
+ exit
123
+ end
124
+
125
+ def identify_stolen_treasure(treasure)
126
+ treasure_maps.each do |name, treasure_locations|
127
+ treasure_locations.each do |stolen_by_enemy, spell|
128
+ if spell_components = treasure.match(stolen_by_enemy)
129
+ say "Found the stolen treasure using the #{name} map "
130
+ return spell.call(spell_components)
131
+ end
132
+ end
133
+ end
134
+
135
+ puts "Unknown file: #{treasure}"
136
+ return []
137
+ end
138
+
139
+ def run_tests(coordinates)
140
+ coordinates.flatten!
141
+
142
+ coordinates.reject! do |coordinate|
143
+ found_treasure = File.exist?(coordinate)
144
+ puts "#{coordinate} does not exist." unless found_treasure
145
+ end
146
+
147
+ return if coordinates.empty?
148
+
149
+ puts "\nRunning #{coordinates.join(', ').inspect}"
150
+ system "ruby #{coordinates.join(' ')}"
151
+ blink
152
+ end
153
+
154
+ def something_changed(treasure)
155
+ say "#{treasure} changed" unless treasure.empty?
156
+ coordinates = treasure.map { |t| identify_stolen_treasure(t) }.uniq.compact
157
+ run_tests coordinates
158
+ end
159
+
160
+ private
161
+ def say(msg)
162
+ puts msg if verbose
163
+ end
164
+
165
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rsanheim-beholder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.5
5
+ platform: ruby
6
+ authors:
7
+ - Chad Humphries
8
+ autorequire: beholder
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-27 00:00:00 -08:00
13
+ default_executable: beholder
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: fsevents
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: An ancient beholder that watches your treasure, and deals with thiefs
26
+ email: chad@spicycode.com
27
+ executables:
28
+ - beholder
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.textile
33
+ - LICENSE
34
+ - TODO
35
+ files:
36
+ - LICENSE
37
+ - README.textile
38
+ - Rakefile
39
+ - TODO
40
+ - lib/beholder.rb
41
+ - examples/example_helper.rb
42
+ - examples/lib
43
+ - examples/lib/beholder_example.rb
44
+ - bin/beholder
45
+ has_rdoc: true
46
+ homepage: http://github.com/spicycode/beholder
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.2.0
68
+ signing_key:
69
+ specification_version: 2
70
+ summary: An ancient beholder that watches your treasure, and deals with thiefs
71
+ test_files: []
72
+