fssm-0.2.10/0000755000004100000410000000000012153365457012605 5ustar www-datawww-datafssm-0.2.10/.travis.yml0000644000004100000410000000012412153365457014713 0ustar www-datawww-databranches: only: - cleanup rvm: - jruby # - 1.8.7 # - 1.9.2 # - rbx-head fssm-0.2.10/ext/0000755000004100000410000000000012153365457013405 5ustar www-datawww-datafssm-0.2.10/ext/rakefile.rb0000644000004100000410000000065212153365457015517 0ustar www-datawww-data# -*- encoding: utf-8 -*- $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__))) require 'rubygems/dependency_installer' require 'fssm' # semi-elegant solution or hack? *shrug* task :default do name, version = FSSM::Support.optimal_backend_dependency if name and version installer = Gem::DependencyInstaller.new({:domain => :both, :env_shebang => true}) installer.install name, version end end fssm-0.2.10/LICENSE0000644000004100000410000000204112153365457013607 0ustar www-datawww-dataCopyright (c) 2011 Travis Tilley Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fssm-0.2.10/profile/0000755000004100000410000000000012153365457014245 5ustar www-datawww-datafssm-0.2.10/profile/prof.html0000644000004100000410000016705412153365457016116 0ustar www-datawww-data

Profile Report

Thread ID Total Time
109440 91.978859

Thread 109440

%Total %Self Total Self Wait Child Calls Name Line
100.00% 1.61% 91.98 1.48 0.00 90.50 1 Global#[No method] 12
    1.06 0.13 0.00 0.94 5000/5000 FSSM::Tree::NodeInsertion#unset 22
    89.44 1.82 0.00 87.62 165000/165000 FSSM::Tree::Cache#set 29
    89.44 1.82 0.00 87.62 165000/165000 Global#[No method] 29
97.24% 1.98% 89.44 1.82 0.00 87.62 165000 FSSM::Tree::Cache#set 145
    7.78 1.35 0.00 6.43 165000/500000 <Class::FSSM::Pathname>#for 148
    9.28 1.45 0.00 7.83 165000/165000 FSSM::Pathname#expand_path 148
    70.56 1.48 0.00 69.08 165000/165000 FSSM::Tree::NodeInsertion#set 149
    70.56 1.48 0.00 69.08 165000/165000 FSSM::Tree::Cache#set 149
76.71% 1.61% 70.56 1.48 0.00 69.08 165000 FSSM::Tree::NodeInsertion#set 66
    58.98 0.86 0.00 58.12 165000/165000 FSSM::Tree::NodeInsertion#descendant! 67
    10.10 2.91 0.00 7.19 165000/165000 FSSM::Tree::Node#from_path 68
    58.98 0.86 0.00 58.12 165000/165000 FSSM::Tree::NodeInsertion#set 67
64.12% 0.93% 58.98 0.86 0.00 58.12 165000 FSSM::Tree::NodeInsertion#descendant! 82
    58.12 17.58 0.00 40.54 165000/170000 FSSM::Tree::NodeInsertion#recurse 83
    58.12 17.58 0.00 40.54 165000/170000 FSSM::Tree::NodeInsertion#descendant! 83
    0.11 0.06 0.00 0.05 5000/170000 FSSM::Tree::NodeInsertion#descendant 79
63.31% 19.17% 58.23 17.64 0.00 40.60 170000 FSSM::Tree::NodeInsertion#recurse 86
    8.99 6.67 0.00 2.32 1215000/1215000 FSSM::Tree::NodeBase#child! 92
    2.32 2.32 0.00 0.00 1385000/1390000 Array#empty? 90
    27.21 2.10 0.00 25.11 170000/175000 FSSM::Tree::NodeInsertion#key_segments 87
    2.09 2.09 0.00 0.00 1215000/1215000 Array#shift 91
    0.74 0.06 0.00 0.68 5000/175000 FSSM::Tree::NodeInsertion#unset 49
    27.21 2.10 0.00 25.11 170000/175000 FSSM::Tree::NodeInsertion#recurse 87
30.39% 2.35% 27.95 2.16 0.00 25.79 175000 FSSM::Tree::NodeInsertion#key_segments 73
    1.62 1.13 0.00 0.49 170000/500000 <Class::FSSM::Pathname>#for 75
    23.84 4.05 0.00 19.80 170000/170000 FSSM::Pathname#segments 75
    0.32 0.32 0.00 0.00 175000/675000 Kernel#is_a? 74
    23.84 4.05 0.00 19.80 170000/170000 FSSM::Tree::NodeInsertion#key_segments 75
25.92% 4.40% 23.84 4.05 0.00 19.80 170000 FSSM::Pathname#segments 43
    0.29 0.29 0.00 0.00 170000/170000 String#empty? 48
    0.42 0.42 0.00 0.00 165000/165000 Array#unshift 48
    13.85 6.04 0.00 7.81 170000/170000 FSSM::Pathname#set_prefix_and_names 45
    1.46 0.86 0.00 0.59 170000/340000 Kernel#dup 46
    3.78 2.04 0.00 1.73 170000/340000 Array#delete 47
    13.85 6.04 0.00 7.81 170000/170000 FSSM::Pathname#segments 45
15.06% 6.57% 13.85 6.04 0.00 7.81 170000 FSSM::Pathname#set_prefix_and_names 144
    0.37 0.37 0.00 0.00 165000/165000 MatchData#[] 150
    1.35 1.35 0.00 0.00 170000/170000 String#split 154
    0.36 0.36 0.00 0.00 170000/170000 Array#+ 154
    0.31 0.31 0.00 0.00 165000/165000 MatchData#post_match 151
    0.66 0.66 0.00 0.00 165000/165000 FSSM::Pathname#to_s 150
    0.68 0.68 0.00 0.00 170000/170000 Regexp#match 149
    0.29 0.29 0.00 0.00 170000/170000 Array#compact! 157
    3.78 2.05 0.00 1.73 170000/340000 Array#delete 158
    1.36 1.07 0.00 0.29 165000/500000 FSSM::Tree::Node#from_path 130
    7.78 1.35 0.00 6.43 165000/500000 FSSM::Tree::Cache#set 148
    1.62 1.13 0.00 0.49 170000/500000 FSSM::Tree::NodeInsertion#key_segments 75
11.70% 3.86% 10.76 3.55 0.00 7.21 500000 <Class::FSSM::Pathname>#for 21
    0.90 0.90 0.00 0.00 500000/675000 Kernel#is_a? 22
    6.32 0.92 0.00 5.39 170000/335038 Class#new 22
    10.10 2.91 0.00 7.19 165000/165000 FSSM::Tree::NodeInsertion#set 68
10.98% 3.16% 10.10 2.91 0.00 7.19 165000 FSSM::Tree::Node#from_path 129
    1.36 1.07 0.00 0.29 165000/500000 <Class::FSSM::Pathname>#for 130
    1.99 0.86 0.00 1.13 165000/165000 FSSM::Pathname#mtime 134
    1.79 0.87 0.00 0.92 165000/165000 FSSM::Pathname#symlink? 134
    2.05 0.86 0.00 1.19 165000/165000 FSSM::Pathname#ftype 131
    6.32 0.92 0.00 5.39 170000/335038 <Class::FSSM::Pathname>#for 22
    0.00 0.00 0.00 0.00 38/335038 FSSM::Tree::NodeBase#child! 14
    3.01 0.91 0.00 2.11 165000/335038 FSSM::Pathname#expand_path 247
10.14% 1.99% 9.33 1.83 0.00 7.50 335038 Class#new 0
    0.00 0.00 0.00 0.00 38/38 <Class::Object>#allocate 14
    0.59 0.59 0.00 0.00 335000/505000 <Class::String>#allocate 22
    0.00 0.00 0.00 0.00 38/38 FSSM::Tree::NodeBase#initialize 14
    6.91 3.04 0.00 3.87 335000/335000 FSSM::Pathname#initialize 22
    9.28 1.45 0.00 7.83 165000/165000 FSSM::Tree::Cache#set 148
