epo 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -43,14 +43,14 @@ $ tree db/
43
43
  db/
44
44
  └── user
45
45
  ├── peter
46
- | ├── resource-default.json [1a]
46
+ | ├── resource-epo-default.json [1a]
47
47
  | └── picture.png [1b]
48
48
  ├── jon
49
- | ├── resource-default.json [2a]
49
+ | ├── resource-epo-default.json [2a]
50
50
  | ├── picture.png [2b]
51
51
  | └── pubkey.rsa.txt [2c]
52
52
  └── marc
53
- ├── resource-default.json [3a]
53
+ ├── resource-epo-default.json [3a]
54
54
  ├── picture.png [3b]
55
55
  └── thumbnail.dat [3c]
56
56
 
@@ -150,7 +150,22 @@ Iterates on all the DB items (as observations)
150
150
 
151
151
  welo >= 0.1.0
152
152
  derailleur >= 0.5.0
153
- a JSON library if you use json formatting
153
+ a JSON library if you use json formatting (recommended)
154
+
155
+ == Benchmark
156
+ Config:
157
+ * macbook pro (2010), without SSD:
158
+ * ruby 1.9.2
159
+ * json 1.5.1
160
+
161
+ $ ruby example/benchmark.rb
162
+ 0.770000 1.020000 1.790000 ( 4.563546)
163
+ 5935
164
+ 1.160000 0.420000 1.580000 ( 1.601897)
165
+
166
+ So, roughly 5 seconds to write 6000 records, and 2 seconds to read them back.
167
+ No sync operation nor cache flush was forced in between the results. That's maybe why
168
+ we can read back only 5935 records out of 6000.
154
169
 
155
170
  == License
156
171
 
data/Rakefile CHANGED
@@ -31,7 +31,7 @@ spec = Gem::Specification.new do |s|
31
31
  s.executables = []
32
32
  s.has_rdoc = true
33
33
 
34
- s.add_dependency('derailleur', '>= 0.0.5')
34
+ s.add_dependency('derailleur', '>= 0.0.6')
35
35
  s.add_dependency('welo', '>= 0.1.0')
36
36
  end
37
37
 
data/TODO CHANGED
@@ -1,9 +1,2 @@
1
- * delete record/observation
2
- * maybe with some modifications to Derailleur:
3
- - graceful not found case
4
- - clever pruning when finding:
5
- - when path is a directory and there is no node, prune
6
- - when path is a directory but there is a node, continue
7
- - cache observation structs somewhere in the db or in the observer
8
- * fiber-friendly batch operations
1
+ * nesting/epitheting resources
9
2
  * register on inotify events
data/lib/epo/core/db.rb CHANGED
@@ -39,7 +39,7 @@ module EPO
39
39
  # Registers a model on a derailleur node at its path_model
40
40
  # Does not modify self.models
41
41
  def build_route_for_model(model)
42
- path = model.path_model(identifying_sym) #XXX allow to change the db identifying name
42
+ path = File.join(model.path_model(identifying_sym), ':filename')
43
43
  node = build_route(path)
44
44
  node.content = model
45
45
  end
@@ -75,20 +75,79 @@ module EPO
75
75
  extensions.find{|ext| ext == File.extname(path)}
76
76
  end
77
77
 
78
+ # Returns the main name of a path, i.e., the basename without
79
+ # the extension
80
+ # * main_name('/a/b/c/foo.bar') #=> 'foo'
81
+ def main_name(path)
82
+ File.basename(path).sub(/\.[^\.]+$/,'')
83
+ end
84
+
85
+ def understands_filename?(path)
86
+ understands_ext?(path) and
87
+ understands_main_name?(path)
88
+ end
89
+
90
+ def understands_main_name?(path)
91
+ main_name(path) =~ resource_regexp
92
+ end
93
+
94
+ RESOURCE_REGEXP = %r{resource-epo-\w+}
95
+
96
+ def resource_regexp
97
+ RESOURCE_REGEXP
98
+ end
99
+
78
100
  # Returns the perspective name and extension name (a 2 items array)
79
101
  # for the given path.
80
102
  # By default the blobs for resources content are stored in files named
81
103
  # 'resource-<persp><ext>' with ext starting with a dot
82
104
  def persp_and_ext_for_basename(path)
83
- base = File.basename(path).sub('resource-','').sub(/\.[^\.]+$/,'')
84
- [base, File.extname(path)]
105
+ [main_name(path).sub('resource-epo-',''), File.extname(path)]
85
106
  end
86
107
 
87
108
  # Returns the basename of a resource blob for a perspective named persp and
88
109
  # in a format with extension ext (including the leading dot).
89
110
  # see also persp_and_ext_for_basename
90
111
  def basename_for_persp_and_ext(persp, ext)
