storable 0.6.5 → 0.7.0

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.
Files changed (4) hide show
  1. data/CHANGES.txt +16 -2
  2. data/lib/storable.rb +105 -54
  3. data/storable.gemspec +1 -1
  4. metadata +2 -2
data/CHANGES.txt CHANGED
@@ -1,13 +1,27 @@
1
1
  STORABLE, CHANGES
2
2
 
3
- * TODO: field_types and field_names go out of order when defining some fields with types and others not. The quick fix is to define fields with no type at the end
3
+ * TODO: Handle nested hashes and arrays.
4
+ * TODO: to_xml, see: http://codeforpeople.com/lib/ruby/xx/xx-2.0.0/README
5
+
6
+ #### 0.7.0 (2010-04-03) #############################
7
+
8
+ * FIXED: Default initialize method missed the last argument
9
+ * FIXED: Possible error in from_hash when a value is nil
10
+ * CHANGE: Remove unused "require 'fileutils'"
11
+ * CHANGE: field_types is now a Hash
12
+ * CHANGE: Set the instance variable if a setter method doesn't exist.
13
+ * CHANGE: Will grab the first element of an Array if there is only one
14
+ and a field type is defined that is not an Array.
15
+ * ADDED: Storable.debug
16
+ * ADDED: Boolean class (candy for TrueClass fields)
17
+ * ADDED: Automatically convert a Proc to a string of its source in to_hash
18
+ * ADDED: Support for Symbol field type
4
19
 
5
20
 
6
21
  #### 0.6.5 (2010-03-23) #############################
7
22
 
8
23
  * ADDED: Use Yajl if it's available.
9
24
 
10
-
11
25
  #### 0.6.4 (2010-03-10) #############################
12
26
 
13
27
  * FIXED: Don't pull the first value out of an array if there is only one element.
data/lib/storable.rb CHANGED
@@ -4,8 +4,6 @@
4
4
  #++
5
5
 
6
6
 
7
- USE_ORDERED_HASH = (RUBY_VERSION =~ /^1.9/).nil?
8
-
9
7
  YAJL_LOADED = begin
10
8
  require 'yajl'
11
9
  true
@@ -24,19 +22,12 @@ require 'yaml'
24
22
  require 'fileutils'
25
23
  require 'time'
26
24
 
27
-
28
- class Storable
29
- module DefaultProcessors
30
- def hash_proc_processor
31
- Proc.new do |procs|
32
- a = {}
33
- procs.each_pair { |n,v|
34
- a[n] = (Proc === v) ? v.source : v
35
- }
36
- a
37
- end
38
- end
39
- end
25
+ unless defined?(Boolean)
26
+ # Used in field definitions.
27
+ #
28
+ # field :name => Boolean
29
+ #
30
+ class Boolean; end
40
31
  end
41
32
 
42
33
  # Storable makes data available in multiple formats and can
@@ -44,17 +35,18 @@ end
44
35
  # Storable.field method which tells Storable the order and
45
36
  # name.
46
37
  class Storable
47
- extend Storable::DefaultProcessors
48
-
38
+ USE_ORDERED_HASH = (RUBY_VERSION =~ /^1.9/).nil?
39
+ require 'proc_source'
49
40
  require 'storable/orderedhash' if USE_ORDERED_HASH
50
41
  unless defined?(SUPPORTED_FORMATS) # We can assume all are defined
51
- VERSION = "0.6.5"
42
+ VERSION = "0.7.0"
52
43
  NICE_TIME_FORMAT = "%Y-%m-%d@%H:%M:%S".freeze
53
44
  SUPPORTED_FORMATS = [:tsv, :csv, :yaml, :json, :s, :string].freeze
54
45
  end
55
46
 
47
+ @debug = false
56
48
  class << self
57
- attr_accessor :field_names, :field_types
49
+ attr_accessor :field_names, :field_types, :debug
58
50
  end
59
51
 
60
52
  # This value will be used as a default unless provided on-the-fly.
@@ -88,18 +80,25 @@ class Storable
88
80
  def self.field(args={}, &processor)