10.09% 1.58% 9.28 1.45 0.00 7.83 165000 FSSM::Pathname#expand_path 247
    0.28 0.28 0.00 0.00 165000/165000 Kernel#class 247
    4.54 4.54 0.00 0.00 165000/165000 <Class::File>#expand_path 247
    3.01 0.91 0.00 2.11 165000/335038 Class#new 247
    8.99 6.67 0.00 2.32 1215000/1215000 FSSM::Tree::NodeInsertion#recurse 92
9.77% 7.25% 8.99 6.67 0.00 2.32 1215000 FSSM::Tree::NodeBase#child! 13
    0.00 0.00 0.00 0.00 38/38 Hash#[]= 14
    0.00 0.00 0.00 0.00 38/335038 Class#new 14
    2.32 2.32 0.00 0.00 1215000/1215000 Hash#[] 14
    3.78 2.04 0.00 1.73 170000/340000 FSSM::Pathname#segments 47
    3.78 2.05 0.00 1.73 170000/340000 FSSM::Pathname#set_prefix_and_names 158
8.22% 4.45% 7.56 4.09 0.00 3.47 340000 Array#delete 0
    3.47 3.47 0.00 0.00 2110000/2110000 String#== 158
    6.91 3.04 0.00 3.87 335000/335000 Class#new 22
7.51% 3.30% 6.91 3.04 0.00 3.87 335000 FSSM::Pathname#initialize 26
    0.28 0.28 0.00 0.00 170000/170000 Kernel#=~ 27
    3.59 1.19 0.00 2.39 335000/335000 String#initialize 31
    4.54 4.54 0.00 0.00 165000/165000 FSSM::Pathname#expand_path 247
4.94% 4.94% 4.54 4.54 0.00 0.00 165000 <Class::File>#expand_path 0
    3.59 1.19 0.00 2.39 335000/335000 FSSM::Pathname#initialize 31
3.90% 1.30% 3.59 1.19 0.00 2.39 335000 String#initialize 0
    2.39 0.84 0.00 1.55 170000/170000 Pathname#to_str 31
    3.47 3.47 0.00 0.00 2110000/2110000 Array#delete 158
3.77% 3.77% 3.47 3.47 0.00 0.00 2110000 String#== 0
    1.46 0.86 0.00 0.59 170000/340000 FSSM::Pathname#segments 46
    1.55 0.87 0.00 0.67 170000/340000 Pathname#to_str 242
3.27% 1.89% 3.00 1.74 0.00 1.27 340000 Kernel#dup 0
    0.30 0.30 0.00 0.00 170000/505000 <Class::String>#allocate 242
    0.29 0.29 0.00 0.00 170000/170000 <Class::Array>#allocate 46
    0.37 0.37 0.00 0.00 170000/170000 String#initialize_copy 242
    0.30 0.30 0.00 0.00 170000/170000 Array#initialize_copy 46
    2.39 0.84 0.00 1.55 170000/170000 String#initialize 31
2.60% 0.92% 2.39 0.84 0.00 1.55 170000 Pathname#to_str 241
    1.55 0.87 0.00 0.67 170000/340000 Kernel#dup 242
    0.01 0.01 0.00 0.00 5000/1390000 FSSM::Tree::NodeInsertion#unset 51
    2.32 2.32 0.00 0.00 1385000/1390000 FSSM::Tree::NodeInsertion#recurse 90
2.53% 2.53% 2.33 2.33 0.00 0.00 1390000 Array#empty? 0
    2.32 2.32 0.00 0.00 1215000/1215000 FSSM::Tree::NodeBase#child! 14
2.52% 2.52% 2.32 2.32 0.00 0.00 1215000 Hash#[] 0
    0.00 0.00 0.00 0.00 38/38 Hash#default 14
    2.09 2.09 0.00 0.00 1215000/1215000 FSSM::Tree::NodeInsertion#recurse 91
2.27% 2.27% 2.09 2.09 0.00 0.00 1215000 Array#shift 0
    2.05 0.86 0.00 1.19 165000/165000 FSSM::Tree::Node#from_path 131
2.23% 0.93% 2.05 0.86 0.00 1.19 165000 FSSM::Pathname#ftype 229
    1.19 1.19 0.00 0.00 165000/165000 <Class::File>#ftype 229
    1.99 0.86 0.00 1.13 165000/165000 FSSM::Tree::Node#from_path 134
2.17% 0.94% 1.99 0.86 0.00 1.13 165000 FSSM::Pathname#mtime 231
    1.13 1.13 0.00 0.00 165000/165000 <Class::File>#mtime 231
    1.79 0.87 0.00 0.92 165000/165000 FSSM::Tree::Node#from_path 134
1.94% 0.94% 1.79 0.87 0.00 0.92 165000 FSSM::Pathname#symlink? 216
    0.92 0.92 0.00 0.00 165000/165000 <Module::FileTest>#symlink? 216
    1.35 1.35 0.00 0.00 170000/170000 FSSM::Pathname#set_prefix_and_names 154
1.47% 1.47% 1.35 1.35 0.00 0.00 170000 String#split 0
    0.90 0.90 0.00 0.00 500000/675000 <Class::FSSM::Pathname>#for 22
    0.32 0.32 0.00 0.00 175000/675000 FSSM::Tree::NodeInsertion#key_segments 74
1.32% 1.32% 1.22 1.22 0.00 0.00 675000 Kernel#is_a? 0
    1.19 1.19 0.00 0.00 165000/165000 FSSM::Pathname#ftype 229
1.30% 1.30% 1.19 1.19 0.00 0.00 165000 <Class::File>#ftype 0
    1.13 1.13 0.00 0.00 165000/165000 FSSM::Pathname#mtime 231
1.23% 1.23% 1.13 1.13 0.00 0.00 165000 <Class::File>#mtime 0
    1.06 0.13 0.00 0.94 5000/5000 Global#[No method] 22
1.15% 0.14% 1.06 0.13 0.00 0.94 5000 FSSM::Tree::NodeInsertion#unset 48
    0.01 0.01 0.00 0.00 5000/5000 Array#pop 56
    0.14 0.03 0.00 0.11 5000/5000 FSSM::Tree::NodeInsertion#descendant 57
    0.04 0.03 0.00 0.01 5000/5000 FSSM::Tree::NodeBase#remove_child 61
    0.01 0.01 0.00 0.00 5000/1390000 Array#empty? 51
    0.74 0.06 0.00 0.68 5000/175000 FSSM::Tree::NodeInsertion#key_segments 49
    0.92 0.92 0.00 0.00 165000/165000 FSSM::Pathname#symlink? 216
1.00% 1.00% 0.92 0.92 0.00 0.00 165000 <Module::FileTest>#symlink? 0
fssm-0.2.10/profile/prof-cache.rb0000644000004100000410000000135512153365457016605 0ustar www-datawww-data$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'fssm' require 'rubygems' require 'ruby-prof' $test_path = FSSM::Pathname.new('..').expand_path $test_files = FSSM::Pathname.glob(File.join($test_path, '**', '*')) RubyProf.start RubyProf.pause cache = FSSM::Tree::Cache.new 5000.times do |num| iteration = "%-5d" % (num + 1) print "iteration #{iteration}" print '!' RubyProf.resume cache.unset($test_path) RubyProf.pause print '!' $test_files.each do |fn| print '.' RubyProf.resume cache.set(fn) RubyProf.pause end print "\n\n" end result = RubyProf.stop output = File.new('prof.html', 'w+') printer = RubyProf::GraphHtmlPrinter.new(result) printer.print(output, :min_percent => 1) fssm-0.2.10/profile/prof-fssm-pathname.html0000644000004100000410000007310212153365457020645 0ustar www-datawww-data

Profile Report

Thread ID Total Time
3696392 14.408241

Thread 3696392

