tmsu_file_db 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/README.md +142 -0
- data/lib/tmsu_file_db.rb +50 -9
- data/lib/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 910c386ec16296462c430627b29d00ce9c718874
|
4
|
+
data.tar.gz: 60c51e32d1ddd33291fe0cbc930b09dd18c6f706
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9f561e9efc1c6180264896d66d1bab28d8c8a5ac5b5b16c1f8ed1dd08c668e19f1b596e418ed7a0b288ebda99dfac566ff75066b834b7d50acaec78147f6b41
|
7
|
+
data.tar.gz: e81b583feb2a1bdadf8c433d38193c9f5965078a080c34e0a4c919c16e51e9e60e0bb12d016f200dc329d4aacd193840c9627e255c8d0518e4feeb83b1fc7683
|
data/README.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
This is an ORM similar to ActiveRecord, but uses the filesystem instead.
|
2
|
+
|
3
|
+
It uses TMSU which is a "non-hierarchical" filesystem tagging system.
|
4
|
+
|
5
|
+
Usage:
|
6
|
+
|
7
|
+
**Install the gem**
|
8
|
+
|
9
|
+
```sh
|
10
|
+
gem install tmsu_file_db
|
11
|
+
```
|
12
|
+
|
13
|
+
**Define a model**
|
14
|
+
|
15
|
+
```rb
|
16
|
+
require 'tmsu_file_db'
|
17
|
+
|
18
|
+
class User < TmsuModel
|
19
|
+
|
20
|
+
# this configure block is optional, it defaults to the current directory
|
21
|
+
configure root_path: "./db/users"
|
22
|
+
|
23
|
+
# Validations must return an array
|
24
|
+
validate do |record|
|
25
|
+
record.name.nil? ? ["name can't be blank"] : []
|
26
|
+
end
|
27
|
+
|
28
|
+
# Specific attributes can be validated as well
|
29
|
+
validate(:email) do |email, record|
|
30
|
+
email&.include?("@") ? ["email isn't valid"] : []
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
**Create instances**
|
37
|
+
|
38
|
+
# create, update, and delete
|
39
|
+
u = User.new name: "max"
|
40
|
+
u.valid? # => false
|
41
|
+
u.errors # => ["email isn't valid"]
|
42
|
+
u.save # => false
|
43
|
+
u.email = "maxpleaner@gmail.com"
|
44
|
+
u.valid? # => true
|
45
|
+
u.save # => true
|
46
|
+
u.update(email: "max.pleaner@gmail.com") # => true
|
47
|
+
u.update(email: "") # => false
|
48
|
+
|
49
|
+
# There are getter/setter methods for convenience
|
50
|
+
u.name # => "max"
|
51
|
+
u["name"] # => "max"
|
52
|
+
u[:name] # => "max"
|
53
|
+
u.name = "max p."
|
54
|
+
u[:name] # => "max p."
|
55
|
+
|
56
|
+
# All these getter/setters are working on 'attributes' under the hood.
|
57
|
+
u.attributes[:name] # => "max p."
|
58
|
+
|
59
|
+
# each record is assigned a filesystem path
|
60
|
+
u.path
|
61
|
+
|
62
|
+
# creating a new record will create a new file in the root_path
|
63
|
+
# but will not add anything to the file unless .write is called
|
64
|
+
u.write "hello"
|
65
|
+
|
66
|
+
# The content of the file is not part of the "attributes" i.e. name and email
|
67
|
+
# Those are stored using TMSU tags
|
68
|
+
u.tags # => { email: "max.pleaner@gmail.com", name: "max" }
|
69
|
+
|
70
|
+
# Attributes can be deleted
|
71
|
+
u.delete :name
|
72
|
+
u.tags # => { email: "max.pleaner@gmail.com" }
|
73
|
+
```
|
74
|
+
|
75
|
+
**Use class-level query methods**
|
76
|
+
|
77
|
+
_Note that this does not use Arel or any of that jazz. So chaining queries or using joins will not work._
|
78
|
+
|
79
|
+
_Note also that there is no `id` on models, only `path`, which is an absolute path._
|
80
|
+
|
81
|
+
```rb
|
82
|
+
User.where(name: "max p.")[0].name == "max p." # => true
|
83
|
+
User.find_by(name: "max p.").name == "max p." # => true
|
84
|
+
User.update_all(name: "max") # => true
|
85
|
+
User.all[0].name == "max" # => true
|
86
|
+
|
87
|
+
# You can make arbitrary queries using TMSU syntax
|
88
|
+
# e.g. select all users with email set that are not named melvin
|
89
|
+
User.query("name != 'melvin' and email")[0].name == "max" # => true
|
90
|
+
```
|
91
|
+
|
92
|
+
You can also skip `TmsuModel` and use `TmsuRuby.file` instead. This does _not_ handle creation / deletion of files. It should only be used with files that already exist.
|
93
|
+
|
94
|
+
Note that these methods are technically available on `TmsuModel` instances, callable on the `tmsu_file` attribute. But this shoudln't be done, because it will cause the in-memory attributes to be out of sync.
|
95
|
+
|
96
|
+
```rb
|
97
|
+
file_path = './my_pic.jpg' # this should already exist
|
98
|
+
|
99
|
+
tmsu_file = TmsuRuby.file file_path
|
100
|
+
tmsu_file.tags # => {}
|
101
|
+
|
102
|
+
tmsu_file.tag "foo" # .tag can be passed a string
|
103
|
+
tmsu_file.tags # => { foo: nil }
|
104
|
+
|
105
|
+
tmsu_file.untag "foo"
|
106
|
+
tmsu_file.tags # => { }
|
107
|
+
|
108
|
+
tmsu_file.tag ["foo", "bar"] # .tag can also be passed an array
|
109
|
+
tmsu_file.tags # => { foo: nil, bar: nil }
|
110
|
+
|
111
|
+
tmsu_file.tag(a: 1, b: 2) # .tag can also be passed a hash
|
112
|
+
tmsu_file.tags # => { foo: nil, bar: nil, a: 1, b: 2 }
|
113
|
+
```
|
114
|
+
|
115
|
+
It's also possible to use `TmsuRuby` to work on multiple files instead of just one:
|
116
|
+
|
117
|
+
```rb
|
118
|
+
glob_selector = "./**/*.jpg"
|
119
|
+
|
120
|
+
tmsu_file = TmsuRuby.file glob_selector
|
121
|
+
|
122
|
+
# there is a special method used to add tags in this case
|
123
|
+
tmsu_file.tag_selector "foo"
|
124
|
+
tmsu_file.tag_selector ["a", "b"]
|
125
|
+
tmsu_file.tag_selector c: 1, d: 2
|
126
|
+
|
127
|
+
# Simiarly to untag
|
128
|
+
tmsu_file.untag_selector "c"
|
129
|
+
|
130
|
+
# check that the tags were added to files
|
131
|
+
TmsuRuby.file("./my_pic.jpg").tags
|
132
|
+
# => { foo: nil, a: nil, b: nil, d: 2 }
|
133
|
+
```
|
134
|
+
|
135
|
+
Using `TmsuRuby.file` you can search by tag as well:
|
136
|
+
|
137
|
+
```rb
|
138
|
+
# Returns array of paths (files with the tag, systemwide)
|
139
|
+
TmsuRuby.file("name")
|
140
|
+
```
|
141
|
+
|
142
|
+
|
data/lib/tmsu_file_db.rb
CHANGED
@@ -18,7 +18,7 @@ class TmsuModel
|
|
18
18
|
|
19
19
|
def self.configure(root_path:)
|
20
20
|
Config[:root_path] = root_path || "./db".tap do |path|
|
21
|
-
`mkdir #{path}`
|
21
|
+
`mkdir -p #{path}`
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -51,16 +51,31 @@ class TmsuModel
|
|
51
51
|
query opts_to_query opts
|
52
52
|
end
|
53
53
|
|
54
|
+
def self.all
|
55
|
+
|
56
|
+
end
|
57
|
+
|
54
58
|
def self.query string
|
55
|
-
TmsuFile.new(query_glob).paths_query(query)
|
59
|
+
TmsuFile.new(query_glob).paths_query(query).map do |path|
|
60
|
+
new TmsuFile.new(path).tags
|
61
|
+
end
|
56
62
|
end
|
57
63
|
|
58
64
|
def self.update_all opts={}
|
59
|
-
Dir.glob(query_glob).each
|
65
|
+
Dir.glob(query_glob).each do |path|
|
66
|
+
errors = new(path).tap { |inst| inst.update(opts) }.errors
|
67
|
+
unless errors.empty?
|
68
|
+
raise(
|
69
|
+
ArgumentError, "couldn't update all. Path #{path} caused errors: #{errors.join(", ")}"
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
true
|
60
74
|
end
|
61
75
|
|
62
76
|
def self.destroy_all opts={}
|
63
77
|
Dir.glob(query_glob).each { |path| `rm #{path}` }
|
78
|
+
true
|
64
79
|
end
|
65
80
|
|
66
81
|
attr_reader :attributes, :errors, :path
|
@@ -92,7 +107,10 @@ class TmsuModel
|
|
92
107
|
end
|
93
108
|
|
94
109
|
def method_missing(sym, *arguments, &blk)
|
95
|
-
|
110
|
+
attr_name = sym.to_s[0..-1]
|
111
|
+
if sym.to_s[-1] == "=" && attributes.keys.include?(attr_name)
|
112
|
+
attributes[attr_name] = arguments[0]
|
113
|
+
elsif attributes.keys.include? sym
|
96
114
|
attributes[sym]
|
97
115
|
else
|
98
116
|
super
|
@@ -138,14 +156,22 @@ class TmsuModel
|
|
138
156
|
|
139
157
|
def save
|
140
158
|
ensure_persisted
|
159
|
+
return false unless valid?
|
141
160
|
tag attributes
|
142
|
-
|
161
|
+
true
|
143
162
|
end
|
144
163
|
|
145
164
|
def update attrs={}
|
165
|
+
original_attrs = attributes.clone
|
146
166
|
attrs.each_key { |k| self[k] = attrs[k] }
|
167
|
+
unless valid?
|
168
|
+
# rollback attribute change
|
169
|
+
self.attributes.clear
|
170
|
+
original_attrs.each { |k,v| self[k] = v }
|
171
|
+
return false
|
172
|
+
end
|
147
173
|
save
|
148
|
-
|
174
|
+
true
|
149
175
|
end
|
150
176
|
|
151
177
|
def destroy
|
@@ -153,6 +179,12 @@ class TmsuModel
|
|
153
179
|
self
|
154
180
|
end
|
155
181
|
|
182
|
+
def delete(attr)
|
183
|
+
untag(attr)
|
184
|
+
attributes.delete attr
|
185
|
+
attr
|
186
|
+
end
|
187
|
+
|
156
188
|
end
|
157
189
|
|
158
190
|
module SystemPatch
|
@@ -172,7 +204,7 @@ module TmsuRubyInitializer
|
|
172
204
|
puts "initializing tmsu"
|
173
205
|
puts system "tmsu init"
|
174
206
|
puts "making vfs_path #{vfs_path}"
|
175
|
-
puts system "mkdir #{vfs_path}"
|
207
|
+
puts system "mkdir -p #{vfs_path}"
|
176
208
|
puts "mounting vfs path"
|
177
209
|
puts system "tmsu mount #{vfs_path}"
|
178
210
|
end
|
@@ -186,7 +218,6 @@ module TmsuFileAPI
|
|
186
218
|
using SystemPatch
|
187
219
|
|
188
220
|
def tags
|
189
|
-
ensure_persisted
|
190
221
|
system("tmsu tags #{path}").split(" ")[1..-1].reduce({}) do |res, tag|
|
191
222
|
key, val = tag.split("=")
|
192
223
|
res.tap { res[key] = val }
|
@@ -202,7 +233,6 @@ module TmsuFileAPI
|
|
202
233
|
|
203
234
|
def untag tag_list
|
204
235
|
`touch #{path}` unless persisted?
|
205
|
-
attributes.delete
|
206
236
|
system "tmsu untag #{path} #{tag_list}"
|
207
237
|
tags
|
208
238
|
end
|
@@ -231,6 +261,17 @@ module TmsuFileAPI
|
|
231
261
|
files tag_obj
|
232
262
|
end
|
233
263
|
|
264
|
+
def untag_selector(tag_obj)
|
265
|
+
tag_arg = case tag_obj
|
266
|
+
when String
|
267
|
+
tag_obj
|
268
|
+
when Array
|
269
|
+
tag_obj.join(" ")
|
270
|
+
end
|
271
|
+
system "tmsu tag --tags '#{build_tag_arg tag_obj}' #{path}"
|
272
|
+
files tag_obj
|
273
|
+
end
|
274
|
+
|
234
275
|
def merge_tag(source, dest)
|
235
276
|
source_files = files source
|
236
277
|
dest_files = files dest
|
data/lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tmsu_file_db
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- maxpleaner
|
@@ -31,6 +31,7 @@ executables:
|
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
|
+
- README.md
|
34
35
|
- bin/tmsu_file_db
|
35
36
|
- lib/tmsu_file_db.rb
|
36
37
|
- lib/version.rb
|