89
81
  # TODO: Examine casting from: http://codeforpeople.com/lib/ruby/fattr/fattr-1.0.3/
90
82
  args = {args => nil} unless args.kind_of?(Hash)
91
-
83
+
92
84
  args.each_pair do |m,t|
93
85
  self.field_names ||= []
94
- self.field_types ||= []
86
+ self.field_types ||= {}
95
87
  self.field_names << m
96
- self.field_types << t unless t.nil?
88
+ self.field_types[m] = t unless t.nil?
89
+
90
+ # This processor automatically converts a Proc object
91
+ # to a String of its source.
92
+ processor = proc_processor if t == Proc && processor.nil?
97
93
 
98
94
  unless processor.nil?
99
95
  define_method("_storable_processor_#{m}", &processor)
100
96
  end
101
97
 
102
- next if method_defined?(m) # don't refine the accessor methods
98
+ if method_defined?(m) # don't redefine the accessor methods
99
+ STDERR.puts "method exists: #{self}##{m}" if Storable.debug
100
+ next
101
+ end
103
102
 
104
103
  define_method(m) do instance_variable_get("@#{m}") end
105
104
  define_method("#{m}=") do |val|
@@ -119,7 +118,7 @@ class Storable
119
118
  # It's assumed that the order values matches the order
120
119
  def initialize(*args)
121
120
  (self.class.field_names || []).each_with_index do |n,index|
122
- break if (index+1) >= args.size
121
+ break if (index+1) > args.size
123
122
  self.send("#{n}=", args[index])
124
123
  end
125
124
  end
@@ -137,7 +136,7 @@ class Storable
137
136
  # Dump the object data to the given format.
138
137
  def dump(format=nil, with_titles=false)
139
138
  format &&= format.to_sym
140
- format ||= 's' # as in, to_s
139
+ format ||= :s # as in, to_s
141
140
  raise "Format not defined (#{format})" unless SUPPORTED_FORMATS.member?(format)
142
141
  send("to_#{format}")
143
142
  end
@@ -173,39 +172,44 @@ class Storable
173
172
 
174
173
  def from_hash(from={})
175
174
  fnames = field_names
176
- fnames.each_with_index do |key,index|
177
- stored_value = from[key] || from[key.to_s] # support for symbol keys and string keys
175
+ fnames.each_with_index do |fname,index|
176
+ ftype = field_types[fname]
177
+ value_orig = from[fname] || from[fname.to_s]
178
178
 
179
- # TODO: Correct this horrible implementation
180
- # (sorry, me. It's just one of those days.) -- circa 2008-09-15
179
+ next if value_orig.nil?
181
180
 
182
- if field_types[index] == Array
183
- value = Array === stored_value ? stored_value : [stored_value]
184
- elsif field_types[index].kind_of?(Hash)
185
- value = stored_value
186
- else
187
-
188
- value = stored_value
181
+ if ftype == String && value_orig.to_s.empty?
182
+ value = ''
183
+ elsif ftype == Array
184
+ value = Array === value_orig ? value_orig : [value_orig]
185
+ elsif ftype == Hash
186
+ value = value_orig
187
+ elsif !ftype.nil?
188
+ value_orig = value_orig.first if Array === value_orig && value_orig.size == 1
189
189
 
190
- if field_types[index] == Time
191
- value = Time.parse(value)
192
- elsif field_types[index] == DateTime
193
- value = DateTime.parse(value)
194
- elsif field_types[index] == TrueClass
195
- value = (value.to_s == "true")
196
- elsif field_types[index] == Float
197
- value = value.to_f
198
- elsif field_types[index] == Integer
199
- value = value.to_i
200
- elsif field_types[index].kind_of?(Storable) && stored_value.kind_of?(Hash)
201
- # I don't know why this is here so I'm going to raise an exception
202
- # and wait a while for an error in one of my other projects.
203
- #value = field_types[index].from_hash(stored_value)
204
- raise "Delano, delano, delano. Clean up Storable!"
190
+ if [Time, DateTime].member?(ftype)
191
+ value = ftype.parse(value_orig)
192
+ elsif [TrueClass, FalseClass, Boolean].member?(ftype)
193
+ value = (value_orig.to_s.upcase == "TRUE")
194
+ elsif ftype == Float
195
+ value = value_orig.to_f
196
+ elsif ftype == Integer
197
+ value = value_orig.to_i
198
+ elsif ftype == Symbol
199
+ value = value_orig.to_s.to_sym
200
+ elsif ftype == Proc && String === value_orig
201
+ value = Proc.from_string value_orig
205
202
  end