%Total %Self Total Self Wait Child Calls Name Line
100.00% 0.00% 14.41 0.00 0.00 14.41 0 Global#[No method] 28
    3.06 0.56 0.00 2.50 90000/90000 Class#new 35
    11.35 1.98 0.00 9.37 90000/90000 FSSM::Pathname#to_a 36
    11.35 1.98 0.00 9.37 90000/90000 Global#[No method] 36
78.76% 13.76% 11.35 1.98 0.00 9.37 90000 FSSM::Pathname#to_a 47
    6.60 2.97 0.00 3.63 90000/90000 FSSM::Pathname#set_prefix_and_names 49
    0.94 0.49 0.00 0.45 90000/90000 Kernel#dup 50
    1.40 0.78 0.00 0.62 90000/180000 Array#delete 51
    0.16 0.16 0.00 0.00 90000/90000 String#empty? 52
    0.26 0.26 0.00 0.00 90000/90000 Array#unshift 52
    6.60 2.97 0.00 3.63 90000/90000 FSSM::Pathname#to_a 49
45.80% 20.58% 6.60 2.97 0.00 3.63 90000 FSSM::Pathname#set_prefix_and_names 212
    0.46 0.46 0.00 0.00 90000/90000 Regexp#match 217
    0.23 0.23 0.00 0.00 90000/90000 MatchData#[] 218
    0.40 0.40 0.00 0.00 90000/90000 FSSM::Pathname#to_s 218
    0.21 0.21 0.00 0.00 90000/90000 MatchData#post_match 219
    0.72 0.72 0.00 0.00 90000/90000 String#split 219
    0.17 0.17 0.00 0.00 90000/90000 Array#compact! 225
    1.44 0.80 0.00 0.64 90000/180000 Array#delete 226
    3.06 0.56 0.00 2.50 90000/90000 Global#[No method] 35
21.24% 3.92% 3.06 0.56 0.00 2.50 90000 Class#new 0
    0.18 0.18 0.00 0.00 90000/90000 <Class::String>#allocate 35
    2.31 1.18 0.00 1.13 90000/90000 FSSM::Pathname#initialize 35
    1.44 0.80 0.00 0.64 90000/180000 FSSM::Pathname#set_prefix_and_names 226
    1.40 0.78 0.00 0.62 90000/180000 FSSM::Pathname#to_a 51
19.77% 11.03% 2.85 1.59 0.00 1.26 180000 Array#delete 0
    1.26 1.26 0.00 0.00 720000/720000 String#== 51
    2.31 1.18 0.00 1.13 90000/90000 Class#new 35
16.06% 8.21% 2.31 1.18 0.00 1.13 90000 FSSM::Pathname#initialize 28
    0.94 0.94 0.00 0.00 90000/90000 FSSM::Pathname#dememo 33
    0.19 0.19 0.00 0.00 90000/90000 String#initialize 35
    1.26 1.26 0.00 0.00 720000/720000 Array#delete 51
8.74% 8.74% 1.26 1.26 0.00 0.00 720000 String#== 0
    0.94 0.49 0.00 0.45 90000/90000 FSSM::Pathname#to_a 50
6.55% 3.43% 0.94 0.49 0.00 0.45 90000 Kernel#dup 0
    0.20 0.20 0.00 0.00 90000/90000 <Class::Array>#allocate 50
    0.25 0.25 0.00 0.00 90000/90000 Array#initialize_copy 50
    0.94 0.94 0.00 0.00 90000/90000 FSSM::Pathname#initialize 33
6.50% 6.50% 0.94 0.94 0.00 0.00 90000 FSSM::Pathname#dememo 203
    0.72 0.72 0.00 0.00 90000/90000 FSSM::Pathname#set_prefix_and_names 219
4.97% 4.97% 0.72 0.72 0.00 0.00 90000 String#split 0
    0.46 0.46 0.00 0.00 90000/90000 FSSM::Pathname#set_prefix_and_names 217
3.21% 3.21% 0.46 0.46 0.00 0.00 90000 Regexp#match 0
    0.40 0.40 0.00 0.00 90000/90000 FSSM::Pathname#set_prefix_and_names 218
2.77% 2.77% 0.40 0.40 0.00 0.00 90000 FSSM::Pathname#to_s 42
    0.26 0.26 0.00 0.00 90000/90000 FSSM::Pathname#to_a 52
1.79% 1.79% 0.26 0.26 0.00 0.00 90000 Array#unshift 0
    0.25 0.25 0.00 0.00 90000/90000 Kernel#dup 50
1.72% 1.72% 0.25 0.25 0.00 0.00 90000 Array#initialize_copy 0
    0.23 0.23 0.00 0.00 90000/90000 FSSM::Pathname#set_prefix_and_names 218
1.61% 1.61% 0.23 0.23 0.00 0.00 90000 MatchData#[] 0
    0.21 0.21 0.00 0.00 90000/90000 FSSM::Pathname#set_prefix_and_names 219
1.45% 1.45% 0.21 0.21 0.00 0.00 90000 MatchData#post_match 0
    0.20 0.20 0.00 0.00 90000/90000 Kernel#dup 50
1.41% 1.41% 0.20 0.20 0.00 0.00 90000 <Class::Array>#allocate 0
    0.19 0.19 0.00 0.00 90000/90000 FSSM::Pathname#initialize 35
1.35% 1.35% 0.19 0.19 0.00 0.00 90000 String#initialize 0
    0.18 0.18 0.00 0.00 90000/90000 Class#new 35
1.26% 1.26% 0.18 0.18 0.00 0.00 90000 <Class::String>#allocate 0
    0.17 0.17 0.00 0.00 90000/90000 FSSM::Pathname#set_prefix_and_names 225
1.19% 1.19% 0.17 0.17 0.00 0.00 90000 Array#compact! 0
    0.16 0.16 0.00 0.00 90000/90000 FSSM::Pathname#to_a 52
1.11% 1.11% 0.16 0.16 0.00 0.00 90000 String#empty? 0
fssm-0.2.10/profile/prof-pathname.rb0000644000004100000410000000314112153365457017332 0ustar www-datawww-data$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'fssm' require 'pathname' require 'rubygems' require 'ruby-prof' $test_path = "#{Pathname.new('..').expand_path}" $iterations = 90000 class Pathname # original segments implementation I was using with # the plain ruby Pathname library. def segments prefix, names = split_names(@path) names.unshift(prefix) unless prefix.empty? names.shift if names[0] == '.' names end end core_result = Pathname.new($test_path).segments fssm_result = FSSM::Pathname.new($test_path).segments raise Exception, "#{core_result.inspect} != #{fssm_result.inspect}\nFSSM::Pathname is incompatible with Pathname" unless core_result == fssm_result RubyProf.start RubyProf.pause $iterations.times do |num| iteration = "%-6d" % (num + 1) puts "FSSM::Pathname iteration #{iteration}" RubyProf.resume p = FSSM::Pathname.new($test_path) segments = p.segments RubyProf.pause end puts "\nFSSM Pathname profile finished\n\n" result = RubyProf.stop output = File.new('prof-fssm-pathname.html', 'w+') printer = RubyProf::GraphHtmlPrinter.new(result) printer.print(output, :min_percent => 1) RubyProf.start RubyProf.pause $iterations.times do |num| iteration = "%-6d" % (num + 1) puts "::Pathname iteration #{iteration}" RubyProf.resume p = ::Pathname.new($test_path) segments = p.segments RubyProf.pause end puts "\nruby Pathname profile finished\n\n" result = RubyProf.stop output = File.new('prof-plain-pathname.html', 'w+') printer = RubyProf::GraphHtmlPrinter.new(result) printer.print(output, :min_percent => 1) fssm-0.2.10/profile/prof-pathname-rubinius.rb0000644000004100000410000000137112153365457021173 0ustar www-datawww-data$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'pathname' $test_path = "#{Pathname.new('..').expand_path}" $iterations = 900000 if ARGV.first == 'native' puts "Using native Pathname" class Pathname # original segments implementation I was using with # the plain ruby Pathname library. def segments prefix, names = split_names(@path) names.unshift(prefix) unless prefix.empty? names.shift if names[0] == '.' names end end $iterations.times do |num| p = ::Pathname.new($test_path) segments = p.segments end else puts "Using FSSM::Pathname" require 'fssm' $iterations.times do |num| p = FSSM::Pathname.new($test_path) segments = p.segments end end fssm-0.2.10/profile/prof-plain-pathname.html0000644000004100000410000006076412153365457021012 0ustar www-datawww-data

