Permalink
Browse files

Basic functionality spec'd

  • Loading branch information...
1 parent 3e57a78 commit 9ed4dac713f07e9ff666f157d2feae41905cc6a8 @winton committed Jul 7, 2012
Showing with 271 additions and 31 deletions.
  1. +1 −0 .gitignore
  2. +17 −9 README.md
  3. +7 −1 Rakefile
  4. +22 −19 ext/change/change.c
  5. +2 −1 ext/change/makefile
  6. +111 −1 lib/change.rb
  7. +111 −0 spec/change_spec.rb
  8. 0 spec/fixture/modify.txt
  9. 0 spec/fixture/remove.txt
View
@@ -6,4 +6,5 @@ ext/change/change
ext/change/*.o
Gemfile.lock
pkg
+spec/tmp
tmp
View
@@ -15,6 +15,8 @@ Changed?
@change = Change.new("/absolute/path")
@change.d?("relative/path")
+ @change.d # { :add => [], :mod => [], :rem => [] }
+ @change.d(true) # reload
`Change` uses a YAML file stored in the root directory called `.change.yml` to maintain state.
@@ -32,16 +34,22 @@ Dependencies
Sometimes you want to say "if this file changes, then these other files should change as well".
+To group dependencies, `Change` uses the concept of a "session":
+
@change = Change.new("/absolute/path")
- @change.d(:some_id) # set id (must be symbol)
- @change.d("relative/path") # add dependency
- @change.d(null) # unset id (must run this when finished)
+ @change.s(:some_id) # start session with id
+ @change.r("relative/path") # record dependency
+ @change.s(nil) # stop session
+
+If you use `@change.d?` within a session, it will return true if any dependencies have changed. `Change` recalls the dependencies from the last session to achieve this.
-If you use `@change.d?` after an id is set, it will return true if any dependencies from the last execution changed.
+ @change.s(:some_id)
+ @change.d?('relative/path')
+ # returns true if any dependencies have changed
+ # dependencies are recalled from LAST :some_id session
-Tell `Change` to record which files were modified while an id was set during last execution:
+Recall files that were modified during the last execution of this session:
- @change = Change.new("/absolute/path", :record => true)
- @change.d(:some_id) # set id (must be symbol)
- @change.d # modified files from last session (hash)
- @change.d(null) # unset id (must run this when finished)
+ @change.s(:some_id) do
+ @change.d_ # returns: { :add => [], :mod => [], :rem => [] }
+ end
View
@@ -1 +1,7 @@
-require 'bundler/gem_tasks'
+require 'bundler/gem_tasks'
+require 'spec/rake/spectask'
+
+Spec::Rake::SpecTask.new(:spec) do |t|
+ t.spec_files = FileList['spec/**/*_spec.rb']
+end
+task :default => :spec
View
@@ -5,34 +5,37 @@
#include "murmur3.h"
int main(int argc, char **argv) {
- uint32_t hash[4]; /* Output for the hash */
- uint32_t seed = 42; /* Seed value for hash */
- char *buffer;
+ uint32_t hash[4]; /* Output for the hash */
+ uint32_t seed = 42; /* Seed value for hash */
- if (argc != 2) {
- printf("usage: %s \"path\"\n", argv[0]);
- exit(1);
- }
+ int x;
- FILE *fh = fopen(argv[1], "rb");
+ for (x = 1; x < argc; x++) {
+ FILE *fh = fopen(argv[x], "rb");
- if (fh != NULL) {
- fseek(fh, 0L, SEEK_END);
- long s = ftell(fh);
- buffer = malloc(s);
-
- rewind(fh);
+ if (fh != NULL) {
+ fseek(fh, 0L, SEEK_END);
+ long s = ftell(fh);
- if (buffer != NULL) {
- fread(buffer, s, 1, fh);
- fclose(fh); fh = NULL;
-
+ char *buffer = malloc(s + 1);
+ buffer[s] = '\0';
+
+ rewind(fh);
+
+ if (s != 0) {
+ fread(buffer, s, 1, fh);
+ fclose(fh);
+ fh = NULL;
+ }
+
MurmurHash3_x86_32(buffer, strlen(buffer), seed, hash);
printf("%08u\n", hash[0]);
free(buffer);
+
+ if (fh != NULL)
+ fclose(fh);
}
- if (fh != NULL) fclose(fh);
}
return 0;
View
@@ -4,4 +4,5 @@ all: change
change: change.o murmur3.o
install:
rm *.o
- mv change ../../bin/murmur3
+ mv change ../../bin/murmur3
+ chmod +x ../../bin/murmur3
View
@@ -1,4 +1,114 @@
+require 'yaml'
+
$:.unshift File.dirname(__FILE__)
-module Change
+class Change
+
+ def initialize(root)
+ @root = File.expand_path(root)
+ end
+
+ def d?(path)
+ changed = @d.values.flatten
+ if @session && @last_deps && @last_deps.include?(path)
+ @last_deps.any? { |p| changed.include?(p) }
+ else
+ changed.include?(path)
+ end
+ end
+
+ def d(reload=false)
+ if @d && !reload
+ @d
+ else
+ paths = Dir.chdir(@root) { Dir["**/*"] }
+ add = paths - states.keys
+ rem = states.keys - paths
+ hashes = murmur_hashes(paths)
+
+ mod = paths.inject([]) do |array, path|
+ hash = hashes[path]
+ size = Dir.chdir(@root) { File.size(path) }
+
+ if states[path] && size != states[path][:size]
+ array << path
+ elsif states[path] && hash != states[path][:hash]
+ array << path
+ end
+
+ states[path] ||= {}
+ states[path][:hash] = hash
+ states[path][:size] = size
+
+ array
+ end
+
+ mod -= add
+ rem.each { |path| states.delete(path) }
+
+ write!
+
+ @d = { :add => add, :mod => mod, :rem => rem }
+ end
+ end
+
+ def d_
+ @last_session
+ end
+
+ def r(path)
+ if @session
+ deps[@session].push(path)
+ deps[@session].uniq!
+ write!
+ end
+ end
+
+ def s(id)
+ d(true)
+
+ if @session && @session != id
+ @last_session = sessions[@session] = d
+ write!
+ end
+
+ @session = id
+ @last_deps = @session ? deps[@session] : nil
+ deps[@session] = [] if @session
+ end
+
+ private
+
+ def data
+ @data ||= YAML.load(File.read("#{@root}/.change.yml")) rescue {}
+ end
+
+ def murmur_bin
+ @murmur ||= File.expand_path('../../bin/murmur3', __FILE__)
+ end
+
+ def murmur_hashes(paths)
+ hashes = Dir.chdir(@root) do
+ `#{murmur_bin} #{paths.collect { |m| "'#{m}'" }.join(' ')}`
+ end
+ Hash[paths.zip(hashes.split("\n"))]
+ end
+
+ def deps
+ data['deps'] ||= {}
+ end
+
+ def sessions
+ data['sessions'] ||= {}
+ end
+
+ def states
+ data['states'] ||= {}
+ end
+
+ def write!
+ File.open("#{@root}/.change.yml", 'w') do |f|
+ f.write(YAML.dump(data))
+ end
+ end
end
View
@@ -1,4 +1,115 @@
+require 'fileutils'
require 'spec_helper'
describe Change do
+
+ before :all do
+ @root = File.expand_path('../../', __FILE__)
+ FileUtils.rm_rf(@tmp = "#{@root}/spec/tmp")
+ FileUtils.cp_r("#{@root}/spec/fixture", @tmp)
+ @change = Change.new(@tmp)
+ end
+
+ describe :d do
+ it "should return all files as added" do
+ @change.d.should == {
+ :add => [ "modify.txt", "remove.txt" ],
+ :mod => [],
+ :rem => []
+ }
+ end
+
+ it "should cache last changes" do
+ @change.d.should == {
+ :add => [ "modify.txt", "remove.txt" ],
+ :mod => [],
+ :rem => []
+ }
+ end
+
+ it "should return no files modified" do
+ @change.d(true).should == {
+ :add => [],
+ :mod => [],
+ :rem => []
+ }
+ end
+
+ it "should return modified" do
+ File.open("#{@tmp}/modify.txt", 'w') { |f| f.write('!') }
+ @change.d(true).should == {
+ :add => [],
+ :mod => [ "modify.txt" ],
+ :rem => []
+ }
+ end
+
+ it "should return removed" do
+ FileUtils.rm("#{@tmp}/remove.txt")
+ @change.d(true).should == {
+ :add => [],
+ :mod => [],
+ :rem => [ "remove.txt" ]
+ }
+ end
+
+ it "should return added" do
+ File.open("#{@tmp}/add.txt", 'w') { |f| f.write('!') }
+ @change.d(true).should == {
+ :add => [ "add.txt" ],
+ :mod => [],
+ :rem => []
+ }
+ end
+
+ it "should save state" do
+ @change = Change.new(@tmp)
+ @change.d.should == {
+ :add => [],
+ :mod => [],
+ :rem => []
+ }
+ end
+ end
+
+ describe :d? do
+ it "should return state if present" do
+ File.open("#{@tmp}/modify.txt", 'w') { |f| f.write('!!') }
+ @change.d(true)
+ @change.d?('add.txt').should == false
+ @change.d?('modify.txt').should == true
+ end
+ end
+
+ describe :s do
+ before :all do
+ @change.s(:id)
+ @change.r('modify.txt')
+ @change.r('remove.txt')
+ File.open("#{@tmp}/add.txt", 'w') { |f| f.write('!') }
+ File.open("#{@tmp}/modify.txt", 'w') { |f| f.write('!!!') }
+ @change.s(nil)
+ end
+
+ it "should record dependencies" do
+ @change.send(:deps).should == { :id => [ "modify.txt", "remove.txt" ] }
+ end
+
+ it "should record files modified during session" do
+ @change.send(:sessions).should == {
+ :id => {
+ :add => [ "add.txt" ], :mod => [ "modify.txt" ], :rem => []
+ }
+ }
+ end
+
+ it "should return proper data within next session" do
+ File.open("#{@tmp}/modify.txt", 'w') { |f| f.write('!!!!') }
+ @change.s(:id)
+ @change.d?("remove.txt").should == true
+ @change.d.should == { :add => [], :mod => [ "modify.txt" ], :rem => [] }
+ @change.d_.should == { :add => [ "add.txt" ], :mod => [ "modify.txt" ], :rem => [] }
+ @change.s(nil)
+ end
+ end
end
No changes.
No changes.

0 comments on commit 9ed4dac

Please sign in to comment.