206
203
  end
204
+
205
+ value ||= value_orig
206
+
207
+ if self.respond_to?("#{fname}=")
208
+ self.send("#{fname}=", value)
209
+ else
210
+ self.instance_variable_set("@#{fname}", value)
211
+ end
207
212
 
208
- self.send("#{key}=", value) if self.respond_to?("#{key}=")
209
213
  end
210
214
 
211
215
  self.postprocess
@@ -229,7 +233,11 @@ class Storable
229
233
  def to_json(*from, &blk)
230
234
  hash = to_hash
231
235
  if YAJL_LOADED
232
- Yajl::Encoder.encode(hash)
236
+ ret = Yajl::Encoder.encode(hash)
237
+ #raise "DELANO"
238
+ #ret.force_encoding("ISO-8859-1")
239
+ #p [:to, ret.encoding.name] if ret.respond_to?(:encoding)
240
+ ret
233
241
  elsif JSON_LOADED
234
242
  hash.to_json(*from, &blk)
235
243
  else
@@ -262,8 +270,10 @@ class Storable
262
270
  # +from+ a YAML String or Array (split into by line).
263
271
  def self.from_json(*from)
264
272
  from_str = [from].flatten.compact.join('')
273
+ #from_str.force_encoding("ISO-8859-1")
274
+ #p [:from, from_str.encoding.name] if from_str.respond_to?(:encoding)
265
275
  if YAJL_LOADED
266
- tmp = Yajl::Parser.parse(from_str)
276
+ tmp = Yajl::Parser.parse(from_str, :check_utf8 => false)
267
277
  elsif JSON_LOADED
268
278
  tmp = JSON::load(from_str)
269
279
  else
@@ -367,3 +377,44 @@ class Storable
367
377
  end
368
378
 
369
379
 
380
+ class Storable
381
+ # These methods can be used by Storable objects as
382
+ # custom field processors.
383
+ #
384
+ # e.g.
385
+ #
386
+ # class A < Storable
387
+ # field :name => String, &hash_proc_processor
388
+ # end
389
+ #
390
+ module DefaultProcessors
391
+ # Replace a hash of Proc objects with a hash
392
+ # of
393
+ def hash_proc_processor
394
+ Proc.new do |procs|
395
+ a = {}
396
+ procs.each_pair { |n,v|
397
+ a[n] = (Proc === v) ? v.source : v
398
+ }
399
+ a
400
+ end
401
+ end
402
+ def proc_processor
403
+ Proc.new do |val|
404
+ (Proc === val) ? val.source : val
405
+ end
406
+ end
407
+ # If the object already has a value for +@id+
408
+ # use it, otherwise return the current digest.
409
+ #
410
+ # This allows an object to have a preset ID.
411
+ #
412
+ def gibbler_id_processor
413
+ Proc.new do |val|
414
+ @id || self.gibbler
415
+ end
416
+ end
417
+ end
418
+ extend Storable::DefaultProcessors
419
+ end
420
+
data/storable.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  @spec = Gem::Specification.new do |s|
2
2
  s.name = "storable"
3
3
  s.rubyforge_project = "storable"
4
- s.version = "0.6.5"
4
+ s.version = "0.7.0"
5
5
  s.summary = "Storable: Marshal Ruby classes into and out of multiple formats (yaml, json, csv, tsv)"
6
6
  s.description = s.summary
7
7
  s.author = "Delano Mandelbaum"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: storable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-03-23 00:00:00 -04:00
12
+ date: 2010-04-03 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15