91
- "resource-#{persp}#{ext}"
112
+ "resource-epo-#{persp}#{ext}"
113
+ end
114
+
115
+ # Deletes a resource by removing its identifying directory. Beware because
116
+ # this method doesn't care about other files which may coexist in the same
117
+ # filesystem branch.
118
+ def delete_completely(root, resource)
119
+ path = File.join(root, resource.path(identifying_sym))
120
+ FileUtils.rm_rf(path)
121
+
122
+ # removes the empty directories upward
123
+ (resource.identifiers(identifying_sym).size - 1).times do |t|
124
+ path = File.split(path).first
125
+ begin
126
+ FileUtils.rmdir(path)
127
+ rescue Errno::ENOTEMPTY
128
+ break
129
+ end
130
+ end
131
+ end
132
+
133
+ def delete(root, resource)
134
+ remaining_path = File.join(root, resource.path(identifying_sym))
135
+ # removes the resource-epo-* files
136
+ Dir.entries(remaining_path).each do |e|
137
+ if e.start_with?('resource-epo-')
138
+ FileUtils.rm(File.join(remaining_path, e))
139
+ end
140
+ end
141
+
142
+ # removes the empty directories upward
143
+ resource.identifiers(identifying_sym).size.times do |t|
144
+ begin
145
+ Dir.rmdir(remaining_path)
146
+ remaining_path = File.split(remaining_path).first
147
+ rescue Errno::ENOTEMPTY
148
+ break
149
+ end
150
+ end
92
151
  end
93
152
 
94
153
  # Saves one or more resource at the filesystem path given at root
@@ -171,7 +230,7 @@ module EPO
171
230
  # Iterates on every resource found and understood in the filesystem
172
231
  # directory root.
173
232
  # If no block is given, returns an iterator.
174
- def each_resource(root)
233
+ def each_resource_observation(root)
175
234
  if block_given?
176
235
  xp = observer
177
236
  models.each do |model|
@@ -181,7 +240,7 @@ module EPO
181
240
  end
182
241
  xp.read_tree(root)
183
242
  else
184
- Enumerator.new(self, :each_resource, root)
243
+ Enumerator.new(self, :each_resource_observation, root)
185
244
  end
186
245
  end
187
246
 
@@ -21,6 +21,9 @@ module EPO
21
21
  # The DB able to tell if a path is understandable or not
22
22
  attr_accessor :db
23
23
 
24
+ # A cache for the observation structures
25
+ attr_accessor :structures
26
+
24
27
  # Creates and return a new observer from a DB.
25
28
  # It will take the models from the DB.
26
29
  def self.for_db(db)
@@ -34,35 +37,26 @@ module EPO
34
37
  def initialize(models=[])
35
38
  super(models)
36
39
  @db = DB.new(models)
40
+ @structures = {}
37
41
  register(:observation) do |o|
38
42
  dispatch(o)
39
43
  end
40
44
  end
41
45
 
42
- # Read a single path, the root is the part of the path corresponding
43
- # to where on the filesystem the database is rooted.
44
- # e.g. for a path in a photo collection:
45
- # /home/crapooze/project/foobar/db/photo/1
46
- # the root is likely to be:
47
- # /home/crapooze/project/foobar/db
48
- # If a successful observation happens, then it will call the relevant hooks.
49
- def read_path(path, root=nil)
50
- full_dirname, base = File.split(path)
51
- dirname = if root
52
- full_dirname.sub(root,'')
53
- else
54
- full_dirname
55
- end
56
- #XXX may raise an exception for unknown path, we should rescue this/use a
57
- #silent method and test for nil
58
- node = db.get_route(dirname)
46
+ def get_node_for_path(path, root=nil)
47
+ path = path.sub(root,'') if root
48
+ db.get_route_silent(path)
49
+ end
59
50
 
51
+ def read_path_as_resource(path, model)
60
52
  persp_str = db.persp_and_ext_for_basename(path).first
61
- persp = node.content.perspectives.keys.find{|k| k.to_s == persp_str}
62
- #XXX no need to create this many time, should cache it
63
- st = Welo::ObservationStruct.new_for_resource_in_perspective(node.content, persp)
64
- source = Source.new(db, path)
65
- observe_source(source, st)
53
+ persp = model.perspectives.keys.find{|k| k.to_s == persp_str}
54
+ observe_source(Source.new(db, path), structure(model, persp))
55
+ end
56
+
57
+ def structure(model, persp)
58
+ pair = [model, persp]
59
+ @structures[pair] ||= Welo::ObservationStruct.new_for_resource_in_perspective(model, persp)
66
60
  end
67
61
 
68
62
  # Recursively reads the files in the filesystem (with Find).
@@ -70,10 +64,15 @@ module EPO
70
64
  # Currently, there is no pruning, or control possible.
71
65
  def read_tree(root)
72
66
  Find.find(root) do |path|
73
- if File.directory?(path)
74
- #XXX maybe prune the branch if valid but has no content
75
- elsif db.understands_ext?(path)
76
- read_path(path, root)
67
+ node = get_node_for_path(path, root)
68
+ if node
69
+ if node.content #a model attached to a file in a directory
70
+ if db.understands_filename?(path)
71
+ read_path_as_resource(path, node.content)
72
+ end
73
+ end
74
+ else #the db doesn't understand this branch, we'd rather drop now
75
+ Find.prune
77
76
  end
78
77
  end
79
78
  end
data/lib/epo.rb CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  module EPO
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  AUTHORS = ["crapooze"]
5
5
  WEBSITE = "https://github.com/crapooze/epo"
6
6
  LICENCE = "MIT"
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - crapooze
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-03-19 00:00:00 -04:00
17
+ date: 2011-03-27 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -28,8 +28,8 @@ dependencies:
28
28
  segments:
29
29
  - 0
30
30
  - 0
31
- - 5
32
- version: 0.0.5
31
+ - 6
32
+ version: 0.0.6
33
33
  type: :runtime
34
34
  version_requirements: *id001
35
35
  - !ruby/object:Gem::Dependency