conrad_filer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+