Profile Report

Thread ID Total Time
3696392 27.97608

Thread 3696392

%Total %Self Total Self Wait Child Calls Name Line
100.00% 0.00% 27.98 0.00 0.00 27.98 0 Global#[No method] 50
    5.33 0.61 0.00 4.73 90000/90000 Class#new 57
    22.64 1.34 0.00 21.30 90000/90000 Pathname#segments 58
    22.64 1.34 0.00 21.30 90000/90000 Global#[No method] 58
80.93% 4.80% 22.64 1.34 0.00 21.30 90000 Pathname#segments 15
    20.96 3.23 0.00 17.73 90000/90000 Pathname#split_names 16
    0.16 0.16 0.00 0.00 90000/90000 String#empty? 17
    0.18 0.18 0.00 0.00 90000/450000 Array#unshift 17
    20.96 3.23 0.00 17.73 90000/90000 Pathname#segments 16
74.94% 11.56% 20.96 3.23 0.00 17.73 90000 Pathname#split_names 307
    17.00 10.58 0.00 6.42 450000/450000 Pathname#chop_basename 311
    0.73 0.73 0.00 0.00 360000/450000 Array#unshift 311
    17.00 10.58 0.00 6.42 450000/450000 Pathname#split_names 311
60.76% 37.83% 17.00 10.58 0.00 6.42 450000 Pathname#chop_basename 296
    1.38 1.38 0.00 0.00 450000/450000 <Class::File>#basename 297
    1.37 1.37 0.00 0.00 450000/450000 Regexp#to_s 298
    1.95 1.95 0.00 0.00 450000/450000 Regexp#=~ 298
    0.80 0.80 0.00 0.00 360000/360000 String#rindex 298
    0.91 0.91 0.00 0.00 360000/360000 String#[] 298
    5.33 0.61 0.00 4.73 90000/90000 Global#[No method] 57
19.07% 2.18% 5.33 0.61 0.00 4.73 90000 Class#new 0
    0.25 0.25 0.00 0.00 90000/90000 <Class::BasicObject>#allocate 57
    4.47 1.87 0.00 2.60 90000/90000 Pathname#initialize 57
    4.47 1.87 0.00 2.60 90000/90000 Class#new 57
15.98% 6.70% 4.47 1.87 0.00 2.60 90000 Pathname#initialize 210
    0.20 0.20 0.00 0.00 90000/90000 Kernel#respond_to? 211
    0.97 0.58 0.00 0.39 90000/90000 Kernel#dup 212
    0.16 0.16 0.00 0.00 90000/90000 Kernel#tainted? 218
    1.27 0.94 0.00 0.33 90000/90000 Pathname#taint 218
    1.95 1.95 0.00 0.00 450000/450000 Pathname#chop_basename 298
6.97% 6.97% 1.95 1.95 0.00 0.00 450000 Regexp#=~ 0
    1.38 1.38 0.00 0.00 450000/450000 Pathname#chop_basename 297
4.94% 4.94% 1.38 1.38 0.00 0.00 450000 <Class::File>#basename 0
    1.37 1.37 0.00 0.00 450000/450000 Pathname#chop_basename 298
4.88% 4.88% 1.37 1.37 0.00 0.00 450000 Regexp#to_s 0
    1.27 0.94 0.00 0.33 90000/90000 Pathname#initialize 218
4.54% 3.36% 1.27 0.94 0.00 0.33 90000 Pathname#taint 222
    0.33 0.33 0.00 0.00 180000/180000 Kernel#taint 222
    0.97 0.58 0.00 0.39 90000/90000 Pathname#initialize 212
3.46% 2.07% 0.97 0.58 0.00 0.39 90000 Kernel#dup 0
    0.21 0.21 0.00 0.00 90000/90000 <Class::String>#allocate 212
    0.18 0.18 0.00 0.00 90000/90000 String#initialize_copy 212
    0.91 0.91 0.00 0.00 360000/360000 Pathname#chop_basename 298
3.26% 3.26% 0.91 0.91 0.00 0.00 360000 String#[] 0
    0.73 0.73 0.00 0.00 360000/450000 Pathname#split_names 311
    0.18 0.18 0.00 0.00 90000/450000 Pathname#segments 17
3.24% 3.24% 0.91 0.91 0.00 0.00 450000 Array#unshift 0
    0.80 0.80 0.00 0.00 360000/360000 Pathname#chop_basename 298
2.88% 2.88% 0.80 0.80 0.00 0.00 360000 String#rindex 0
    0.33 0.33 0.00 0.00 180000/180000 Pathname#taint 222
