storable 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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