conrad_filer 0.0.1

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/.project ADDED
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>ConradFiler</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ </buildSpec>
9
+ <natures>
10
+ </natures>
11
+ </projectDescription>
data/README ADDED
File without changes
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'conrad_filer/cli'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'conrad_filer/cli'
8
+ end
9
+
10
+ cli = ConradFiler::CLI.new(ARGV)
11
+ cli.run
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ require 'conrad_filer/config'
3
+ require 'inotify'
4
+
5
+ module ConradFiler
6
+
7
+ Thread.abort_on_exception = true
8
+
9
+ class CLI
10
+
11
+ def initialize(argv)
12
+ @file_data = Hash.new
13
+ @file_changes = Hash.new
14
+ end
15
+
16
+ def run
17
+ @config = ConradFiler::Config.new
18
+ @watch_jobs = @config.get_watch_jobs
19
+ @id_types = @config.get_id_types
20
+ @rules = @config.get_rules
21
+
22
+ # Go through each of the rules and add them to the watch jobs they are configured for
23
+ # (note: any watch jobs that are not assigned rules will be disabled later)
24
+ @rules.each_value do |rule|
25
+ rule.get_watch_job_names.each do |watch_job_name|
26
+ rule.get_id_type_names.each do |id_type_name|
27
+ # puts "#{rule.name} #{watch_job_name} #{id_type_name}"
28
+ watch_job = @watch_jobs[watch_job_name]
29
+ watch_job.add_rule(@id_types[id_type_name], rule)
30
+ end
31
+ end
32
+ end
33
+
34
+ # Start up each watch job in a thread that will monitor directories. If a change is detected matching the bitmask associated
35
+ # with the watch job, the rule will be asked to perform for each id type that matches the file associated with the event
36
+ @watch_jobs.each_pair do |name, watch_job|
37
+ if watch_job.is_valid?
38
+ watch_job.start_inotify_thread
39
+ else
40
+ @config.disable_watch_job(name) # if watch job is enabled but nobody is referencing it, disable it so next time we don't bother including it
41
+ @watch_jobs.delete(name)
42
+ end
43
+ end
44
+
45
+
46
+ # Now that we are monitoring live updates, its time to go hunt down changes since the last time the app was run and perform the rules
47
+ @watch_jobs.each_value do |watch_job|
48
+ # TODO: Tell the watch job to look for changes since last run
49
+ #watch_job.?????
50
+ end
51
+
52
+ # Scan the directories looking for changes since the app was last run
53
+
54
+ #DirMonitor.all_jobs(db) do |monitor_job|
55
+ # @threads << Thread.new(polling_job) do |job|
56
+ # cur_dir = job.directory
57
+ # #Dir[job.].each do |filename|
58
+ # fs = File::Stat.new(filename)
59
+ # end
60
+ #end
61
+ #@threads.each {|thread| thread.join}
62
+
63
+ # Create a special watch job for the configuration file and db looking for changes
64
+ # by not spawning the thread, the app will continue running
65
+ self.create_config_watch_job.start_loop
66
+ end
67
+
68
+ # create a watch job to detect if the database or config file have changed (call on_config_file_changed if this happens)
69
+ def create_config_watch_job
70
+ config_watch = Inotify::InotifyThread.new
71
+ config_watch_bitmask = Inotify::InotifyBitmask.all_events
72
+ config_watch_bitmask.set_flag(:in_move_self)
73
+ config_watch_bitmask.set_flag(:in_delete_self)
74
+ config_watch_bitmask.set_flag(:in_modify)
75
+ #puts @config.cfg_filename
76
+ #puts config_watch_bitmask.as_array_of_symbols
77
+ config_watch.add_watch(@config.cfg_filename, config_watch_bitmask.bitmask, false)
78
+ config_watch.add_watch(@config.db_filename, config_watch_bitmask.bitmask, false)
79
+ config_watch.register_event(config_watch_bitmask.bitmask, &(self.method(:on_config_file_changed)))
80
+ config_watch
81
+ end
82
+
83
+ # ConradFilerApp.on_config_file_changed
84
+ # This is the callback which is called when the configuration changes
85
+ def on_config_file_changed(notify_event, path)
86
+ filename = path
87
+ if filename == File.expand_path(@config.cfg_filename)
88
+ print "config_file_changed - "
89
+ elsif filename == File.expand_path(@config.db_filename)
90
+ print "database updated - "
91
+ end
92
+ puts Inotify::InotifyBitmask.new(notify_event.mask).as_array_of_symbols
93
+ end
94
+ end
95
+
96
+ end
@@ -0,0 +1,98 @@
1
+ require 'rubygems'
2
+ require 'parseconfig'
3
+ require 'conrad_filer/watch_job'
4
+ require 'conrad_filer/id_type'
5
+ require 'conrad_filer/rule'
6
+ require 'conrad_filer/db'
7
+ require 'inotify'
8
+
9
+ module ConradFiler
10
+
11
+ class Config
12
+ attr_reader :cfg_filename
13
+ attr_reader :db_filename
14
+
15
+ # Register all tables referenced in this class so the database can be created if needed
16
+ ConradFiler::DB.register_table_definition("CREATE TABLE WATCH_JOBS (NAME TEXT, PATHNAME TEXT, BITMASK INTEGER, RECURSIVE TEXT, ENABLED TEXT)")
17
+ ConradFiler::DB.register_table_definition("CREATE TABLE ID_TYPES (NAME TEXT, MATCH_REGEXP TEXT, IGNORE_CASE TEXT)")
18
+ ConradFiler::DB.register_table_definition("CREATE TABLE RULES (NAME TEXT, DIRECTORY TEXT, RECURSIVE TEXT)")
19
+ ConradFiler::DB.register_table_definition("CREATE TABLE RULE_WATCH_JOBS (RULE TEXT, WATCH_JOB TEXT)")
20
+ ConradFiler::DB.register_table_definition("CREATE TABLE RULE_ID_TYPES (RULE TEXT, ID_TYPE TEXT)")
21
+
22
+
23
+ ## Register to create some data to play with when a new db is created
24
+ bitmask = Inotify::InotifyBitmask.all_events
25
+ bitmask.unset_flag(:in_close_nowrite)
26
+ bitmask.unset_flag(:in_open)
27
+ ConradFiler::DB.register_table_definition("INSERT INTO WATCH_JOBS (NAME,PATHNAME,BITMASK,RECURSIVE,ENABLED) VALUES ('WATCH_JOB','/home/jon/watch_job',#{bitmask.bitmask},'F','T')")
28
+ ConradFiler::DB.register_table_definition("INSERT INTO ID_TYPES (NAME,MATCH_REGEXP,IGNORE_CASE) VALUES ('ID_TYPE','.\\.MP3$','T')")
29
+ ConradFiler::DB.register_table_definition("INSERT INTO ID_TYPES (NAME,MATCH_REGEXP,IGNORE_CASE) VALUES ('ID_TYPE2','.\\.m4a$','T')")
30
+ ConradFiler::DB.register_table_definition("INSERT INTO RULES (NAME) VALUES ('RULE')")
31
+ ConradFiler::DB.register_table_definition("INSERT INTO RULE_WATCH_JOBS (RULE, WATCH_JOB) VALUES ('RULE', 'WATCH_JOB')")
32
+ ConradFiler::DB.register_table_definition("INSERT INTO RULE_ID_TYPES (RULE, ID_TYPE) VALUES ('RULE', 'ID_TYPE')")
33
+ ConradFiler::DB.register_table_definition("INSERT INTO RULE_ID_TYPES (RULE, ID_TYPE) VALUES ('RULE', 'ID_TYPE2')")
34
+
35
+
36
+ DEFAULT_CONFIG_FILENAME = '/etc/conrad_filer.conf'
37
+ #DEFAULT_DB_FILENAME = '/var/local/conrad_filer/conrad_filer.sdb'
38
+ DEFAULT_DB_FILENAME = 'conrad_filer.sdb'
39
+ #DEFAULT_LOG_FILENAME = '/var/local/conrad_filer/conrad_filer.log'
40
+ DEFAULT_LOG_FILENAME = 'conrad_filer.log'
41
+
42
+ def initialize(filename="")
43
+ @cfg_filename = filename
44
+ self.determine_file_locations
45
+ self.open_database
46
+ end
47
+
48
+ def determine_file_locations
49
+ @cfg_filename = DEFAULT_CONFIG_FILENAME if @cfg_filename.length == 0
50
+ @file_config = ParseConfig.new(@cfg_filename)
51
+ @db_filename = @file_config.get_value('database').to_s
52
+ @db_filename = DEFAULT_DB_FILENAME if @db_filename.length == 0
53
+ @log_filename = @file_config.get_value('logfile').to_s
54
+ @log_filename = DEFAULT_LOG_FILENAME if @log_filename.length == 0
55
+ end
56
+
57
+ def open_database
58
+ @db = ConradFiler::DB.new(@db_filename)
59
+ @db.open
60
+ end
61
+
62
+ def get_watch_jobs
63
+ objs = Hash.new
64
+ rows = @db.execute("SELECT * FROM WATCH_JOBS WHERE ENABLED = 'T'")
65
+ rows.each do |row|
66
+ obj = ConradFiler::WatchJob.new(@db, row)
67
+ objs[row['NAME']] = obj
68
+ end
69
+ objs
70
+ end
71
+
72
+ def disable_watch_job(watch_job_name)
73
+ @db.execute("UPDATE WATCH_JOBS SET ENABLED = 'F' WHERE NAME = '#{watch_job_name}'")
74
+ end
75
+
76
+ def get_id_types
77
+ objs = Hash.new
78
+ @db.execute("SELECT * FROM ID_TYPES").each do |row|
79
+ obj = ConradFiler::IdType.new(@db, row)
80
+ objs[row['NAME']] = obj
81
+ end
82
+ objs
83
+ end
84
+
85
+ def get_rules
86
+ objs = Hash.new
87
+ @db.execute("SELECT * FROM RULES").each do |row|
88
+ obj = ConradFiler::Rule.new(@db, row)
89
+ row['WATCH_JOBS'] = @db.execute_as_array("SELECT WATCH_JOB FROM RULE_WATCH_JOBS WHERE RULE = #{row['NAME']}")
90
+ row['ID_TYPES'] = @db.execute_as_array("SELECT ID_TYPE FROM RULE_ID_TYPES WHERE RULE = #{row['NAME']}")
91
+ objs[row['NAME']] = obj
92
+ end
93
+ objs
94
+ end
95
+
96
+ end
97
+
98
+ end
@@ -0,0 +1,87 @@
1
+ include ObjectSpace
2
+ require 'sqlite3'
3
+
4
+ module ConradFiler
5
+
6
+ class DB
7
+
8
+ @@table_defs = Array.new
9
+ @@table_defs << "CREATE TABLE DIR_INFO (DIR_NAME TEXT, DIR_HASH INTEGER, FULL_PATH TEXT)"
10
+
11
+ ## Class Methods ##
12
+
13
+ # Register the sql that will be called if the database needs to be created
14
+ def self.register_table_definition(sql)
15
+ @@table_defs << sql
16
+ end
17
+
18
+ # Finalizer definition for class instances (registered in ConradDB::open method if the database is successfully opened)
19
+ def self.finalize_instance(db)
20
+ proc { db.close }
21
+ end
22
+
23
+
24
+ ## Instance Methods ##
25
+
26
+ # Initialize the new instance
27
+ def initialize(filename)
28
+ @filename = filename
29
+ end
30
+
31
+ # Create the configuration database
32
+ def create
33
+ self.delete
34
+ @db = SQLite3::Database.new(@filename)
35
+ @@table_defs.each do |sql|
36
+ puts sql
37
+ @db.execute(sql)
38
+ end
39
+ end
40
+
41
+ # Open the configuration database
42
+ def open
43
+ if File.exists?(@filename)
44
+ @db = SQLite3::Database.open(@filename)
45
+ else
46
+ self.create
47
+ end
48
+ @db.results_as_hash = true
49
+ # Register the finalizer to make sure the db connection is closed
50
+ ObjectSpace.define_finalizer(self, self.class.finalize_instance(@db))
51
+ self
52
+ end
53
+
54
+ # Execute the sql
55
+ def execute(sql)
56
+ @db.execute(sql)
57
+ end
58
+
59
+ # Execute the sql as normal, but return an array from the first column of the result set
60
+ def execute_as_array(sql)
61
+ return_array = Array.new
62
+ @db.results_as_hash = false
63
+ self.execute(sql).each do |rec|
64
+ return_array << rec[0]
65
+ end
66
+ @db.results_as_hash = true
67
+ return_array
68
+ end
69
+
70
+ # Close the configuration database
71
+ def close
72
+ @db.close
73
+
74
+ # Unegister all finalizers since they are no longer needed
75
+ ObjectSpace.undefine_finalizer(self)
76
+ end
77
+
78
+ # Delete the configuration database
79
+ def delete
80
+ if File.exists?(@filename)
81
+ File.delete(@filename)
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,22 @@
1
+
2
+
3
+ module ConradFiler
4
+
5
+ class FileSystemEntry
6
+ class DBDir
7
+ puts "DBDir loading"
8
+ def initialize
9
+ @instvar
10
+ @@classvar
11
+ end
12
+ def self.from_path
13
+ end
14
+
15
+ class DBFile
16
+ puts "DBFile loading"
17
+ def initialize
18
+ @instvar
19
+ @@classvar
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ require 'conrad_filer/db'
2
+
3
+ module ConradFiler
4
+
5
+ class DirPollingJob
6
+
7
+ ConradFiler::DB.register_table_definition("CREATE TABLE WATCH_LIST (DIRECTORY TEXT, GLOB TEXT, INC_SUBDIR TEXT, RECURSIVE TEXT)")
8
+
9
+ ## Class Methods ##
10
+
11
+ def self.query
12
+ "SELECT DIRECTORY, GLOB, INC_SUBDIR, POLLING_DELAY FROM POLLING_JOBS"
13
+ end
14
+
15
+ def self.all_jobs(db)
16
+ jobs = Array.new
17
+ db.execute(self.query) do |row|
18
+ jobs << self.new(row)
19
+ end
20
+ jobs
21
+ end
22
+
23
+
24
+ ## Instance Methods ##
25
+
26
+ def initialize(an_array)
27
+ @rec = an_array
28
+ end
29
+
30
+ def include_subdirs?
31
+ @rec[3] == "T"
32
+ end
33
+
34
+ def directory
35
+ @rec[1]
36
+ end
37
+
38
+ def glob
39
+ @rec[2]
40
+ end
41
+
42
+ def perform
43
+ # Loop over all entries in the directory and collect the files and subdirectories
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,31 @@
1
+ require 'conrad_filer/db'
2
+
3
+ module ConradFiler
4
+
5
+ # An ID Type describes files in a directory using a regular expression. It is used by a rule to define which file types the rule applies to.
6
+ class IdType
7
+
8
+ def initialize(db, a_hash)
9
+ @db_rec = a_hash
10
+ end
11
+
12
+ def matches_pathname?(pathname)
13
+ pathname =~ self.get_regexp
14
+ end
15
+
16
+ def match_regexp
17
+ @db_rec['MATCH_REGEXP']
18
+ end
19
+
20
+ def ignore_case
21
+ @db_rec['IGNORE_CASE'][0,1].upcase == "T"
22
+ end
23
+
24
+ def get_regexp
25
+ return @regexp if @regexp != nil
26
+ @regexp = Regexp.new(self.match_regexp, self.ignore_case)
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,37 @@
1
+ require 'conrad_filer/db'
2
+ require 'inotify'
3
+
4
+ module ConradFiler
5
+
6
+ # A rule describes an action to perform on a file that matches one of its defined id types
7
+ class Rule
8
+
9
+ attr_accessor :db_rec
10
+
11
+ def initialize(db, a_hash)
12
+ @db_rec = a_hash
13
+ end
14
+
15
+ def get_watch_job_names
16
+ @db_rec['WATCH_JOBS']
17
+ end
18
+
19
+ def get_id_type_names
20
+ @db_rec['ID_TYPES']
21
+ end
22
+
23
+ def name
24
+ @db_rec['NAME']
25
+ end
26
+
27
+ def on_watch_event(watch_job, id_type, notify_event, path)
28
+ bitmask = Inotify::InotifyBitmask.new(notify_event.mask)
29
+ if notify_event.name.class == String
30
+ pathname = File.join(path, notify_event.name)
31
+ else
32
+ pathname = path
33
+ end
34
+ puts "[ConradRule::on_watch_event] '#{pathname}' #{bitmask.as_array_of_symbols}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module ConradFiler
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,72 @@
1
+ require 'conrad_filer/db'
2
+ require 'inotify'
3
+
4
+ module ConradFiler
5
+
6
+ # A watch job describes directories to monitor and includes a bitmask describing which actions to monitor
7
+ class WatchJob
8
+
9
+ def initialize(db, a_hash)
10
+ @db_rec = a_hash
11
+ @bitmask = Inotify::InotifyBitmask.new(@db_rec['BITMASK'])
12
+ @rules = Hash.new {Array.new}
13
+ @inotify_thread = Inotify::InotifyThread.new
14
+ end
15
+
16
+ def id_types
17
+ @rules.keys
18
+ end
19
+
20
+ def add_rule(id_type, rule)
21
+ @rules[id_type] = @rules[id_type] << rule
22
+ end
23
+
24
+ def recursive?
25
+ @db_rec['RECURSIVE'][0,1].upcase == "T"
26
+ end
27
+
28
+ def pathname
29
+ @db_rec['PATHNAME']
30
+ end
31
+
32
+ def bitmask
33
+ @bitmask.bitmask
34
+ end
35
+
36
+ def is_valid?
37
+ @rules.size > 0
38
+ end
39
+
40
+ def prepare_inotify_thread
41
+ @inotify_thread.add_watch(self.pathname, self.bitmask, self.recursive?)
42
+ @inotify_thread.register_event(self.bitmask, &(self.method(:process_event)))
43
+ end
44
+
45
+ def start_inotify_thread
46
+ self.prepare_inotify_thread
47
+ @inotify_thread.start_thread
48
+ end
49
+
50
+ def start_inotify_loop
51
+ self.prepare_inotify_thread
52
+ @inotify_thread.start_loop
53
+ end
54
+
55
+ def process_event(notify_event, path)
56
+ bitmask = Inotify::InotifyBitmask.new(notify_event.mask)
57
+ puts "[ConradWatchJob::process_event] '#{path}/#{notify_event.name}' #{bitmask.as_array_of_symbols}"
58
+ @rules.each_pair do |id_type, rules_for_id_type|
59
+ if id_type.matches_pathname?(File.join(path,notify_event.name.to_s))
60
+ rules_for_id_type.each do |rule|
61
+ rule.on_watch_event(self, id_type, notify_event, path)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ #def register_event(bitmask, &block)
68
+ # @inotify_thread.register_event(bitmask, &block)
69
+ #end
70
+ end
71
+
72
+ end
@@ -0,0 +1,9 @@
1
+ require 'conrad_filer/version'
2
+ require 'conrad_filer/cli'
3
+ require 'conrad_filer/config'
4
+ require 'conrad_filer/db'
5
+ require 'conrad_filer/id_type'
6
+ require 'conrad_filer/rule'
7
+ require 'conrad_filer/watch_job'
8
+ require 'conrad_filer/db_objects'
9
+ require 'conrad_filer/dir_polling_job'
@@ -0,0 +1,96 @@
1
+ require 'find'
2
+ require 'inotify'
3
+
4
+ module Inotify
5
+
6
+ class InotifyBitmask
7
+
8
+ @@symbols_and_bits = {
9
+ :in_access => 0, 0 => :in_access, # File was accessed (read) (*)
10
+ :in_modify => 1, 1 => :in_modify, # File was modified (*)
11
+ :in_attrib => 2, 2 => :in_attrib, # Metadata changed (permissions, timestamps, extended attributes, etc.) (*)
12
+ :in_close_write => 3, 3 => :in_close_write, # File opened for writing was closed (*)
13
+ :in_close_nowrite => 4, 4 => :in_close_nowrite, # File not opened for writing was closed (*)
14
+ :in_open => 5, 5 => :in_open, # File was opened (*)
15
+ :in_moved_from => 6, 6 => :in_moved_from, # File moved out of watched directory (*)
16
+ :in_moved_to => 7, 7 => :in_moved_to, # File moved into watched directory (*)
17
+ :in_create => 8, 8 => :in_create, # File/directory created in watched directory (*)
18
+ :in_delete => 9, 9 => :in_delete, # File/directory deleted from watched directory (*)
19
+ :in_delete_self => 10, 10 => :in_delete_self, # Watched file/directory was itself deleted
20
+ :in_move_self => 11, 11 => :in_move_self, # Watched file/directory was itself moved
21
+ :in_unmount => 13, 13 => :in_unmount, # Backing fs was unmounted
22
+ :in_q_overflow => 14, 14 => :in_q_overflow, # Event queued overflowed
23
+ :in_ignored => 15, 15 => :in_ignored, # File was ignored
24
+ :in_mask_add => 29, 29 => :in_mask_add, # add to the mask of an already existing watch
25
+ :in_isdir => 30, 30 => :in_isdir, # event occurred against dir
26
+ :in_oneshot => 31, 31 => :in_oneshot # only send event once
27
+ }
28
+ @@symbols_to_masks = {
29
+ :in_access => 0b00000000000000000000000000000001,
30
+ :in_modify => 0b00000000000000000000000000000010,
31
+ :in_attrib => 0b00000000000000000000000000000100,
32
+ :in_close_write => 0b00000000000000000000000000001000,
33
+ :in_close_nowrite => 0b00000000000000000000000000010000,
34
+ :in_open => 0b00000000000000000000000000100000,
35
+ :in_moved_from => 0b00000000000000000000000001000000,
36
+ :in_moved_to => 0b00000000000000000000000010000000,
37
+ :in_create => 0b00000000000000000000000100000000,
38
+ :in_delete => 0b00000000000000000000001000000000,
39
+ :in_delete_self => 0b00000000000000000000010000000000,
40
+ :in_move_self => 0b00000000000000000000100000000000,
41
+ :in_unmount => 0b00000000000000000010000000000000,
42
+ :in_q_overflow => 0b00000000000000000100000000000000,
43
+ :in_ignored => 0b00000000000000001000000000000000,
44
+ :in_mask_add => 0b00100000000000000000000000000000,
45
+ :in_isdir => 0b01000000000000000000000000000000,
46
+ :in_oneshot => 0b10000000000000000000000000000000
47
+ }
48
+
49
+ attr_reader :bitmask
50
+ @symbols
51
+
52
+ def self.all_events
53
+ inotify_bitmask = self.new(0b00000000000000000000111111111111) # All standard events
54
+ end
55
+ def initialize(bitmask=0)
56
+ if bitmask.class == String
57
+ @bitmask = bitmask.to_i
58
+ else
59
+ @bitmask = bitmask
60
+ end
61
+ end
62
+ def set_flag(a_symbol)
63
+ @bitmask = @bitmask | @@symbols_to_masks[a_symbol]
64
+ end
65
+ def unset_flag(a_symbol)
66
+ @bitmask = @bitmask & ~@@symbols_to_masks[a_symbol]
67
+ end
68
+
69
+ def test?(bitmask2)
70
+ (@bitmask & bitmask2) != 0
71
+ end
72
+ def watch_ignored?
73
+ self.test?(Inotify::IGNORED)
74
+ end
75
+ def is_directory?
76
+ self.test?(Inotify::ISDIR)
77
+ end
78
+ def overflow?
79
+ self.test?(Inotify::Q_OVERFLOW)
80
+ end
81
+ def unmount?
82
+ self.test?(Inotify::UNMOUNT)
83
+ end
84
+ def as_array_of_symbols
85
+ return @symbols if @symbols != nil
86
+ @symbols = Array.new
87
+ 31.downto(0) do |i|
88
+ if @bitmask[i] == 1
89
+ a_symbol = @@symbols_and_bits[i]
90
+ @symbols << a_symbol if a_symbol != nil
91
+ end
92
+ end
93
+ @symbols
94
+ end
95
+ end
96
+ end
Binary file
@@ -0,0 +1,77 @@
1
+ require 'find'
2
+ require 'inotify'
3
+
4
+ module Inotify
5
+
6
+ class InotifyThread
7
+ def initialize
8
+ @inotify = Inotify.new
9
+ @watch_list = Hash.new
10
+ @reg_events = Hash.new {Array.new}
11
+ @die_on_move = true
12
+ end
13
+
14
+ def start_thread
15
+ @thread = Thread.new do
16
+ self.start_loop
17
+ end
18
+ end
19
+
20
+ def start_loop
21
+ @inotify.each_event do |notify_event|
22
+ @reg_events.keys.each do |reg_event|
23
+ if (notify_event.mask & reg_event) > 0
24
+ @reg_events[reg_event].each do |event_block|
25
+ # There is no way to get the new path if the watched directory is moved, so stop watching it
26
+ if @die_on_move and InotifyBitmask.new(notify_event.mask).test?(:in_move_self)
27
+ self.remove_watch(@watch_list[notify_event.wd])
28
+ break
29
+ else
30
+ event_block.call(notify_event, @watch_list[notify_event.wd])
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def end_thread
39
+ @thread.kill
40
+ end
41
+
42
+ def add_watch(pathname, bitmask, recursive)
43
+ bm = InotifyBitmask.new(bitmask)
44
+ if @die_on_move
45
+ bm.set_flag(:in_move_self)
46
+ end
47
+ full_path = File.expand_path(pathname)
48
+ wd = @inotify.add_watch(full_path, bm.bitmask)
49
+ return wd if wd < 0
50
+ @watch_list[wd] = full_path
51
+ if recursive and FileTest.directory?(full_path)
52
+ Dir.foreach(full_path) do |filename|
53
+ full_filename = File.join(full_path, filename)
54
+ if FileTest.directory?(full_filename)
55
+ if filename[0] != ?.
56
+ wd = self.add_watch(full_filename, bm.bitmask, recursive)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ wd
62
+ end
63
+
64
+ def remove_watch(pathname)
65
+ wd = @watch_list[pathname]
66
+ if !wd.nil?
67
+ @inotify.rm_watch(wd)
68
+ @watch_list.delete(wd)
69
+ end
70
+ end
71
+
72
+ def register_event(bitmask, &block)
73
+ @reg_events[bitmask] = @reg_events[bitmask] << block
74
+ end
75
+ end
76
+
77
+ end
data/lib/inotify.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'inotify/inotify_native'
2
+ require 'inotify/thread'
3
+ require 'inotify/bitmask'
data/lib/test_run.rb ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'conrad_filer/cli'
3
+ cli = ConradFiler::CLI.new(ARGV)
4
+ cli.run
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: conrad_filer
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Jon Raiford
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-03 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Think of it as applying a firewall ruleset to file management.
23
+ email:
24
+ - jon@raiford.org
25
+ executables:
26
+ - conrad_filer.rb
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .project
33
+ - README
34
+ - bin/conrad_filer.rb
35
+ - lib/conrad_filer.rb
36
+ - lib/conrad_filer/cli.rb
37
+ - lib/conrad_filer/config.rb
38
+ - lib/conrad_filer/db.rb
39
+ - lib/conrad_filer/db_objects.rb
40
+ - lib/conrad_filer/dir_polling_job.rb
41
+ - lib/conrad_filer/id_type.rb
42
+ - lib/conrad_filer/rule.rb
43
+ - lib/conrad_filer/version.rb
44
+ - lib/conrad_filer/watch_job.rb
45
+ - lib/inotify.rb
46
+ - lib/inotify/bitmask.rb
47
+ - lib/inotify/inotify_native.so
48
+ - lib/inotify/thread.rb
49
+ - lib/test_run.rb
50
+ has_rdoc: true
51
+ homepage: ""
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.7
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: A rule-based file manager
84
+ test_files: []
85
+