1.17% 1.17% 0.33 0.33 0.00 0.00 180000 Kernel#taint 0
fssm-0.2.10/Rakefile0000644000004100000410000000041712153365457014254 0ustar www-datawww-datarequire 'rubygems' require 'bundler' Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |spec| spec.rspec_opts = ["--color", "--backtrace", "--format", "documentation"] spec.verbose = true end task :default => :spec fssm-0.2.10/spec/0000755000004100000410000000000012153365457013537 5ustar www-datawww-datafssm-0.2.10/spec/root/0000755000004100000410000000000012153365457014522 5ustar www-datawww-datafssm-0.2.10/spec/root/file.yml0000644000004100000410000000000012153365457016152 0ustar www-datawww-datafssm-0.2.10/spec/root/file.css0000644000004100000410000000000012153365457016141 0ustar www-datawww-datafssm-0.2.10/spec/root/duck/0000755000004100000410000000000012153365457015450 5ustar www-datawww-datafssm-0.2.10/spec/root/duck/quack.txt0000644000004100000410000000000012153365457017303 0ustar www-datawww-datafssm-0.2.10/spec/root/file.rb0000644000004100000410000000000012153365457015754 0ustar www-datawww-datafssm-0.2.10/spec/root/moo/0000755000004100000410000000000012153365457015314 5ustar www-datawww-datafssm-0.2.10/spec/root/moo/cow.txt0000644000004100000410000000000012153365457016633 0ustar www-datawww-datafssm-0.2.10/spec/monitor_spec.rb0000644000004100000410000001712712153365457016575 0ustar www-datawww-datarequire 'spec_helper' require 'count_down_latch' require 'fileutils' require 'tempfile' module FSSM::MonitorSpecHelpers def create_tmp_dir @tmp_dir = FSSM::Pathname.for(Dir.mktmpdir).realpath.to_s FileUtils.cp_r File.join(File.dirname(__FILE__), 'root'), @tmp_dir # Because git does not track empty directories, create one ourselves. FileUtils.mkdir_p @tmp_dir + '/root/yawn' @tmp_dir end def remove_tmp_dir FileUtils.remove_entry @tmp_dir end def create_handler(type, latch) lambda do |*args| @handler_results[type] << args latch.count_down end end def run_monitor(num_events_to_expect=0, options={}) event_latch = CountDownLatch.new(num_events_to_expect) @handler_results = Hash.new { |hash, key| hash[key] = [] } thread = Thread.new do monitor = FSSM::Monitor.new(options) monitor.path(@tmp_dir) do |p| p.create(&create_handler(:create, event_latch)) p.update(&create_handler(:update, event_latch)) p.delete(&create_handler(:delete, event_latch)) end monitor.run end sleep 1 # give time for monitor to start up yield if block_given? event_latch.wait thread.kill end end describe "The File System State Monitor" do describe "monitor" do include FSSM::MonitorSpecHelpers before do create_tmp_dir end after do remove_tmp_dir end describe "with default options" do it "should call create callback upon file creation" do run_monitor(1) do file = @tmp_dir + "/newfile.rb" File.exists?(file).should be_false FileUtils.touch file end @handler_results[:create].should == [[@tmp_dir, 'newfile.rb']] end it "should call update callback upon file modification" do run_monitor(1) do FileUtils.touch @tmp_dir + '/root/file.rb' end @handler_results[:update].should == [[@tmp_dir, 'root/file.rb']] end it "should call delete callback upon file deletion" do run_monitor(1) do FileUtils.rm @tmp_dir + "/root/file.rb" end @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb']] end it "should call create and delete callbacks upon file renaming in the same directory" do run_monitor(2) do FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/root/old_file.rb" end @handler_results[:create].should == [[@tmp_dir, 'root/old_file.rb']] @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb']] @handler_results[:update].should == [] end it "should call create and delete callbacks upon file moving to another directory" do run_monitor(2) do FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/old_file.rb" end @handler_results[:create].should == [[@tmp_dir, 'old_file.rb']] @handler_results[:delete].should == [[@tmp_dir, 'root/file.rb']] @handler_results[:update].should == [] end it "should not call callbacks upon directory operations" do run_monitor do FileUtils.mkdir @tmp_dir + "/another_yawn" FileUtils.rmdir @tmp_dir + "/root/yawn" end @handler_results[:create].should == [] @handler_results[:delete].should == [] end end describe "when configured to consider files and directories" do it "should call create callback upon directory creation" do run_monitor(1, :directories => true) do FileUtils.mkdir @tmp_dir + "/another_yawn" end @handler_results[:create].should include([@tmp_dir, 'another_yawn', :directory]) end it "should call delete callback upon directory deletion" do run_monitor(1, :directories => true) do FileUtils.rmdir @tmp_dir + "/root/yawn" end @handler_results[:delete].should include([@tmp_dir, 'root/yawn', :directory]) end it "should call create, update, and delete callbacks upon directory renaming in the same directory" do run_monitor(3, :directories => true) do FileUtils.mv @tmp_dir + "/root/yawn", @tmp_dir + "/root/old_yawn" end @handler_results[:create].should include([@tmp_dir, 'root/old_yawn', :directory]) @handler_results[:delete].should include([@tmp_dir, 'root/yawn', :directory]) @handler_results[:update].should include([@tmp_dir, 'root', :directory]) end it "should call create, update, and delete callbacks upon directory moving to another directory" do run_monitor(3, :directories => true) do FileUtils.mv @tmp_dir + "/root/yawn", @tmp_dir + "/old_yawn" end @handler_results[:create].should include([@tmp_dir, 'old_yawn', :directory]) @handler_results[:delete].should include([@tmp_dir, 'root/yawn', :directory]) @handler_results[:update].should include([@tmp_dir, 'root', :directory]) end it "should call create, update, and delete callbacks upon file renaming in the same directory" do run_monitor(3, :directories => true) do FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/root/old_file.rb" end @handler_results[:create].should include([@tmp_dir, 'root/old_file.rb', :file]) @handler_results[:delete].should include([@tmp_dir, 'root/file.rb', :file]) @handler_results[:update].should include([@tmp_dir, 'root', :directory]) end it "should call create, update, and delete callbacks upon file moving to another directory" do run_monitor(3, :directories => true) do FileUtils.mv @tmp_dir + "/root/file.rb", @tmp_dir + "/old_file.rb" end @handler_results[:create].should include([@tmp_dir, 'old_file.rb', :file]) @handler_results[:delete].should include([@tmp_dir, 'root/file.rb', :file]) @handler_results[:update].should include([@tmp_dir, 'root', :directory]) end it "should call delete callbacks upon directory structure deletion, in reverse order" do expected_delete_events = [ ['root/yawn', :directory], ['root/moo/cow.txt', :file], ['root/moo', :directory], ['root/file.yml', :file], ['root/file.rb', :file], ['root/file.css', :file], ['root/duck/quack.txt', :file], ['root/duck', :directory], ['root', :directory] ] run_monitor(expected_delete_events.size, :directories => true) do FileUtils.rm_rf @tmp_dir + '/.' end @handler_results[:create].should == [] @handler_results[:delete].should == expected_delete_events.map { |(file, type)| [@tmp_dir, file, type] } @handler_results[:update].should == [] end it "should call create callbacks upon directory structure creation, in order" do expected_create_events = [ ['new_root', :directory], ['new_root/duck', :directory], ['new_root/duck/quack.txt', :file], ['new_root/file.css', :file], ['new_root/file.rb', :file], ['new_root/file.yml', :file], ['new_root/moo', :directory], ['new_root/moo/cow.txt', :file], ['new_root/yawn', :directory] ] run_monitor(expected_create_events.size, :directories => true) do FileUtils.cp_r @tmp_dir + '/root/.', @tmp_dir + '/new_root' end @handler_results[:create].should == expected_create_events.map { |(file, type)| [@tmp_dir, file, type] } @handler_results[:delete].should == [] @handler_results[:update].should == [] end end end end fssm-0.2.10/spec/count_down_latch.rb0000644000004100000410000000671412153365457017426 0ustar www-datawww-data#!/usr/bin/env ruby # coding: utf-8 require 'thread' class CountDownLatch attr_reader :count def initialize(to) @count = to.to_i raise ArgumentError, "cannot count down from negative integer" unless @count >= 0 @lock = Mutex.new @condition = ConditionVariable.new end def count_down @lock.synchronize do @count -= 1 if @count > 0 @condition.broadcast if @count == 0 end end def wait @lock.synchronize do @condition.wait(@lock) while @count > 0 end end end if $0 == __FILE__ require 'test/unit' class CountDownLatchTest < Test::Unit::TestCase def test_requires_positive_count assert_raise(ArgumentError) { CountDownLatch.new(-1) } end def test_basic_latch_usage latch = CountDownLatch.new(1) name = "foo" Thread.new do name = "bar" latch.count_down end latch.wait assert_equal(0, latch.count) assert_equal("bar", name) end def test_basic_latch_usage_inverted latch = CountDownLatch.new(1) name = "foo" Thread.new do latch.wait assert_equal(0, latch.count) assert_equal("bar", name) end name = "bar" latch.count_down end def test_count_down_from_zero_skips_wait latch = CountDownLatch.new(0) latch.wait assert_equal(0, latch.count) end def test_count_down_twice_with_thread latch = CountDownLatch.new(2) name = "foo" Thread.new do latch.count_down name = "bar" latch.count_down end latch.wait assert_equal(0, latch.count) assert_equal("bar", name) end def test_count_down_twice_with_two_parallel_threads latch = CountDownLatch.new(2) name = "foo" Thread.new { latch.count_down } Thread.new do name = "bar" latch.count_down end latch.wait assert_equal(0, latch.count) assert_equal("bar", name) end def test_count_down_twice_with_two_chained_threads latch = CountDownLatch.new(2) name = "foo" Thread.new do latch.count_down Thread.new do name = "bar" latch.count_down end end latch.wait assert_equal(0, latch.count) assert_equal("bar", name) end def test_count_down_with_multiple_waiters proceed_latch = CountDownLatch.new(2) check_latch = CountDownLatch.new(2) results = {} Thread.new do proceed_latch.wait results[:first] = 1 check_latch.count_down end Thread.new do proceed_latch.wait results[:second] = 2 check_latch.count_down end assert_equal({}, results) proceed_latch.count_down proceed_latch.count_down check_latch.wait assert_equal(0, proceed_latch.count) assert_equal(0, check_latch.count) assert_equal({:first => 1, :second => 2}, results) end def test_interleaved_latches change_1_latch = CountDownLatch.new(1) check_latch = CountDownLatch.new(1) change_2_latch = CountDownLatch.new(1) name = "foo" Thread.new do name = "bar" change_1_latch.count_down check_latch.wait name = "man" change_2_latch.count_down end change_1_latch.wait assert_equal("bar", name) check_latch.count_down change_2_latch.wait assert_equal("man", name) end end end fssm-0.2.10/spec/path_spec.rb0000644000004100000410000000630012153365457016031 0ustar www-datawww-datarequire "spec_helper" describe "The File System State Monitor" do describe "paths" do it "should accept a valid filesystem directory" do lambda { FSSM::Path.new("#{@watch_root}") }.should_not raise_error end it "should not accept an invalid filesystem directory" do lambda { FSSM::Path.new('/does/not/exist/kthxbye') }.should raise_error end it "should default the path to the current directory" do path = FSSM::Path.new here = Pathname.new('.').realpath "#{here}".should == "#{path}" end it "should accept an optional glob array parameter" do path = FSSM::Path.new('.', ['**/*.yml']) path.glob.should == ['**/*.yml'] end it "should accept an optional glob string parameter" do path = FSSM::Path.new('.', '**/*.yml') path.glob.should == ['**/*.yml'] end it "should accept an optional option parameter" do lambda { FSSM::Path.new('.', '**/*.yml', :foo => :bar) }.should_not raise_error end it "should default the glob to ['**/*']" do path = FSSM::Path.new path.glob.should == ['**/*'] end it "should accept a callback for update events" do path = FSSM::Path.new callback = lambda { |base, relative| return true } path.update(&callback) (path.update).should == callback end it "should accept a callback for delete events" do path = FSSM::Path.new callback = lambda { |base, relative| return true } path.delete(&callback) (path.delete).should == callback end it "should accept a callback for create events" do path = FSSM::Path.new callback = lambda { |base, relative| return true } path.create(&callback) (path.create).should == callback end it "should accept a configuration block" do path = FSSM::Path.new "#{@watch_root}" do glob '**/*.yml' update { |base, relative| 'success' } delete { |base, relative| 'success' } create { |base, relative| 'success' } end "#{path}".should == "#{@watch_root}" path.glob.should == ['**/*.yml'] path.update.should be_a_kind_of(Proc) path.delete.should be_a_kind_of(Proc) path.create.should be_a_kind_of(Proc) path.update.call('', '').should == 'success' path.delete.call('', '').should == 'success' path.create.call('', '').should == 'success' end it "should pass file type to callbacks as the third argument if :directories option is used" do path = FSSM::Path.new "#{@watch_root}", nil, :directories => true do glob '**/*.yml' update { |base, relative, type| [base, relative, type] } delete { |base, relative, type| [base, relative, type] } create { |base, relative, type| [base, relative, type] } end "#{path}".should == "#{@watch_root}" path.glob.should == ['**/*.yml'] path.update.should be_a_kind_of(Proc) path.delete.should be_a_kind_of(Proc) path.create.should be_a_kind_of(Proc) path.update.call('b', 'r', 't').should == ['b', 'r', 't'] path.delete.call('b', 'r', 't').should == ['b', 'r', 't'] path.create.call('b', 'r', 't').should == ['b', 'r', 't'] end end end fssm-0.2.10/spec/spec_helper.rb0000644000004100000410000000051512153365457016356 0ustar www-datawww-data$LOAD_PATH.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__))) require 'rubygems' require 'bundler/setup' require 'fssm' require 'rspec' RSpec.configure do |config| config.before :all do @watch_root = FSSM::Pathname.new(__FILE__).dirname.join('root').expand_path end end fssm-0.2.10/README.markdown0000644000004100000410000000470712153365457015316 0ustar www-datawww-data# FSSM - currently unmaintained # Monitor API =========== There are three ways you can run the monitor. 1. call monitor with a path parameter, and define callbacks in a block 2. call monitor with a block to configure multiple paths and callbacks 3. create a monitor object and run each step manually Monitor with path ----------------- This form watches one path, and enters the run loop automatically. The first parameter is the path to watch, and the second parameter is an optional glob pattern or array of glob patterns that a file must match in order to trigger a callback. The default glob, if ommitted, is `'**/*'`. FSSM.monitor('/some/directory/', '**/*') do update {|base, relative|} delete {|base, relative|} create {|base, relative|} end Monitor with block ------------------ This form watches one or more paths, and enters the run loop automatically. The glob configuration call can be ommitted, and defaults to `'**/*'`. FSSM.monitor do path '/some/directory/' do glob '**/*.yml' update {|base, relative|} delete {|base, relative|} create {|base, relative|} end path '/some/other/directory/' do update {|base, relative|} delete {|base, relative|} create {|base, relative|} end end Monitor object -------------- This form doesn't enter the run loop automatically. monitor = FSSM::Monitor.new monitor.path '/some/directory/' do update {|base, relative|} delete {|base, relative|} create {|base, relative|} end monitor.run Monitoring directories ---------------------- By default, FSSM monitors changes in files only. To enable monitoring of files and directories, pass option `directories => true` in a hash to the monitor. Please note that this may not work as expected in all backends. For example: FSSM::Monitor.new(:directories => true) FSSM.monitor(dir, file_glob, :directories => true) When directories are monitored, there's an additional third argument to the callbacks. Instead of FSSM.monitor('/some/directory/', '**/*') do update {|base, relative|} delete {|base, relative|} create {|base, relative|} end you get this: FSSM.monitor('/some/directory/', '**/*', :directories => true) do update {|base, relative, type|} delete {|base, relative, type|} create {|base, relative, type|} end The value of `type` argument is either `:file` or `:directory`. fssm-0.2.10/metadata.yml0000644000004100000410000000572212153365457015116 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: fssm version: !ruby/object:Gem::Version version: 0.2.10 prerelease: platform: ruby authors: - Travis Tilley - Nathan Weizenbaum - Chris Eppstein - Jonathan Castello - Tuomas Kareinen autorequire: bindir: bin cert_chain: [] date: 2013-01-27 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency prerelease: false requirement: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' none: false type: :development version_requirements: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' none: false name: rake - !ruby/object:Gem::Dependency prerelease: false requirement: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: 2.4.0 none: false type: :development version_requirements: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: 2.4.0 none: false name: rspec description: The File System State Monitor keeps track of the state of any number of paths and will fire events when said state changes (create/update/delete). FSSM supports using FSEvents on MacOS, Inotify on GNU/Linux, and polling anywhere else. email: - ttilley@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - .gitignore - .travis.yml - Gemfile - LICENSE - README.markdown - Rakefile - example.rb - ext/rakefile.rb - fssm.gemspec - lib/fssm.rb - lib/fssm/backends/inotify.rb - lib/fssm/backends/polling.rb - lib/fssm/backends/rbfsevent.rb - lib/fssm/monitor.rb - lib/fssm/path.rb - lib/fssm/pathname.rb - lib/fssm/state/directory.rb - lib/fssm/state/file.rb - lib/fssm/support.rb - lib/fssm/tree.rb - lib/fssm/version.rb - profile/prof-cache.rb - profile/prof-fssm-pathname.html - profile/prof-pathname-rubinius.rb - profile/prof-pathname.rb - profile/prof-plain-pathname.html - profile/prof.html - spec/count_down_latch.rb - spec/monitor_spec.rb - spec/path_spec.rb - spec/root/duck/quack.txt - spec/root/file.css - spec/root/file.rb - spec/root/file.yml - spec/root/moo/cow.txt - spec/spec_helper.rb homepage: https://github.com/ttilley/fssm licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' none: false required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' none: false requirements: [] rubyforge_project: fssm rubygems_version: 1.8.24 signing_key: specification_version: 3 summary: File System State Monitor test_files: - spec/count_down_latch.rb - spec/monitor_spec.rb - spec/path_spec.rb - spec/root/duck/quack.txt - spec/root/file.css - spec/root/file.rb - spec/root/file.yml - spec/root/moo/cow.txt - spec/spec_helper.rb has_rdoc: fssm-0.2.10/Gemfile0000644000004100000410000000004612153365457014100 0ustar www-datawww-datasource "http://rubygems.org" gemspec fssm-0.2.10/example.rb0000644000004100000410000000045512153365457014571 0ustar www-datawww-data$:.unshift(File.join(File.dirname(__FILE__), 'lib')) # for rb-inotify/rb-fsevent require 'rubygems' require 'fssm' FSSM.monitor('.', '**/*') do update { |b, r| puts "Update in #{b} to #{r}" } delete { |b, r| puts "Delete in #{b} to #{r}" } create { |b, r| puts "Create in #{b} to #{r}" } end fssm-0.2.10/.gitignore0000644000004100000410000000012512153365457014573 0ustar www-datawww-data*.gem *.rbc *.sw? .DS_Store .bundle .idea Gemfile.lock coverage nbproject pkg/* rdoc fssm-0.2.10/lib/0000755000004100000410000000000012153365457013353 5ustar www-datawww-datafssm-0.2.10/lib/fssm/0000755000004100000410000000000012153365457014323 5ustar www-datawww-datafssm-0.2.10/lib/fssm/version.rb0000644000004100000410000000004512153365457016334 0ustar www-datawww-datamodule FSSM VERSION = "0.2.10" end fssm-0.2.10/lib/fssm/state/0000755000004100000410000000000012153365457015443 5ustar www-datawww-datafssm-0.2.10/lib/fssm/state/directory.rb0000644000004100000410000000356712153365457020007 0ustar www-datawww-datamodule FSSM::State class Directory attr_reader :path def initialize(path, options={}) @path = path @options = options @cache = FSSM::Tree::Cache.new end def refresh(base=nil, skip_callbacks=false) base_path = FSSM::Pathname.for(base || @path.to_pathname).expand_path previous, current = recache(base_path) unless skip_callbacks deleted(previous, current) created(previous, current) modified(previous, current) end end private def created(previous, current) (current.keys - previous.keys).sort.each do |file| @path.create(file, current[file][1]) end end def deleted(previous, current) (previous.keys - current.keys).sort.reverse.each do |file| @path.delete(file, previous[file][1]) end end def modified(previous, current) (current.keys & previous.keys).each do |file| current_data = current[file] @path.update(file, current_data[1]) if (current_data[0] <=> previous[file][0]) != 0 end end def recache(base) base = FSSM::Pathname.for(base) previous = cache_entries snapshot(base) current = cache_entries [previous, current] end def snapshot(base) base = FSSM::Pathname.for(base) @cache.unset(base) @path.glob.each { |glob| add_glob(base, glob) } end def add_glob(base, glob) FSSM::Pathname.glob(base.join(glob).to_s).each do |fn| @cache.set(fn) end end def cache_entries entries = tag_entries(@cache.files, :file) entries.merge! tag_entries(@cache.directories, :directory) if @options[:directories] entries end def tag_entries(entries, tag) tagged_entries = {} entries.each_pair { |fname, mtime| tagged_entries[fname] = [mtime, tag] } tagged_entries end end end fssm-0.2.10/lib/fssm/state/file.rb0000644000004100000410000000135612153365457016714 0ustar www-datawww-datamodule FSSM::State class File attr_reader :path def initialize(path) @path = path end def refresh(base=nil, skip_callbacks=false) base ||= @path.to_pathname used_to_exist, @exists = @exists, base.exist? # this handles bad symlinks without failing. why handle bad symlinks at # all? well, we could still be interested in their creation and deletion. old_mtime, @mtime = @mtime, base.symlink? ? Time.at(0) : base.mtime if @exists unless skip_callbacks @path.delete(@path.to_s) if used_to_exist && !@exists @path.create(@path.to_s) if !used_to_exist && @exists @path.update(@path.to_s) if used_to_exist && @exists && old_mtime != @mtime end end end end fssm-0.2.10/lib/fssm/tree.rb0000644000004100000410000000617312153365457015616 0ustar www-datawww-datamodule FSSM::Tree module NodeBase def initialize @children = {} end protected def child(segment) @children["#{segment}"] end def child!(segment) (@children["#{segment}"] ||= Node.new) end def has_child?(segment) @children.has_key?("#{segment}") end def remove_child(segment) @children.delete("#{segment}") end def remove_children @children.clear end end module NodeEnumerable include NodeBase include Enumerable def each(prefix=nil, &block) @children.each do |segment, node| cprefix = prefix ? FSSM::Pathname.for(prefix).join(segment) : FSSM::Pathname.for(segment) yield [cprefix, node] node.each(cprefix, &block) end end end module NodeInsertion include NodeBase def unset(path) key = key_segments(path) if key.empty? remove_children return nil end segment = key.pop node = descendant(key) return unless node node.remove_child(segment) nil end def set(path) node = descendant!(path) node.from_path(path).mtime end protected def key_segments(key) return key if key.is_a?(Array) FSSM::Pathname.for(key).segments end def descendant(path) recurse(path, false) end def descendant!(path) recurse(path, true) end def recurse(key, create=false) key = key_segments(key) node = self until key.empty? segment = key.shift node = create ? node.child!(segment) : node.child(segment) return nil unless node end node end end module CacheDebug def set(path) FSSM.dbg("Cache#set(#{path})") super end def unset(path) FSSM.dbg("Cache#unset(#{path})") super end def ftype(ft) FSSM.dbg("Cache#ftype(#{ft})") super end end class Node include NodeBase include NodeEnumerable attr_accessor :mtime attr_accessor :ftype def <=>(other) return unless other.is_a?(::FSSM::Tree::Node) self.mtime <=> other.mtime end def from_path(path) path = FSSM::Pathname.for(path) @ftype = path.ftype # this handles bad symlinks without failing. why handle bad symlinks at # all? well, we could still be interested in their creation and deletion. @mtime = path.symlink? ? Time.at(0) : path.mtime self end end class Cache include NodeBase include NodeEnumerable include NodeInsertion include CacheDebug if $DEBUG def set(path) # all paths set from this level need to be absolute # realpath will fail on broken links path = FSSM::Pathname.for(path).expand_path super(path) end def files ftype('file') end def directories ftype('directory') end def links ftype('link') end alias symlinks links private def ftype(ft) inject({}) do |hash, (path, node)| hash["#{path}"] = node.mtime if node.ftype == ft hash end end end end fssm-0.2.10/lib/fssm/monitor.rb0000644000004100000410000000137412153365457016344 0ustar www-datawww-dataclass FSSM::Monitor def initialize(options={}) @options = options @backend = FSSM::Backends::Default.new end def path(path=nil, glob=nil, &block) path = create_path(path, glob, &block) @backend.add_handler(FSSM::State::Directory.new(path, @options)) path rescue FSSM::FileNotRealError => e FSSM.dbg("#{e}") nil end def file(path=nil, glob=nil, &block) path = create_path(path, glob, &block) @backend.add_handler(FSSM::State::File.new(path)) path rescue FSSM::FileNotRealError => e FSSM.dbg("#{e}") nil end def run @backend.run end private def create_path(path, glob, &block) path = FSSM::Path.new(path, glob, @options) FSSM::Support.use_block(path, block) path end end fssm-0.2.10/lib/fssm/backends/0000755000004100000410000000000012153365457016075 5ustar www-datawww-datafssm-0.2.10/lib/fssm/backends/inotify.rb0000644000004100000410000000121312153365457020100 0ustar www-datawww-datamodule FSSM::Backends class Inotify def initialize @notifier = INotify::Notifier.new end def add_handler(handler, preload=true) @notifier.watch(handler.path.to_s, :recursive, :attrib, :close_write, :create, :delete, :delete_self, :moved_from, :moved_to, :move_self) do |event| path = FSSM::Pathname.for(event.absolute_name) path = path.dirname unless event.name == "" # Event on root directory handler.refresh(path) end handler.refresh(nil, true) if preload end def run begin @notifier.run rescue Interrupt end end end end fssm-0.2.10/lib/fssm/backends/polling.rb0000644000004100000410000000105012153365457020062 0ustar www-datawww-datamodule FSSM::Backends class Polling def initialize(options={}) @handlers = [] @latency = options[:latency] || 1.5 end def add_handler(handler, preload=true) handler.refresh(nil, true) if preload @handlers << handler end def run begin loop do start = Time.now.to_f @handlers.each { |handler| handler.refresh } nap_time = @latency - (Time.now.to_f - start) sleep nap_time if nap_time > 0 end rescue Interrupt end end end end fssm-0.2.10/lib/fssm/backends/rbfsevent.rb0000644000004100000410000000174012153365457020422 0ustar www-datawww-datamodule FSSM::Backends class RBFSEvent def initialize @handlers = [] end def add_handler(handler, preload=true) @handlers << handler handler.refresh(nil, true) if preload end def run begin @fsevent = FSEvent.new @fsevent.watch(temporary_multipath_hack) do |paths| paths.each do |path| temporary_multipath_handler(path) end end @fsevent.run rescue Interrupt @fsevent.stop end end def temporary_multipath_handler(path) @handlers.each do |handler| handler_path = File.join(handler.path.to_s, "") if path.start_with?(handler_path) handler.refresh(path) break end end end def temporary_multipath_hack @handlers = @handlers.sort {|x,y| y.path.to_pathname.segments.length <=> x.path.to_pathname.segments.length} return @handlers.map {|handler| handler.path.to_s} end end end fssm-0.2.10/lib/fssm/support.rb0000644000004100000410000000264312153365457016371 0ustar www-datawww-datarequire 'rbconfig' module FSSM::Support class << self def usable_backend case when mac? && rb_fsevent? 'RBFSEvent' when linux? && rb_inotify? 'Inotify' else 'Polling' end end def optimal_backend_dependency return case when mac? then ['rb-fsevent', '>= 0.4.3.1'] when linux? then ['rb-inotify', '>= 0.8.8'] else [nil, nil] end end def backend @@backend ||= usable_backend end def jruby? defined?(JRUBY_VERSION) end def mac? RbConfig::CONFIG['target_os'] =~ /darwin/i end def lion? RbConfig::CONFIG['target_os'] =~ /darwin11/i end def linux? RbConfig::CONFIG['target_os'] =~ /linux/i end def rb_fsevent? begin require 'rb-fsevent' defined?(FSEvent::VERSION) ? FSEvent::VERSION.to_f >= 0.4 : false rescue LoadError false end end def rb_inotify? begin require 'rb-inotify' if defined?(INotify::VERSION) version = INotify::VERSION version[0] > 0 || version[1] >= 6 end rescue LoadError false end end def use_block(context, block) return if block.nil? if block.arity == 1 block.call(context) else context.instance_eval(&block) end end end end fssm-0.2.10/lib/fssm/pathname.rb0000644000004100000410000000141112153365457016442 0ustar www-datawww-datarequire 'fileutils' require 'find' require 'pathname' module FSSM class Pathname < ::Pathname VIRTUAL_REGEX = /^file:([^!]*)!/ class << self def for(path) path.is_a?(::FSSM::Pathname) ? path : new(path) end alias :[] :glob end def is_virtual? !!(VIRTUAL_REGEX =~ to_s) end def segments path = to_s array = path.split(File::SEPARATOR) array.delete('') array.insert(0, File::SEPARATOR) if path[0, 1] == File::SEPARATOR array[0] += File::SEPARATOR if path[0, 3] =~ SEPARATOR_PAT array end def glob(pattern, flags = 0, &block) patterns = [pattern].flatten patterns.map! { |p| self.class.glob(to_s + p, flags, &block) } patterns.flatten end end end fssm-0.2.10/lib/fssm/path.rb0000644000004100000410000000400512153365457015603 0ustar www-datawww-dataclass FSSM::Path def initialize(path=nil, glob=nil, options={}, &block) @options = options set_path(path || '.') set_glob(glob || '**/*') init_callbacks if block_given? if block.arity == 1 block.call(self) else self.instance_eval(&block) end end end def to_s @path.to_s end def to_pathname @path end def glob(value=nil) return @glob if value.nil? set_glob(value) end def create(*args, &block) callback_action(:create, (block_given? ? block : args)) end def update(*args, &block) callback_action(:update, (block_given? ? block : args)) end def delete(*args, &block) callback_action(:delete, (block_given? ? block : args)) end private def init_callbacks do_nothing = lambda { |base, relative|} @callbacks = Hash.new(do_nothing) end def callback_action(type, args=[]) if args.is_a?(Proc) set_callback(type, args) elsif args.empty? get_callback(type) else run_callback(type, args) end end def set_callback(type, arg) raise ArgumentError, "Proc expected" unless arg.is_a?(Proc) @callbacks[type] = arg end def get_callback(type) @callbacks[type] end def run_callback(type, args) callback_args = split_path(args[0]) callback_args << args[1] if @options[:directories] begin @callbacks[type].call(*callback_args) rescue Exception => e raise FSSM::CallbackError, "#{type} - #{args[0]}: #{e.message}", e.backtrace end end def split_path(path) path = FSSM::Pathname.for(path) [@path.to_s, (path.relative? ? path : path.relative_path_from(@path)).to_s] end def set_path(path) @path = FSSM::Pathname.for(path).expand_path raise FSSM::FileNotFoundError, "No such file or directory - #{@path}" unless @path.exist? raise FSSM::FileNotRealError, "Path is virtual - #{@path}" if @path.is_virtual? @path = @path.realpath end def set_glob(glob) @glob = glob.is_a?(Array) ? glob : [glob] end end fssm-0.2.10/lib/fssm.rb0000644000004100000410000000406012153365457014650 0ustar www-datawww-datadir = File.expand_path(File.dirname(__FILE__)) $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir) require 'thread' module FSSM FSSMError = Class.new(StandardError) FileNotFoundError = Class.new(FSSMError) FileNotRealError = Class.new(FSSMError) CallbackError = Class.new(FSSMError) autoload :VERSION, 'fssm/version' autoload :Pathname, 'fssm/pathname' autoload :Support, 'fssm/support' autoload :Tree, 'fssm/tree' autoload :Path, 'fssm/path' autoload :Monitor, 'fssm/monitor' module State autoload :Directory, 'fssm/state/directory' autoload :File, 'fssm/state/file' end module Backends autoload :Polling, 'fssm/backends/polling' autoload :FSEvents, 'fssm/backends/fsevents' autoload :RBFSEvent, 'fssm/backends/rbfsevent' autoload :Inotify, 'fssm/backends/inotify' class << self def set_backend(const_symbol=nil, value=nil) const_symbol ||= :Default value ||= ::FSSM::Support.backend if (value.is_a?(Symbol) || value.is_a?(String)) unless const_defined?(value) raise NameError, "uninitialized constant FSSM::Backends::#{value}" end value = const_get(value) end unless value.is_a?(Class) raise ArgumentError, "value must be a class or the symbol of an existing backend" end remove_const(const_symbol) if const_defined?(const_symbol) const_set(const_symbol, value) end def const_missing(symbol) symbol == :Default ? set_backend(symbol, FSSM::Support.backend) : super end end end class << self def dbg(msg=nil) STDERR.puts("FSSM -> #{msg}") end def monitor(*args, &block) options = args[-1].is_a?(Hash) ? args.pop : {} monitor = FSSM::Monitor.new(options) FSSM::Support.use_block(args.empty? ? monitor : monitor.path(*args), block) monitor.run end end end fssm-0.2.10/fssm.gemspec0000644000004100000410000000231312153365457015121 0ustar www-datawww-data# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "fssm/version" Gem::Specification.new do |s| s.name = "fssm" s.version = FSSM::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Travis Tilley", "Nathan Weizenbaum", "Chris Eppstein", "Jonathan Castello", "Tuomas Kareinen"] s.email = ["ttilley@gmail.com"] s.homepage = "https://github.com/ttilley/fssm" s.summary = %q{File System State Monitor} s.description = %q{The File System State Monitor keeps track of the state of any number of paths and will fire events when said state changes (create/update/delete). FSSM supports using FSEvents on MacOS, Inotify on GNU/Linux, and polling anywhere else.} s.rubyforge_project = "fssm" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } s.require_paths = ["lib"] # s.extensions = 'ext/rakefile.rb' s.add_development_dependency "rake" s.add_development_dependency "rspec", ">= 2.4.0" end