poro 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +25 -0
- data/lib/poro.rb +1 -0
- data/lib/poro/context.rb +5 -5
- data/lib/poro/context_factories/single_store/mongo_factory.rb +1 -1
- data/lib/poro/contexts/mongo_context.rb +76 -3
- data/lib/poro/version.rb +7 -0
- metadata +6 -5
data/README.rdoc
CHANGED
@@ -22,6 +22,11 @@ through whatever inferior homogenized API Poro may try to provide.
|
|
22
22
|
|
23
23
|
= Installation
|
24
24
|
|
25
|
+
At this time, Poro has only ever been tested on ruby 1.9.2. It is expected to
|
26
|
+
work on any 1.9.x, but may require some effort to make work on 1.8.6 or later.
|
27
|
+
By version 1.0 I hope to have 1.8.6 and 1.8.7 support, however the development
|
28
|
+
of a production worthy gem in 1.9.2 takes the priority.
|
29
|
+
|
25
30
|
Basic usage only requires the installation of the gem:
|
26
31
|
gem install poro
|
27
32
|
However to utilize any meaningful persistence data store, the underlying gems
|
@@ -155,6 +160,13 @@ mailto:jeff@paploo.net
|
|
155
160
|
|
156
161
|
= Version History
|
157
162
|
|
163
|
+
[0.1.1 - 2010-Sep-24] Minor Additions and Bug Fixes.
|
164
|
+
* MongoContext now can optionally encode Symbols as hashes
|
165
|
+
or just leave them as strings.
|
166
|
+
* MongoContext can have conversion to BSON::ObjectId turned off.
|
167
|
+
* MongoContext can save Sets in various formats.
|
168
|
+
* MongoContext handles namespaced models better.
|
169
|
+
* Context doesn't error when trying to find by id.
|
158
170
|
[0.1.0 - 2010-Sep-18] Initial public release.
|
159
171
|
* Major base functionality is complete, though is subject
|
160
172
|
to big changes as it is used in the real world.
|
@@ -167,7 +179,18 @@ mailto:jeff@paploo.net
|
|
167
179
|
|
168
180
|
The following are the primary TODO items, roughly in priority order:
|
169
181
|
|
182
|
+
* YAML Connection Configuration:
|
183
|
+
* Make a Util module that is able to use a rails-compatible YAML
|
184
|
+
file--given by path--to get the elements needed for configuration of a
|
185
|
+
SingleStore factory.
|
186
|
+
* Modify SingleStore to use this file for configuration when appropriate.
|
170
187
|
* Modelify: Break into modules for each piece of functionality.
|
188
|
+
* Modelify: Add callback functionality to Modelify (e.g. before/after save, after initialize).
|
189
|
+
* Add the callbacks to the Context first.
|
190
|
+
* Mongo Context: Add option to encode Sets as one of:
|
191
|
+
* A Set with the raw internal Hash.
|
192
|
+
* A Set with the internal Hash as an Array.
|
193
|
+
* An Array (which will have to be manually turned back into a Set).
|
171
194
|
* Specs: Add specs for Context Find methods.
|
172
195
|
* Check that private methods are private. (Should do on subclasses too.)
|
173
196
|
* Check that the two main find methods pass through to the correct underlying
|
@@ -176,6 +199,8 @@ The following are the primary TODO items, roughly in priority order:
|
|
176
199
|
* Mongo Context: Split into modules in separate files.
|
177
200
|
* Context: Split out modules into files.
|
178
201
|
* Contexts: Add SQL Context.
|
202
|
+
* Ruby: Verify support for ruby 1.9.x.
|
203
|
+
* Ruby: Evaluate adding support for ruby 1.8.6 and 1.8.7.
|
179
204
|
|
180
205
|
= License
|
181
206
|
|
data/lib/poro.rb
CHANGED
data/lib/poro/context.rb
CHANGED
@@ -316,7 +316,7 @@ module Poro
|
|
316
316
|
#
|
317
317
|
# Subclasses SHOULD override this method.
|
318
318
|
#
|
319
|
-
# By default, this method aggregates separate calls to
|
319
|
+
# By default, this method aggregates separate calls to fetch. For
|
320
320
|
# most data stores this makes N calls to the server, decreasing performance.
|
321
321
|
#
|
322
322
|
# When possible, this method should be overriden by subclasses to be more
|
@@ -324,7 +324,7 @@ module Poro
|
|
324
324
|
# filtered by the <tt>clean_id</tt> private method.
|
325
325
|
def find_with_ids(*ids)
|
326
326
|
ids = ids.flatten
|
327
|
-
return ids.map {|id|
|
327
|
+
return ids.map {|id| fetch(id)}
|
328
328
|
end
|
329
329
|
|
330
330
|
end
|
@@ -423,7 +423,7 @@ module Poro
|
|
423
423
|
elsif( arg.respond_to?(:map) )
|
424
424
|
return find_with_ids(arg)
|
425
425
|
else
|
426
|
-
return
|
426
|
+
return fetch(arg)
|
427
427
|
end
|
428
428
|
end
|
429
429
|
|
@@ -440,9 +440,9 @@ module Poro
|
|
440
440
|
# * :first or :one
|
441
441
|
def data_store_find(first_or_all, *args, &block)
|
442
442
|
if(first_or_all == :all || first_or_all == :many)
|
443
|
-
return data_store_find_all(*
|
443
|
+
return data_store_find_all(*args, &block)
|
444
444
|
elsif( first_or_all == :first || first_or_all == :one)
|
445
|
-
return data_store_find_first(*
|
445
|
+
return data_store_find_first(*args, &block)
|
446
446
|
else
|
447
447
|
raise ArgumentError, "#{__method__} expects the first argument to be one of :all, :many, :first, or :one."
|
448
448
|
end
|
@@ -22,7 +22,7 @@ module Poro
|
|
22
22
|
raise ArgumentError, "No mongo connection was supplied to #{self.class.name}." if @connection.nil?
|
23
23
|
|
24
24
|
super() do |klass|
|
25
|
-
collection_name = Util::Inflector.pluralize(Util::Inflector.underscore(klass.name.to_s))
|
25
|
+
collection_name = Util::Inflector.pluralize(Util::Inflector.underscore(klass.name.to_s)).gsub('/', '_')
|
26
26
|
context = Contexts::MongoContext.new(klass)
|
27
27
|
context.data_store = @connection[collection_name]
|
28
28
|
yield(klass, context) if block_given?
|
@@ -45,6 +45,12 @@ module Poro
|
|
45
45
|
@persistent_attributes_whitelist = nil
|
46
46
|
@persistent_attributes_blacklist = nil
|
47
47
|
|
48
|
+
# Some configuration variables
|
49
|
+
@encode_symbols = false
|
50
|
+
@attempt_id_conversion = true
|
51
|
+
@set_encoding_method = :embedded_array
|
52
|
+
|
53
|
+
|
48
54
|
# Initialize
|
49
55
|
super(klass)
|
50
56
|
end
|
@@ -62,6 +68,27 @@ module Poro
|
|
62
68
|
attr_reader :persistent_attributes_blacklist
|
63
69
|
attr_writer :persistent_attributes_blacklist
|
64
70
|
|
71
|
+
# If true, it encodes Symbol as a hash with a class name property, and
|
72
|
+
# then decodes them back into Symbol. If false, it converts them to
|
73
|
+
# String for storage. Defaults to false.
|
74
|
+
#
|
75
|
+
# While one would think they want to preserve the type of the saved element,
|
76
|
+
# the change in storage method makes it harder to write Mongo queries on the
|
77
|
+
# data. Thus it is normally best to save symbols as strings for storage.
|
78
|
+
attr_reader :encode_symbols
|
79
|
+
attr_writer :encode_symbols
|
80
|
+
|
81
|
+
# Normally, one uses BSON::ObjectId instances for the IDs on a stored
|
82
|
+
# Mongo object. However, one can design a database to use many different
|
83
|
+
# values as sthe primary key.
|
84
|
+
#
|
85
|
+
# When this is set to true, fetch tries to convert the given ID into a
|
86
|
+
# BSON::ObjectId before doing a fetch. If the conversion fails, it tries using
|
87
|
+
# the raw value given. If this is set to false, then it always passes
|
88
|
+
# along the raw value, skipping the conversion step.
|
89
|
+
attr_reader :attempt_id_conversion
|
90
|
+
attr_writer :attempt_id_conversion
|
91
|
+
|
65
92
|
def fetch(id)
|
66
93
|
data = data_store.find_one( clean_id(id) )
|
67
94
|
return convert_to_plain_object(data)
|
@@ -94,9 +121,8 @@ module Poro
|
|
94
121
|
private
|
95
122
|
|
96
123
|
def clean_id(id)
|
97
|
-
#TODO: Make the attempted transform configurable; mongo doesn't require an ObjectId as the key.
|
98
124
|
# Attempt to convert to an ObjectID if it looks like it should be.
|
99
|
-
if( !(id.kind_of?(BSON::ObjectId)) && BSON::ObjectId.legal?(id.to_s) )
|
125
|
+
if( self.attempt_id_conversion && !(id.kind_of?(BSON::ObjectId)) && BSON::ObjectId.legal?(id.to_s) )
|
100
126
|
id = BSON::ObjectId.from_string(id.to_s)
|
101
127
|
end
|
102
128
|
return id
|
@@ -123,6 +149,7 @@ module Poro
|
|
123
149
|
obj.kind_of?(Float) ||
|
124
150
|
obj.kind_of?(String) ||
|
125
151
|
obj.kind_of?(Time) ||
|
152
|
+
(self.encode_symbols && obj.kind_of?(Symbol)) ||
|
126
153
|
obj==true ||
|
127
154
|
obj==false ||
|
128
155
|
obj.nil? ||
|
@@ -178,6 +205,10 @@ module Poro
|
|
178
205
|
return encode_array(obj)
|
179
206
|
elsif( obj.kind_of?(Class) )
|
180
207
|
return encode_class(obj)
|
208
|
+
elsif( obj.kind_of?(Set) )
|
209
|
+
return encode_set(obj)
|
210
|
+
elsif( !self.encode_symbols && obj.kind_of?(Symbol) )
|
211
|
+
return encode_symbol(obj)
|
181
212
|
elsif( Context.managed_class?(obj.class) && Context.fetch(obj.class).kind_of?(self.class) )
|
182
213
|
return encode_mongo_managed_object(obj)
|
183
214
|
elsif( Context.managed_class?(obj.class))
|
@@ -207,6 +238,23 @@ module Poro
|
|
207
238
|
return {'_class_name' => klass.class, 'name' => klass.name}
|
208
239
|
end
|
209
240
|
|
241
|
+
# Encodes a symbol.
|
242
|
+
def encode_symbol(sym)
|
243
|
+
return {'_class_name' => 'Symbol', 'value' => sym.to_s}
|
244
|
+
end
|
245
|
+
|
246
|
+
# Encodes a Set as either :raw, :embedded_array, :array.
|
247
|
+
def encode_set(set)
|
248
|
+
method = @set_encoding_method
|
249
|
+
if( method == :raw )
|
250
|
+
return encode_unmanaged_object(set)
|
251
|
+
elsif( method == :embedded_array )
|
252
|
+
return {'_class_name' => 'Set', 'values' => self.convert_to_data(set.to_a, :embedded => true)}
|
253
|
+
else
|
254
|
+
return self.convert_to_data(set.to_a, :embedded => true)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
210
258
|
# Encode a hash that came from a DBRef dereferenced and decoded by this context.
|
211
259
|
#
|
212
260
|
# This will save the hash when its owning object is saved!
|
@@ -294,6 +342,10 @@ module Poro
|
|
294
342
|
class_name = data['_class_name'].to_s
|
295
343
|
if( class_name == 'Class' )
|
296
344
|
return decode_class(data)
|
345
|
+
elsif( class_name == 'Symbol' )
|
346
|
+
return decode_symbol(data)
|
347
|
+
elsif( class_name == 'Set' )
|
348
|
+
return decode_set(data)
|
297
349
|
elsif( class_name == self.klass.to_s )
|
298
350
|
return decode_self_managed_object(data)
|
299
351
|
elsif( class_name && data['managed'] )
|
@@ -321,6 +373,27 @@ module Poro
|
|
321
373
|
return Util::ModuleFinder.find(class_data['name'])
|
322
374
|
end
|
323
375
|
|
376
|
+
# Decode a symbol reference. If this users of the Context expect a Symbol
|
377
|
+
# to be encoded as a Symbol, then decode it as a Symbol. Otherwise the
|
378
|
+
# users of the Context wil be expecting a String.
|
379
|
+
def decode_symbol(symbol_data)
|
380
|
+
if self.encode_symbols
|
381
|
+
return symbol_data['value'].to_sym
|
382
|
+
else
|
383
|
+
return symbol_data['value'].to_s
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# Decode the set depending on if it was encoded as an array or as a raw
|
388
|
+
# object.
|
389
|
+
def decode_set(set_data)
|
390
|
+
if( set_data.include?['values'] )
|
391
|
+
return Set.new(set_data['values'])
|
392
|
+
else
|
393
|
+
return decode_unmanaged_object(set_data)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
324
397
|
# Decode a BSON::DBRef. If there is a context for the reference, it is
|
325
398
|
# wrapped in that object type. If there is no context, it is left as
|
326
399
|
# a DBRef so that it will re-save as a DBRef (otherwise it'll save as a
|
@@ -438,7 +511,7 @@ module Poro
|
|
438
511
|
end
|
439
512
|
|
440
513
|
def data_store_find_all(*args, &block)
|
441
|
-
return data_store.find(*args, &block).to_a.map {|
|
514
|
+
return data_store.find(*args, &block).to_a.map {|doc| self.convert_to_plain_object(doc)}
|
442
515
|
end
|
443
516
|
|
444
517
|
def data_store_find_first(*args, &block)
|
data/lib/poro/version.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# Poro is a lightweight persistence engine that can use many different data stores
|
2
|
+
# to persist information. Poro takes a hands-off approach, attempting to change
|
3
|
+
# existing plain ol' ruby objects as little as possible. For more information
|
4
|
+
# see README.rdoc.
|
5
|
+
module Poro
|
6
|
+
VERSION = '0.1.1'
|
7
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Jeff Reinecke
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-09-
|
17
|
+
date: 2010-09-24 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -47,6 +47,7 @@ files:
|
|
47
47
|
- lib/poro/util/inflector.rb
|
48
48
|
- lib/poro/util/module_finder.rb
|
49
49
|
- lib/poro/util.rb
|
50
|
+
- lib/poro/version.rb
|
50
51
|
- lib/poro.rb
|
51
52
|
- spec/context_factory_spec.rb
|
52
53
|
- spec/context_spec.rb
|
@@ -60,7 +61,7 @@ files:
|
|
60
61
|
has_rdoc: true
|
61
62
|
homepage: http://www.github.com/paploo/poro
|
62
63
|
licenses:
|
63
|
-
-
|
64
|
+
- BSD
|
64
65
|
post_install_message:
|
65
66
|
rdoc_options: []
|
66
67
|
|
@@ -90,6 +91,6 @@ rubyforge_project:
|
|
90
91
|
rubygems_version: 1.3.7
|
91
92
|
signing_key:
|
92
93
|
specification_version: 3
|
93
|
-
summary: A lightweight thin persistence engine that can utilize many different persistence
|
94
|
+
summary: A lightweight thin persistence engine that can utilize many different persistence data stores.
|
94
95
|
test_files: []
|
95
96
|
|