poro 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
 
@@ -1,4 +1,5 @@
1
1
  # Require foundation.
2
+ require 'poro/version'
2
3
  require 'poro/util'
3
4
 
4
5
  # Require core classes and modules.
@@ -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 find_by_id. For
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| find_by_id(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 find_by_id(arg)
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(*first_or_all, &block)
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(*first_or_all, &block)
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 {|data| self.convert_to_plain_object(doc)}
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)
@@ -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
- - 0
9
- version: 0.1.0
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-18 00:00:00 -07:00
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
- - LICENSE
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 engines.
94
+ summary: A lightweight thin persistence engine that can utilize many different persistence data stores.
94
95
  test_files: []
95
96