taggata 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,14 @@
1
1
  module Taggata
2
2
  module Parser
3
3
  class Query
4
- def self.parse(query)
4
+
5
+ attr_reader :db
6
+
7
+ def initialize(db)
8
+ @db = db
9
+ end
10
+
11
+ def parse(query)
5
12
  process(postfix(query))
6
13
  end
7
14
 
@@ -11,21 +18,26 @@ module Taggata
11
18
  #
12
19
  # @param token String the terminal token to resolve
13
20
  # @result [::Taggata::File] list of files with this tag
14
- def self.resolve(token)
21
+ def resolve(token)
15
22
  type, name = token.split(':', 2)
16
23
  case type.downcase
17
24
  when 'is', 'tag'
18
- ::Taggata::Tag.files(:name => name)
25
+ tag = ::Taggata::Persistent::Tag.find_one(db, :name => name)
26
+ tag.nil? ? [] : tag.files
27
+ when 'like'
28
+ files = ::Taggata::Persistent::File.find(db, Sequel.like(:name, name))
19
29
  when 'file', 'name'
20
- File.all.select { |f| f.name[/#{name}/] }
30
+ # File.all.select { |f| f.name[/#{name}/] }
31
+ files = ::Taggata::Persistent::File.find(db, {})
32
+ files.select { |f| f.name[/#{name}/] }
21
33
  when 'path'
22
- File.all.select { |f| f.path[/#{name}/] }
34
+ files = ::Taggata::Persistent::File.find(db, {})
35
+ files.select { |f| f.path[/#{name}/] }
23
36
  when 'missing'
24
- ::Taggata::Tag.files(:name => MISSING_TAG_NAME)
37
+ tag = ::Taggata::Persistent::Tag.find_or_create(db, :name => MISSING_TAG_NAME)
38
+ tag.files
25
39
  when 'untagged'
26
- ids = File.map(:id)
27
- .select { |id| DB[:file_tags].where(:file_id => id).empty? }
28
- File.where(:id => ids).all
40
+ db.find_untagged_files
29
41
  else
30
42
  fail "Unknown token type '#{type}'"
31
43
  end
@@ -35,7 +47,7 @@ module Taggata
35
47
  #
36
48
  # @param postfix [String] the input in postfix notation as array
37
49
  # @return [::Taggata::File]
38
- def self.process(postfix)
50
+ def process(postfix)
39
51
  stack = []
40
52
  postfix.each do |token|
41
53
  if operator? token
@@ -55,7 +67,7 @@ module Taggata
55
67
  # @param op_A [::Taggata::File] first operand
56
68
  # @param op_B [::Taggata::File] second operand
57
69
  # @result [::Taggata::File] result of applying operator to operands
58
- def self.apply(operator, op_A, op_B)
70
+ def apply(operator, op_A, op_B)
59
71
  case operator
60
72
  when :and
61
73
  op_A & op_B
@@ -70,7 +82,7 @@ module Taggata
70
82
  #
71
83
  # @param query String the query string
72
84
  # @result [String] query in postfix notation as an array
73
- def self.postfix(query)
85
+ def postfix(query)
74
86
  postfix = []
75
87
  operators = []
76
88
  query.split.each do |token|
@@ -93,7 +105,7 @@ module Taggata
93
105
  #
94
106
  # @param token String
95
107
  # @result the token
96
- def self.translate(token)
108
+ def translate(token)
97
109
  return :and if ['and', '&'].include? token.downcase
98
110
  return :or if ['or', '|'].include? token.downcase
99
111
  token
@@ -103,7 +115,7 @@ module Taggata
103
115
  #
104
116
  # @param token String
105
117
  # @result true/false
106
- def self.operator?(token)
118
+ def operator?(token)
107
119
  ['&', '|', 'or', 'and', '(', :and, :or].include? token.downcase
108
120
  end
109
121
  end
@@ -1,31 +1,37 @@
1
1
  module Taggata
2
2
  module Parser
3
3
  class Tag
4
+
5
+ attr_reader :db
6
+
7
+ def initialize(db)
8
+ @db = db
9
+ end
10
+
4
11
  # Parses give tagging string
5
12
  #
6
13
  # @param query String tagging string
7
14
  # @return [Hash]
8
- def self.parse(query)
15
+ def parse(query)
9
16
  result = { :add => [], :del => [] }
10
17
  hash = query.split.reduce(result) do |acc, tag|
11
18
  handle_tag(tag, acc)
12
19
  end
13
- dels = hash[:del].empty? ? [] : ::Taggata::Tag.where(:name => hash[:del]).all
20
+ dels = hash[:del].empty? ? [] : ::Taggata::Persistent::Tag.find(db, :name => hash[:del])
14
21
  adds = hash[:add].empty? ? [] : find_tags(hash[:add])
15
22
  { :add => adds, :del => dels }
16
23
  end
17
24
 
18
25
  private
19
26
 
20
- def self.find_tags(names)
21
- in_db = ::Taggata::Tag.where(:name => names).map(:name)
22
- ::Taggata::Tag
23
- .dataset
24
- .multi_insert((names - in_db).map { |name| { :name => name } })
25
- ::Taggata::Tag.where(:name => names).all
27
+ def find_tags(names)
28
+ in_db = ::Taggata::Persistent::Tag.find(db, :name => names).map(&:name)
29
+ values = (names - in_db).map { |name| { :name => name } }
30
+ ::Taggata::Persistent::Tag.bulk_insert(db, values)
31
+ ::Taggata::Persistent::Tag.find(db, :name => names)
26
32
  end
27
33
 
28
- def self.handle_tag(tag, result)
34
+ def handle_tag(tag, result)
29
35
  if tag.start_with?('-')
30
36
  result[:del] << tag[1..-1]
31
37
  elsif tag.start_with?('+')
@@ -0,0 +1,10 @@
1
+ module Taggata
2
+ module Persistent
3
+ require 'taggata/persistent/abstract'
4
+ require 'taggata/persistent/with_parent'
5
+ require 'taggata/persistent/directory'
6
+ require 'taggata/persistent/file'
7
+ require 'taggata/persistent/tag'
8
+ require 'taggata/persistent/file_tag'
9
+ end
10
+ end
@@ -0,0 +1,53 @@
1
+ module Taggata
2
+ module Persistent
3
+ class Abstract
4
+
5
+ def save
6
+ @id ||= db.save(self)
7
+ end
8
+
9
+ def self.destroy(db, options)
10
+ db.destroy(self, options)
11
+ end
12
+
13
+ def destroy(options = {})
14
+ db.destroy(self.class, self.to_hash)
15
+ end
16
+
17
+ def self.find_one(db, options)
18
+ db.find_one(self, options)
19
+ end
20
+
21
+ def self.bulk_insert(db, values)
22
+ db.bulk_insert(self, values)
23
+ end
24
+
25
+ def self.find(db, options)
26
+ db.find(self, options)
27
+ end
28
+
29
+ def self.find_or_create(db, options)
30
+ db.find_or_create(self, options)
31
+ end
32
+
33
+ def invalidate_cache
34
+ end
35
+
36
+ def to_hash
37
+ raise NotImplementedError
38
+ end
39
+
40
+ def self.new_from_hash(db, hash)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def self.table
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def show(indent = 0)
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,80 @@
1
+ module Taggata
2
+ module Persistent
3
+ class Directory < Abstract
4
+
5
+ include ::Taggata::Persistent::WithParent
6
+
7
+ attr_reader :db, :name, :parent_id, :id
8
+
9
+ def initialize(db, name, parent_id = nil, id = nil)
10
+ @db = db
11
+ @name = name
12
+ @parent_id = parent_id
13
+ @id = id
14
+ end
15
+
16
+ def self.table
17
+ :taggata_directories
18
+ end
19
+
20
+ def to_hash
21
+ {
22
+ :name => name,
23
+ :parent_id => parent_id,
24
+ :id => id
25
+ }
26
+ end
27
+
28
+ def self.new_from_hash(db, hash)
29
+ self.new(db,
30
+ hash[:name],
31
+ hash[:parent_id],
32
+ hash[:id])
33
+ end
34
+
35
+ def show(indent = 0)
36
+ indent.times { print " " }
37
+ puts "+ #{self.name}"
38
+ entries.each { |e| e.show(indent + 1) }
39
+ end
40
+
41
+ def directories
42
+ @child_directories ||= db.find_child_directories(self)
43
+ end
44
+
45
+ def files
46
+ @child_files ||= db.find_child_files(self)
47
+ end
48
+
49
+ def invalidate_cache
50
+ @child_directories = @child_files = nil
51
+ end
52
+
53
+ def entries
54
+ directories + files
55
+ end
56
+
57
+ # Scan children of this directory
58
+ def scan
59
+ scanner = ::Taggata::FilesystemScanner.new db
60
+ scanner.process(self)
61
+ validate
62
+ end
63
+
64
+ def validate
65
+ missing = ::Taggata::Persistent::Tag.find_or_create(:name => MISSING_TAG_NAME)
66
+ files.reject { |f| ::File.exist? f.path }.each { |f| f.add_tag missing }
67
+ directories.each(&:validate)
68
+ end
69
+
70
+ # Get full path of this directory
71
+ #
72
+ # @result full path of this directory
73
+ def path
74
+ parents = [self]
75
+ parents << parents.last.parent while parents.last.parent
76
+ ::File.join(parents.reverse.map(&:name))
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,99 @@
1
+ module Taggata
2
+ module Persistent
3
+ class File < Abstract
4
+
5
+ include WithParent
6
+
7
+ attr_reader :db, :name, :parent_id, :id
8
+
9
+ def initialize(db, name, parent_id, id = nil)
10
+ @name = name
11
+ @db = db
12
+ @parent_id = parent_id
13
+ @id = id
14
+ end
15
+
16
+ def add_tags_by_name(*tag_names)
17
+ tag_records = nil
18
+ db.transaction do
19
+ tag_records = tag_names.map { |tag_name| ::Taggata::Persistent::Tag.find_or_create(db, :name => tag_name) }
20
+ end
21
+ add_tags(*tag_records)
22
+ end
23
+
24
+ def add_tags(*to_add)
25
+ current_tag_ids = tags.map(&:id)
26
+ missing_tag_ids = to_add.map(&:id) - current_tag_ids
27
+ return if missing_tag_ids.empty?
28
+ options = missing_tag_ids.map { |tag_id| { :file_id => id, :tag_id => tag_id } }
29
+ db.bulk_insert(FileTag, options)
30
+ end
31
+
32
+ def remove_tags(*to_remove)
33
+ return if to_remove.empty?
34
+ current_tag_ids = tags.map(&:id)
35
+ to_remove_ids = current_tag_ids - to_remove.map(&:id)
36
+ options = to_remove_ids.map { |tag_id| { :file_id => id, :tag_id => tag_id } }
37
+ db.destroy(FileTag, options)
38
+ end
39
+
40
+ def remove_tags_by_name(*tag_names)
41
+ tags = tag_names.map { |tag_name| Tag.find_or_create db, :name => tag_name }
42
+ remove_tags(*tags)
43
+ end
44
+
45
+ def destroy
46
+ file_tags.each(&:destroy)
47
+ super
48
+ end
49
+
50
+ def file_tags
51
+ ::Taggata::Persistent::FileTag.find(db, :file_id => id)
52
+ end
53
+
54
+ def tags
55
+ file_tags.map(&:tag)
56
+ end
57
+
58
+ def to_hash
59
+ {
60
+ :name => name,
61
+ :id => id,
62
+ :parent_id => parent_id
63
+ }
64
+ end
65
+
66
+ def self.new_from_hash(db, hash)
67
+ self.new(db,
68
+ hash[:name],
69
+ hash[:parent_id],
70
+ hash[:id])
71
+ end
72
+
73
+ def self.table
74
+ :taggata_files
75
+ end
76
+
77
+ def show(indent = 0)
78
+ indent.times { print " " }
79
+ puts "- #{self.name}"
80
+ end
81
+
82
+ def path
83
+ ::File.join(parent.path, name)
84
+ end
85
+
86
+ end
87
+ end
88
+ end
89
+
90
+ # def before_destroy
91
+ # remove_all_tags
92
+ # end
93
+ #
94
+ # # Gets full path of the file
95
+ # #
96
+ # # @return String full path of the file
97
+
98
+ # end
99
+ # end
@@ -0,0 +1,48 @@
1
+ module Taggata
2
+ module Persistent
3
+ class FileTag < Abstract
4
+
5
+ attr_reader :db, :file_id, :tag_id
6
+
7
+ def initialize(db, file_id, tag_id)
8
+ @file_id = file_id
9
+ @tag_id = tag_id
10
+ @db = db
11
+ end
12
+
13
+ def self.table
14
+ :taggata_file_tags
15
+ end
16
+
17
+ def file
18
+ ::Taggata::Persistent::File.find_one(db, :id => file_id)
19
+ end
20
+
21
+ def tag
22
+ ::Taggata::Persistent::Tag.find_one(db, :id => tag_id)
23
+ end
24
+
25
+ def to_hash
26
+ {
27
+ :file_id => file_id,
28
+ :tag_id => tag_id
29
+ }
30
+ end
31
+
32
+ def self.new_from_hash(db, hash)
33
+ self.new(db,
34
+ hash[:file_id],
35
+ hash[:tag_id])
36
+ end
37
+
38
+ private
39
+
40
+ def self.file_tags_hashes(tags, files)
41
+ file_tags = tags.map do |tag|
42
+ files.map { |file| { :file_id => file.id, :tag_id => tag.id } }
43
+ end.flatten
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ module Taggata
2
+ module Persistent
3
+ class Tag < Abstract
4
+
5
+ attr_reader :db, :name, :id
6
+
7
+ def initialize(db, name, id = nil)
8
+ @db = db
9
+ @name = name
10
+ @id = id
11
+ end
12
+
13
+ def files
14
+ file_tags.map { |file_tag| file_tag.file }
15
+ end
16
+
17
+ def file_tags
18
+ FileTag.find(db, :tag_id => id)
19
+ end
20
+
21
+ def to_hash
22
+ {
23
+ :name => name,
24
+ :id => id
25
+ }
26
+ end
27
+
28
+ def self.new_from_hash(db, hash)
29
+ self.new(db,
30
+ hash[:name],
31
+ hash[:id])
32
+ end
33
+
34
+ def self.table
35
+ :taggata_tags
36
+ end
37
+
38
+ end
39
+ end
40
+ end