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