taggata 0.0.2 → 0.1.0

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.
@@ -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