sander6-daijobu 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,33 +13,61 @@ Make a new, inappropriately-named Daijobu::Client object and give it your store
13
13
 
14
14
  You can now use the regular hash getter and setter methods ([] and []=) without worrying about serialization.
15
15
 
16
- The supported schemes are :marshal (using Marshal), :json (using JSON from the json gem), :yaml (using YAML), :eval (using Kernel::eval), and :raw (using nothing).
16
+ The out-of-the-box schemes are :marshal (using Marshal), :json (using JSON from the json gem), :yaml (using YAML), :eval (using Kernel::eval), and :raw (using nothing).
17
+
18
+ You can specify your own schemes by making new Daijobu::Parser objects. Just instantiate a new Parser and tell it how you want to parse and unparse.
19
+
20
+ parser = Daijobu::Parser.new({
21
+ :parse => Proc.new { |str| str.deserialize },
22
+ :unparse => Proc.new { |obj| obj.serialize }
23
+ })
24
+
25
+ If you don't like passing procs as keyword arguments, you can give a block and define the #parse and #unparse methods yourself.
26
+
27
+ parser = Daijobu::Parser.new do
28
+ def parse(str)
29
+ str.deserialize_using_some_fancy_method
30
+ end
31
+ def unparse(obj)
32
+ obj.serialize_using_some_fancy_method
33
+ end
34
+ end
35
+
36
+ Alternately, you can make a new Parser with a delegate object, and specify which methods of the delegate should be used for parsing and unparsing. For example, the :marshal parser is this:
37
+
38
+ parser = Daijobu::Parser(Marshal, :parse => :load, :unparse => :dump)
39
+
40
+ Then just pass this parser object as your scheme.
41
+
42
+ daijobu = Daijobu::Client.new(rufus, :schemes => parser)
17
43
 
18
44
  Because some schemes are very strict, and sometimes what gets dumped can't be parsed back (for example, unparsing an integer using JSON), you can specify a set of schemes. If one doesn't work, it'll try the next in the list until it succeeds in parsing.
19
45
 
20
46
  Daijobu::Client.new(rufus, :schemes => [:json, :yaml, :eval])
21
47
 
22
- Daijobu doesn't do any real format-checking; if, say, Marshal.load throws an error, for whatever reason, it'll just rescue and move on to the next scheme. The success of this gem all hinges on the fact that the supported serialization formats don't have much to do with one another (except for JSON and YAML, of course), and you shouldn't ever end up parsing Ruby code as JSON accidentally, or at least not in unintended ways.
48
+ Daijobu doesn't do any real format-checking; if, say, Marshal.load throws an error, for whatever reason, it'll just rescue and move on to the next scheme. Therefore, if you define your own parser and want to use fallbacks, the #parse and #unparse methods should raise some kind of error (besides a Daijobu::Error) if they fail.
23
49
 
24
50
  You can set different read and write schemes, too. This would be useful, say, if you wanted to change the serialization format.
25
51
 
26
52
  Daijobu::Client.new(rufus, :read => :json, :write => :raw)
27
53
 
28
- One last cool thing that I threw in just because: the key names in a key-value store are often namespaced. You can call these namespaces as methods on a Client object to automatically prepend that namespace to any keys asked for.
54
+ One last cool thing that I threw in just because: the key names in a key-value store are often namespaced, so you can call these namespaces as methods on a Client object to automatically prepend that namespace to any keys asked for.
29
55
 
30
- daijobu = Daijobu::Client.new(rufus, :read => :json, :write => :raw)
31
56
  daijobu.namespace['123'] # => looks for 'namespace:123'
32
57
  daijobu.name.space['123'] # => looks for 'name:space:123'
33
58
 
34
- You can set the separator (defaults to ':' because that's what I was using) on the Daijobu::NamespaceProxy class.
59
+ You can set the separator (defaults to ':' because that's what I was using) on the Daijobu::Client class.
35
60
 
36
- Daijobu::NamespaceProxy.default_separator = '/'
37
- daijobu.name.space['123'] # => looks for 'name/space/123'
61
+ Daijobu::Client.default_separator = '/'
62
+ daijobu.name.space['123'] # => 'name/space/123'
38
63
 
39
- And a last bit of syntatical sugar: you can leave off the brackets when asking for namespaced keys.
64
+ Or you can set it per-invocation:
40
65
 
41
- daijobu.namespace '123' # => looks for 'namespace:123'
42
- daijobu.name.space '123' # => looks for 'name:space:123'
66
+ daijobu.name('/').space(':')['123'] # => 'name/space:123'
67
+
68
+ If this is the case, it'll keep using the last separator mentioned for subsequent namespacing.
69
+
70
+ daijobu.some('/').long(':').name.space['123'] # => 'some/long:name:space:123'
43
71
 
44
72
  === "Anything else I should know?"
45
73
 
@@ -53,8 +81,6 @@ I plan to eventually switch out my home-rolled adapters for a more complete and
53
81
 
54
82
  Until then, I'm thinking of putting in support for user-defined adapters as a stopgap, basically any object that responds to #get(key) and #set(key, value).
55
83
 
56
- Less of a priority is support for user-defined serialization schemes that respond to #parse(string) and #unparse(object).
57
-
58
84
  I also plan to take a look at ActiveRecord::Base#serialize to see if I can do something with that, because reading and writing YAML is like dying every day.
59
85
 
60
86
  === "What's with the name?"
@@ -1,14 +1,8 @@
1
1
  $:.unshift(File.dirname(__FILE__))
2
2
  require 'daijobu/errors'
3
3
  require 'daijobu/client'
4
- require 'daijobu/namespace_proxy'
4
+ require 'daijobu/parser'
5
5
  require 'daijobu/scheme_set'
6
- require 'daijobu/scheme'
7
- require 'daijobu/schemes/eval'
8
- require 'daijobu/schemes/json'
9
- require 'daijobu/schemes/marshal'
10
- require 'daijobu/schemes/raw'
11
- require 'daijobu/schemes/yaml'
12
6
  require 'daijobu/adapter'
13
7
  require 'daijobu/adapters/mem_cache'
14
8
  require 'daijobu/adapters/tokyo_cabinet'
@@ -3,6 +3,18 @@ module Daijobu
3
3
  # The unfortunately-named Daijobu::Client is the serialization wrapper for key-value stores.
4
4
  class Client
5
5
 
6
+ @@default_separator = ':'
7
+
8
+ # Getter for the default separator. Default is ':'
9
+ def self.default_separator
10
+ @@default_separator
11
+ end
12
+
13
+ # Setter for the default separator. I use ':', but a lot of people like '/'.
14
+ def self.default_separator=(separator)
15
+ @@default_separator = separator
16
+ end
17
+
6
18
  # Client.new takes a key-value store as its first argument, and then a hash of serialization schemes.
7
19
  #
8
20
  # Options:
@@ -13,6 +25,8 @@ module Daijobu
13
25
  @adapter = Daijobu::Adapter.get(casket)
14
26
  @read_schemes = Daijobu::SchemeSet.new(options[:schemes] || options[:read])
15
27
  @write_schemes = Daijobu::SchemeSet.new(options[:schemes] || options[:write])
28
+ @namespace = nil
29
+ @separator = nil
16
30
  end
17
31
 
18
32
  # Getter for keys. The actual getting method is handled by the specific Adapter object.
@@ -26,20 +40,26 @@ module Daijobu
26
40
  @adapter.set(key.to_s, __unparse__(value))
27
41
  end
28
42
 
29
- # Any missing method is assumed to be a namespace for keys to get. The NamespaceProxy object
30
- # returned also implements the same kind of method_missing, so namespaces can be chained together.
43
+ # Any missing method is assumed to be a namespace for keys to get. Returns self, so
44
+ # namespaces can be chained together.
45
+ #
46
+ # client.namespace['key'] # => gets key 'namespace:key'
47
+ # client.name.space['key'] # => gets key 'name:space:key'
48
+ # client.namespace['key'] = 'value' # => sets key 'namespace:key'
31
49
  #
32
- # client.namespace['key'] # => gets key 'namespace:key'
33
- # client.name.space['key'] # => gets key 'name:space:key'
34
- # client.namespace['key'] = 'value' # => sets key 'namespace:key'
50
+ # If you have mixed separators in your keys, or don't want to fiddle around with the
51
+ # class-level default_separator, you can pass the separator to use as an argument to
52
+ # any missing method.
35
53
  #
36
- # As an added bit of syntactic sugar, you can leave the brackets off when getting keys this way.
54
+ # client.name('/').space('-')['key'] # => gets key 'name/space-key'
37
55
  #
38
- # client.namespace 'key' # => same as client.namespace['key']
56
+ # The last separator mentioned will be use for subsequent separating.
39
57
  #
40
- # See NamespaceProxy for more details about setting the separator.
41
- def method_missing(name, *args)
42
- args.empty? ? Daijobu::NamespaceProxy.new(self, name) : Daijobu::NamespaceProxy.new(self, name)[*args]
58
+ # client.some('/').really(':').long.name('-').space['key'] # => gets key 'some/really:long:name-space-key'
59
+ def method_missing(namespace, separator = nil, *args, &block)
60
+ @namespace = "#{@namespace}#{@separator}#{namespace}"
61
+ @separator = separator || @separator || @@default_separator
62
+ self
43
63
  end
44
64
 
45
65
  private
@@ -14,4 +14,8 @@ module Daijobu
14
14
  # Raised when all of the (un)parsing schemes fail.
15
15
  class NoFallbackScheme < Daijobu::Error; end
16
16
 
17
+ class MissingDelegate < Daijobu::Error; end
18
+
19
+ class InvalidStrategy < Daijobu::Error; end
20
+
17
21
  end
@@ -0,0 +1,126 @@
1
+ module Daijobu
2
+
3
+ # A Daijobu::Parser handles serialization and deserialization of strings and objects.
4
+ # It can either send methods to a delegate object that serializes (for example, the
5
+ # Marshal modules), or define its own schemes.
6
+ class Parser
7
+
8
+ # Returns a new Parser from a fixed default set designed to handle the most common
9
+ # serialization schemes. Pass the name of the Parser you want as a symbol.
10
+ #
11
+ # :marshal => a parser for Ruby binary format, using the Marshal module
12
+ # :json => a parser for JSON, using the JSON module (requires the json gem)
13
+ # :yaml => a parser for YAML, using the YAML module
14
+ # :eval => a parser for Ruby code, using #eval and #inspect
15
+ # :raw => a parser for raw strings that doesn't actually do anything
16
+ def self.get(name)
17
+ respond_to?(name) ? __send__(name) : raise(UnknownScheme)
18
+ end
19
+
20
+ # Returns a new Parser for serializing Ruby binary.
21
+ #
22
+ # :parse => Marshal.load(string)
23
+ # :unparse => Marshal.dump(object)
24
+ def self.marshal
25
+ new(Marshal, :parse => :load, :unparse => :dump)
26
+ end
27
+
28
+ # Returns a new Parser for serializing JSON.
29
+ #
30
+ # :parse => JSON.parse(string)
31
+ # :unparse => JSON.unparse(object)
32
+ def self.json
33
+ require 'json'
34
+ new(JSON, :parse => :parse, :unparse => :unparse)
35
+ end
36
+
37
+ # Returns a new Parser for serializing YAML.
38
+ #
39
+ # :parse => YAML.load(string)
40
+ # :unparse => YAML.dump(object)
41
+ def self.yaml
42
+ require 'yaml'
43
+ new(YAML, :parse => :load, :unparse => :dump)
44
+ end
45
+
46
+ # Returns a new Parser for serializing Ruby code.
47
+ #
48
+ # :parse => Kernel.eval(string)
49
+ # :unparse => object.inspect
50
+ def self.eval
51
+ new(:parse => Proc.new { |str| Kernel::eval(str) }, :unparse => Proc.new { |obj| obj.inspect })
52
+ end
53
+
54
+ # Returns a new Parser that does nothing.
55
+ #
56
+ # :parse => string
57
+ # :unparse => object
58
+ def self.raw
59
+ new(:parse => Proc.new { |str| str }, :unparse => Proc.new { |obj| obj })
60
+ end
61
+
62
+ # Takes a delegate object an a hash specifying which methods of the delegate should be used for
63
+ # parsing and unparsing. For example, say there was some module called Parsley that had methods
64
+ # #load and #dump for deserialization and serialization, respectively. To make a new parser
65
+ # using Parsley, you would say
66
+ #
67
+ # parser = Daijobu::Parser.new(Parsely, :parse => :load, :unparse => :dump)
68
+ #
69
+ # Alternately, you can pass procs as the parse and unparse keys. The parse proc should accept a
70
+ # String, and the unparse proc should accept any Object. For fallback schemes to work, these procs
71
+ # should raise any error (except a Daijobu::Error) if they fail in any way. This isn't strictly
72
+ # necessary if you're only using one serialization scheme.
73
+ #
74
+ # parser = Daijobu::Parser.new({
75
+ # :parse => Proc.new { |string| string.do_something_fancy },
76
+ # :unparse => Proc.new { |object| object.undo_something_fancy }
77
+ # })
78
+ #
79
+ # Or, if you don't like passing procs as keyword arguments (I sure don't), you can give a block
80
+ # and define the parse and unparse methods (and technically anything else you want) on the
81
+ # parser object directly.
82
+ #
83
+ # parser = Daijobu::Parser.new do
84
+ # def parse(string)
85
+ # raise SomeKindOfError unless string.passes_some_conditions?
86
+ # string.turn_into_an_object_somehow
87
+ # end
88
+ # def uparse(object)
89
+ # raise ADifferentKindOfError unless object.can_be_dumped?
90
+ # object.dump_to_string_using_some_clever_method
91
+ # end
92
+ # end
93
+ def initialize(*args, &block)
94
+ strategies = args.last.is_a?(Hash) ? args.pop : {}
95
+ @delegate = args.shift
96
+ @parse_strategy = strategies[:parse]
97
+ @unparse_strategy = strategies[:unparse]
98
+ (class << self; self; end).class_eval(&block) if block_given?
99
+ end
100
+
101
+ # Parses the string. Parsing nil always returns nil.
102
+ def parse(str)
103
+ str.nil? ? nil : strategize(@parse_strategy, str)
104
+ end
105
+
106
+ # Unparses the object.
107
+ def unparse(obj)
108
+ strategize(@unparse_strategy, obj)
109
+ end
110
+
111
+ private
112
+
113
+ # Either sends the symbol to the delegate, or calls the proc for the given strategy.
114
+ def strategize(strategy, arg)
115
+ case strategy
116
+ when Symbol
117
+ raise MissingDelegate unless @delegate
118
+ @delegate.__send__(strategy, arg)
119
+ when Proc
120
+ strategy.call(arg)
121
+ else
122
+ raise InvalidStrategy
123
+ end
124
+ end
125
+ end
126
+ end
@@ -7,12 +7,15 @@ module Daijobu
7
7
 
8
8
  attr_reader :current
9
9
 
10
- # SchemeSet.new takes a single symbol or array of symbols and initializes a new Scheme
10
+ # SchemeSet.new takes a single symbol or array of symbols and initializes a new Parser
11
11
  # object of the appropriate type for each one.
12
12
  def initialize(schemes = nil)
13
+ @schemes = []
13
14
  schemes = Array(schemes)
14
15
  schemes = DEFAULT if schemes.empty?
15
- @schemes = schemes.collect { |scheme| Daijobu::Scheme.get(scheme) }
16
+ schemes.each do |scheme|
17
+ @schemes << (scheme.is_a?(Daijobu::Parser) ? scheme : Daijobu::Parser.get(scheme))
18
+ end
16
19
  @current = 0
17
20
  end
18
21
 
@@ -34,7 +37,7 @@ module Daijobu
34
37
  @current = 0
35
38
  end
36
39
 
37
- # Tries the parse (load) the string with each scheme in turn.
40
+ # Tries to parse (load) the string with each scheme in turn.
38
41
  # Assumes (defensibly) that parsing failed if any non Daijobu::Error exceptions are raised
39
42
  # and moves on to the next scheme.
40
43
  def parse(str)
@@ -51,7 +54,7 @@ module Daijobu
51
54
  obj
52
55
  end
53
56
 
54
- # Tries the unparse (dump) the string with each scheme in turn.
57
+ # Tries to unparse (dump) the object with each scheme in turn.
55
58
  # Assumes (defensibly) that unparsing failed if any non Daijobu::Error exceptions are raised
56
59
  # and moves on to the next scheme.
57
60
  def unparse(obj)
@@ -105,22 +105,29 @@ describe Daijobu::Client do
105
105
  end
106
106
  end
107
107
 
108
- describe "method_missing" do
108
+ describe "namespacing via missing methods" do
109
109
 
110
- describe "with no arguments" do
111
- it "should return a namespace proxy with namespace as the missing method" do
112
- proxy = @daijobu.prefix
113
- proxy.should be_an_instance_of(Daijobu::NamespaceProxy)
114
- proxy.instance_variable_get(:@namespace).should == 'prefix'
115
- end
110
+ it "should return self" do
111
+ @daijobu.namespace.should == @daijobu
112
+ end
113
+
114
+ it "should append the method name as a key namespace" do
115
+ @daijobu.namespace.instance_variable_get(:@namespace).should == 'namespace'
116
+ end
117
+
118
+ it "should chain namespaces together for multiple invocations" do
119
+ @daijobu.name.space.instance_variable_get(:@namespace).should == 'name:space'
116
120
  end
117
121
 
118
122
  describe "with arguments" do
119
- it "should call #[] on the proxy with the arguments" do
120
- proxy = stub('fake-daijobu-proxy!')
121
- Daijobu::NamespaceProxy.expects(:new).with(@daijobu, :prefix).returns(proxy)
122
- proxy.expects(:[]).with('key')
123
- @daijobu.prefix('key')
123
+ it "should set the separator to the given argument" do
124
+ @daijobu.namespace('/').instance_variable_get(:@separator).should == '/'
125
+ end
126
+
127
+ it "should use the last mentioned separator for subsequent namespacing" do
128
+ namespacey = @daijobu.some('/').long.name('-').space
129
+ namespacey.instance_variable_get(:@namespace).should == 'some/long/name-space'
130
+ namespacey.instance_variable_get(:@separator).should == '-'
124
131
  end
125
132
  end
126
133
  end
@@ -0,0 +1,211 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Daijobu::Parser do
4
+
5
+ before do
6
+ @string = '{ "serialized" : "structure" }'
7
+ @object = Object.new
8
+ end
9
+
10
+ describe ".get" do
11
+ it "should return a Daijobu::Parser object" do
12
+ Daijobu::Parser.get(:marshal).should be_an_instance_of(Daijobu::Parser)
13
+ end
14
+
15
+ it "should raise an error when passed a name it doesn't know about" do
16
+ lambda { Daijobu::Parser.get(:buckwild) }.should raise_error(Daijobu::UnknownScheme)
17
+ end
18
+
19
+ it "should send the given name as a method to itself" do
20
+ Daijobu::Parser.stubs(:respond_to?).returns(true)
21
+ Daijobu::Parser.expects(:buckwild)
22
+ Daijobu::Parser.get(:buckwild)
23
+ end
24
+
25
+ describe ".marshal" do
26
+ it "should return a new parser that delegates parsing as :load and unparsing as :dump to Marshal" do
27
+ Daijobu::Parser.expects(:new).with(Marshal, { :parse => :load, :unparse => :dump })
28
+ Daijobu::Parser.marshal
29
+ end
30
+ end
31
+
32
+ describe ".json" do
33
+ it "should return a new parser that delegates parsing as :parse and unparsing as :unparse to JSON" do
34
+ Daijobu::Parser.expects(:new).with(JSON, { :parse => :parse, :unparse => :unparse })
35
+ Daijobu::Parser.json
36
+ end
37
+ end
38
+
39
+ describe ".yaml" do
40
+ it "should return a new parser that delegates parsing as :load and unparsing as :dump to YAML" do
41
+ Daijobu::Parser.expects(:new).with(YAML, { :parse => :load, :unparse => :dump })
42
+ Daijobu::Parser.yaml
43
+ end
44
+ end
45
+
46
+ describe ".eval" do
47
+ it "should return a new parser that delegates parsing and unparsing as some procs" do
48
+ Daijobu::Parser.expects(:new).with(instance_of(Hash))
49
+ Daijobu::Parser.eval
50
+ end
51
+
52
+ describe "should return a parser that" do
53
+ before do
54
+ @parser = Daijobu::Parser.eval
55
+ end
56
+
57
+ it "should eval the string given to parse" do
58
+ Kernel.expects(:eval).with(@string)
59
+ @parser.parse(@string)
60
+ end
61
+
62
+ it "should inspect the object to unparse" do
63
+ @object.expects(:inspect)
64
+ @parser.unparse(@object)
65
+ end
66
+ end
67
+ end
68
+
69
+ describe ".raw" do
70
+ it "should return a new parser that delegates parsing and unparsing as some procs" do
71
+ Daijobu::Parser.expects(:new).with(instance_of(Hash))
72
+ Daijobu::Parser.raw
73
+ end
74
+
75
+ describe "should return a parser that" do
76
+ before do
77
+ @parser = Daijobu::Parser.raw
78
+ end
79
+
80
+ it "should return the string given to parse" do
81
+ @parser.parse(@string).should == @string
82
+ end
83
+
84
+ it "should return the object given to unparse" do
85
+ @parser.unparse(@object).should == @object
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "parsing" do
92
+ it "should return nil when trying to parse nil" do
93
+ @parser = Daijobu::Parser.new
94
+ @parser.parse(nil).should be_nil
95
+ end
96
+
97
+ describe "with a symbol strategy" do
98
+ before do
99
+ @symbol = :load
100
+ end
101
+
102
+ describe "without a delegate" do
103
+ before do
104
+ @parser = Daijobu::Parser.new(:parse => @symbol)
105
+ @parser.instance_variable_get(:@parse_strategy).should == @symbol
106
+ end
107
+
108
+ it "should raise an error" do
109
+ lambda { @parser.parse(@string) }.should raise_error(Daijobu::MissingDelegate)
110
+ end
111
+ end
112
+
113
+ describe "with a delegate" do
114
+ before do
115
+ @delegate = Object.new
116
+ @parser = Daijobu::Parser.new(@delegate, :parse => @symbol)
117
+ end
118
+
119
+ it "should send the symbol and the argument to the delegate" do
120
+ @delegate.expects(@symbol).with(@string)
121
+ @parser.parse(@string)
122
+ end
123
+ end
124
+ end
125
+
126
+ describe "with a proc strategy" do
127
+ before do
128
+ @proc = Proc.new { |string| string.downcase }
129
+ @parser = Daijobu::Parser.new(:parse => @proc)
130
+ end
131
+
132
+ it "should call the proc with the argument" do
133
+ @proc.expects(:call).with(@string)
134
+ @parser.parse(@string)
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "unparsing" do
140
+ describe "with a symbol strategy" do
141
+ before do
142
+ @symbol = :dump
143
+ end
144
+
145
+ describe "without a delegate" do
146
+ before do
147
+ @parser = Daijobu::Parser.new(:unparse => @symbol)
148
+ end
149
+
150
+ it "should raise an error" do
151
+ lambda { @parser.unparse(@object) }.should raise_error(Daijobu::MissingDelegate)
152
+ end
153
+ end
154
+
155
+ describe "with a delegate" do
156
+ before do
157
+ @delegate = Object.new
158
+ @parser = Daijobu::Parser.new(@delegate, :unparse => @symbol)
159
+ end
160
+
161
+ it "should send the symbol and the argument to the delegate" do
162
+ @delegate.expects(@symbol).with(@object)
163
+ @parser.unparse(@object)
164
+ end
165
+ end
166
+ end
167
+
168
+ describe "with a proc strategy" do
169
+ before do
170
+ @proc = Proc.new { |object| object.inspect }
171
+ @parser = Daijobu::Parser.new(:unparse => @proc)
172
+ end
173
+
174
+ it "should call the proc with the argument" do
175
+ @proc.expects(:call).with(@string)
176
+ @parser.unparse(@string)
177
+ end
178
+ end
179
+ end
180
+
181
+ describe "when given a block" do
182
+ before do
183
+ @parser = Daijobu::Parser.new do
184
+ def parse(str)
185
+ eval(str)
186
+ end
187
+
188
+ def unparse(obj)
189
+ obj.inspect
190
+ end
191
+
192
+ def test
193
+ "Make sure this shows up."
194
+ end
195
+ end
196
+ end
197
+
198
+ it "should add the defined methods to the parser instance" do
199
+ @parser.should respond_to(:parse)
200
+ @parser.should respond_to(:unparse)
201
+ @parser.should respond_to(:test)
202
+ end
203
+
204
+ it "should redefine the parse or unparse methods as defined" do
205
+ @parser.expects(:eval).with(@string)
206
+ @parser.parse(@string)
207
+ @object.expects(:inspect)
208
+ @parser.unparse(@object)
209
+ end
210
+ end
211
+ end
@@ -2,7 +2,17 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe Daijobu::SchemeSet do
4
4
 
5
- describe "initialization" do
5
+ describe "on initialization" do
6
+ it "should have the default set of schemes" do
7
+ Daijobu::Parser.expects(:get).with(:marshal)
8
+ Daijobu::Parser.expects(:get).with(:json)
9
+ Daijobu::Parser.expects(:get).with(:yaml)
10
+ Daijobu::Parser.expects(:get).with(:eval)
11
+ Daijobu::SchemeSet.new
12
+ end
13
+ end
14
+
15
+ describe "initialization" do
6
16
  before do
7
17
  @scheme_set = Daijobu::SchemeSet.new
8
18
  end
@@ -10,15 +20,7 @@ describe Daijobu::SchemeSet do
10
20
  it "should set current to 0" do
11
21
  @scheme_set.current.should == 0
12
22
  end
13
-
14
- it "should have the default set of schemes" do
15
- schemes = @scheme_set.instance_variable_get(:@schemes)
16
- schemes[0].should be_an_instance_of(Daijobu::Scheme::Marshal)
17
- schemes[1].should be_an_instance_of(Daijobu::Scheme::JSON)
18
- schemes[2].should be_an_instance_of(Daijobu::Scheme::YAML)
19
- schemes[3].should be_an_instance_of(Daijobu::Scheme::Eval)
20
- end
21
-
23
+
22
24
  it "should accept an array of schemes" do
23
25
  lambda { Daijobu::SchemeSet.new([:json, :yaml]) }.should_not raise_error
24
26
  end
@@ -35,8 +37,6 @@ describe Daijobu::SchemeSet do
35
37
 
36
38
  it "should have the proper schemes in the given order" do
37
39
  schemes = @scheme_set.instance_variable_get(:@schemes)
38
- schemes[0].should be_an_instance_of(Daijobu::Scheme::Eval)
39
- schemes[1].should be_an_instance_of(Daijobu::Scheme::YAML)
40
40
  end
41
41
  end
42
42
 
@@ -96,7 +96,7 @@ describe Daijobu::SchemeSet do
96
96
 
97
97
  describe "assuming that the string can be parsed" do
98
98
  before do
99
- Daijobu::Scheme::JSON.any_instance.stubs(:parse).returns(@hashy)
99
+ Daijobu::Parser.any_instance.stubs(:parse).returns(@hashy)
100
100
  end
101
101
 
102
102
  it "should reset" do
@@ -111,7 +111,7 @@ describe Daijobu::SchemeSet do
111
111
 
112
112
  describe "assuming that the string can't be parsed" do
113
113
  before do
114
- Daijobu::Scheme::JSON.any_instance.stubs(:parse).raises(Daijobu::Error)
114
+ Daijobu::Parser.any_instance.stubs(:parse).raises(Daijobu::Error)
115
115
  end
116
116
 
117
117
  it "should raise the given error" do
@@ -150,7 +150,7 @@ describe Daijobu::SchemeSet do
150
150
 
151
151
  describe "assuming that the object can be unparsed" do
152
152
  before do
153
- Daijobu::Scheme::JSON.any_instance.stubs(:unparse).returns(@stringy)
153
+ Daijobu::Parser.any_instance.stubs(:unparse).returns(@stringy)
154
154
  end
155
155
 
156
156
  it "should return the unparsed entity" do
@@ -165,7 +165,7 @@ describe Daijobu::SchemeSet do
165
165
 
166
166
  describe "assuming that the object can't be unparsed" do
167
167
  before do
168
- Daijobu::Scheme::JSON.any_instance.stubs(:unparse).raises(Daijobu::Error)
168
+ Daijobu::Parser.any_instance.stubs(:unparse).raises(Daijobu::Error)
169
169
  end
170
170
 
171
171
  it "should raise the given error" do
@@ -7,6 +7,8 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
7
  require 'daijobu'
8
8
 
9
9
  # Some fake contants for the classes and stuff referred to.
10
+ YAML = Module.new unless defined?(YAML)
11
+ JSON = Module.new unless defined?(JSON)
10
12
  MemCache = Class.new unless defined?(MemCache)
11
13
  Rufus = Module.new unless defined?(Rufus)
12
14
  Rufus::Tokyo = Module.new unless defined?(Rufus::Tokyo)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sander6-daijobu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sander Hartlage
@@ -30,17 +30,11 @@ files:
30
30
  - lib/daijobu/adapter.rb
31
31
  - lib/daijobu/client.rb
32
32
  - lib/daijobu/errors.rb
33
- - lib/daijobu/namespace_proxy.rb
34
- - lib/daijobu/scheme.rb
33
+ - lib/daijobu/parser.rb
35
34
  - lib/daijobu/scheme_set.rb
36
35
  - lib/daijobu/adapters/mem_cache.rb
37
36
  - lib/daijobu/adapters/tokyo_cabinet.rb
38
37
  - lib/daijobu/adapters/tokyo_tyrant.rb
39
- - lib/daijobu/schemes/eval.rb
40
- - lib/daijobu/schemes/json.rb
41
- - lib/daijobu/schemes/marshal.rb
42
- - lib/daijobu/schemes/raw.rb
43
- - lib/daijobu/schemes/yaml.rb
44
38
  has_rdoc: false
45
39
  homepage: http://github.com/sander6/daijobu
46
40
  post_install_message:
@@ -74,12 +68,6 @@ test_files:
74
68
  - spec/daijobu/adapters/tokyo_tyrant_spec.rb
75
69
  - spec/daijobu/client_spec.rb
76
70
  - spec/daijobu/errors_spec.rb
77
- - spec/daijobu/namespace_proxy_spec.rb
71
+ - spec/daijobu/parser_spec.rb
78
72
  - spec/daijobu/scheme_set_spec.rb
79
- - spec/daijobu/scheme_spec.rb
80
- - spec/daijobu/schemes/eval_spec.rb
81
- - spec/daijobu/schemes/json_spec.rb
82
- - spec/daijobu/schemes/marshal_spec.rb
83
- - spec/daijobu/schemes/raw_spec.rb
84
- - spec/daijobu/schemes/yaml_spec.rb
85
73
  - spec/spec_helper.rb
@@ -1,44 +0,0 @@
1
- module Daijobu
2
- class NamespaceProxy
3
-
4
- @@default_separator = ':'
5
-
6
- # Getter for the default separator. Default is ':'
7
- def self.default_separator
8
- @@default_separator
9
- end
10
-
11
- # Setter for the default separator. I use ':', but a lot of people like '/'.
12
- def self.default_separator=(separator)
13
- @@default_separator = separator
14
- end
15
-
16
- # NamespaceProxy.new takes an owner (a Daijobu::Client), a namespace, and a separator (defaults
17
- # to @@default_separator).
18
- # NamespaceProxy objects are typically created using Daijobu::Client#method_missing, so you're
19
- # rarely going to instantiate one of these on your own.
20
- def initialize(owner, namespace, separator = @@default_separator)
21
- @owner = owner
22
- @namespace = namespace.to_s
23
- @separator = separator
24
- end
25
-
26
- # Sends #[] back to the owner, prepending the key given with the namespace and separator.
27
- def [](key)
28
- @owner["#{@namespace}#{@separator}#{key}"]
29
- end
30
-
31
- # Sends #[]= back to the owner, prepending the key given with the namespace and separator.
32
- def []=(key, value)
33
- @owner["#{@namespace}#{@separator}#{key}"] = value
34
- end
35
-
36
- # Any missing method is assumed to be yet another namespace.
37
- def method_missing(namespace, *args)
38
- separator = args.shift || @@default_separator
39
- @namespace = "#{@namespace}#{@separator}#{namespace}"
40
- @separator = separator
41
- self
42
- end
43
- end
44
- end
@@ -1,33 +0,0 @@
1
- module Daijobu
2
-
3
- # The Scheme module is the parent of the various serialization schemes.
4
- module Scheme
5
-
6
- # Given a name, returns a new instance of the corresponding scheme.
7
- #
8
- # :marshal => Daijobu::Scheme::Marshal
9
- # :json => Daijobu::Scheme::JSON
10
- # :yaml => Daijobu::Scheme::YAML
11
- # :eval => Daijobu::Scheme::Eval
12
- # :raw => Daijobu::Scheme::Raw
13
- #
14
- # Raises Daijobu::UnknownScheme if given a name it can't handle.
15
- def self.get(name)
16
- case name
17
- when :marshal
18
- Daijobu::Scheme::Marshal.new
19
- when :json
20
- Daijobu::Scheme::JSON.new
21
- when :yaml
22
- Daijobu::Scheme::YAML.new
23
- when :eval
24
- Daijobu::Scheme::Eval.new
25
- when :raw
26
- Daijobu::Scheme::Raw.new
27
- else
28
- raise Daijobu::UnknownScheme
29
- end
30
- end
31
-
32
- end
33
- end
@@ -1,21 +0,0 @@
1
- module Daijobu
2
- module Scheme
3
-
4
- # Daijobu::Scheme::Eval is the serialization for pure Ruby code.
5
- # Theoretically, then, anything could be put into and taken out of a key-value-store,
6
- # provided that they're always rehydrated into an appropriate binding.
7
- class Eval
8
-
9
- # Parses by #eval'ing the string.
10
- def parse(str)
11
- str.nil? ? nil : eval(str)
12
- end
13
-
14
- # Unparses by #inspect'ing the object.
15
- def unparse(obj)
16
- obj.inspect
17
- end
18
-
19
- end
20
- end
21
- end
@@ -1,26 +0,0 @@
1
- require 'json'
2
-
3
- module Daijobu
4
- module Scheme
5
-
6
- # Daijobu::Scheme::JSON is the serialization for JSON.
7
- # Uses the native (C) json gem, which is respectably fast.
8
- class JSON
9
-
10
- # Parses the string using JSON.parse.
11
- # JSON is pretty strict, and it dies whenever the object doesn't have an enclosing
12
- # structure (i.e. an array or tuple). You might have problems parsing bare strings,
13
- # integers, booleans, and nulls. It's weird, though, because JSON doesn't seem to have
14
- # a problem unparsing these things. Just a heads-up.
15
- def parse(str)
16
- str.nil? ? nil : ::JSON.parse(str)
17
- end
18
-
19
- # Unparses the object using JSON.unparse.
20
- def unparse(obj)
21
- ::JSON.unparse(obj)
22
- end
23
-
24
- end
25
- end
26
- end
@@ -1,19 +0,0 @@
1
- module Daijobu
2
- module Scheme
3
-
4
- # Daijobu::Scheme::Marshal is the serialization for Ruby binary code.
5
- class Marshal
6
-
7
- # Parses the string using Marshal.load.
8
- def parse(str)
9
- str.nil? ? nil : ::Marshal.load(str)
10
- end
11
-
12
- # Unparses the object using Marshal.dump.
13
- def unparse(obj)
14
- ::Marshal.dump(obj)
15
- end
16
-
17
- end
18
- end
19
- end
@@ -1,21 +0,0 @@
1
- module Daijobu
2
- module Scheme
3
-
4
- # Daijobu::Scheme::Raw is the serialization for nothing. Seriously, it doesn't do anything.
5
- # It's rather dubious why you'd use this scheme for reading (if you're just going to be reading
6
- # as raw, why use this gem at all?), but writing raw is useful sometimes.
7
- class Raw
8
-
9
- # Doesn't parse anything. Just returns the string.
10
- def parse(str)
11
- str
12
- end
13
-
14
- # Doesn't unparse anything. Just returns the object.
15
- def unparse(obj)
16
- obj
17
- end
18
-
19
- end
20
- end
21
- end
@@ -1,25 +0,0 @@
1
- require 'yaml'
2
-
3
- module Daijobu
4
- module Scheme
5
-
6
- # Daijobu::Scheme::YAML is the serialization for YAML.
7
- # Due to the strictness of the JSON module, you'll often have more luck parsing JSON containing
8
- # bare objects (strings, integers, booleans, etc. without an enclosing structure) using YAML
9
- # than with JSON, but YAML's a lot slower. That being said, it makes a good fallback scheme to use
10
- # when JSON starts dying.
11
- class YAML
12
-
13
- # Parses the string using YAML.load.
14
- def parse(str)
15
- str.nil? ? nil : ::YAML.load(str)
16
- end
17
-
18
- # Unparses the object using YAML.dump.
19
- def unparse(obj)
20
- ::YAML.dump(obj)
21
- end
22
-
23
- end
24
- end
25
- end
@@ -1,60 +0,0 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
2
-
3
- describe Daijobu::NamespaceProxy do
4
-
5
- before do
6
- @daijobu = stub('fake-daijobu-client!')
7
- end
8
-
9
- describe "initialization" do
10
-
11
- it "should set the owner as the given owner" do
12
- Daijobu::NamespaceProxy.new(@daijobu, 'prefix').instance_variable_get(:@owner).should == @daijobu
13
- end
14
-
15
- it "should set the namespace as the given namespace" do
16
- Daijobu::NamespaceProxy.new(@daijobu, 'prefix').instance_variable_get(:@namespace).should == 'prefix'
17
- end
18
-
19
- it "should be indifferent to strings or symbols as the namespace" do
20
- Daijobu::NamespaceProxy.new(@daijobu, :prefix).instance_variable_get(:@namespace).should == 'prefix'
21
- end
22
-
23
- it "should initialize with the class-level default separator" do
24
- Daijobu::NamespaceProxy.new(@daijobu, 'prefix').instance_variable_get(:@separator).should == Daijobu::NamespaceProxy.default_separator
25
- end
26
-
27
- it "should set the separator as the given separator" do
28
- Daijobu::NamespaceProxy.new(@daijobu, 'prefix', '/').instance_variable_get(:@separator).should == '/'
29
- end
30
-
31
- end
32
-
33
- describe "methods" do
34
- before do
35
- @proxy = Daijobu::NamespaceProxy.new(@daijobu, 'prefix')
36
- end
37
-
38
- describe "#[]" do
39
- it "should send #[] with the namespaced key to the owner" do
40
- @daijobu.expects(:[]).with('prefix:key')
41
- @proxy['key']
42
- end
43
- end
44
-
45
- describe "#[]=" do
46
- it "should send #[]= with the namespaced key to the owner" do
47
- @daijobu.expects(:[]=).with('prefix:key', 'value')
48
- @proxy['key'] = 'value'
49
- end
50
- end
51
-
52
- describe "method_missing" do
53
- it "should return a new proxy with compounded namespace" do
54
- new_proxy = @proxy.another_prefix
55
- new_proxy.should be_an_instance_of(Daijobu::NamespaceProxy)
56
- new_proxy.instance_variable_get(:@namespace).should == 'prefix:another_prefix'
57
- end
58
- end
59
- end
60
- end
@@ -1,39 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe Daijobu::Scheme do
4
-
5
- describe ".get" do
6
-
7
- describe "with :marshal" do
8
- it "should return a marshal scheme" do
9
- Daijobu::Scheme.get(:marshal).should be_an_instance_of(Daijobu::Scheme::Marshal)
10
- end
11
- end
12
-
13
- describe "with :json" do
14
- it "should return a json scheme" do
15
- Daijobu::Scheme.get(:json).should be_an_instance_of(Daijobu::Scheme::JSON)
16
- end
17
- end
18
-
19
- describe "with :yaml" do
20
- it "should return a yaml scheme" do
21
- Daijobu::Scheme.get(:yaml).should be_an_instance_of(Daijobu::Scheme::YAML)
22
- end
23
- end
24
-
25
- describe "with :eval" do
26
- it "should return a eval scheme" do
27
- Daijobu::Scheme.get(:eval).should be_an_instance_of(Daijobu::Scheme::Eval)
28
- end
29
- end
30
-
31
- describe "with anything else" do
32
- it "should raise an error" do
33
- lambda { Daijobu::Scheme.get(:xml) }.should raise_error(Daijobu::UnknownScheme)
34
- end
35
- end
36
-
37
- end
38
-
39
- end
@@ -1,37 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
-
3
- describe Daijobu::Scheme::Eval do
4
-
5
- before do
6
- @scheme = Daijobu::Scheme::Eval.new
7
- end
8
-
9
- describe "#parse" do
10
- before do
11
- @stringy = '{ "thing" => 10 }'
12
- end
13
-
14
- it "should parse the given string using Kernel::eval" do
15
- @scheme.expects(:eval).with(@stringy)
16
- @scheme.parse(@stringy)
17
- end
18
-
19
- describe "when the input string is nil" do
20
- it "should return nil" do
21
- @scheme.parse(nil).should be_nil
22
- end
23
- end
24
- end
25
-
26
- describe "#unparse" do
27
- before do
28
- @hashy = { "thing" => 10 }
29
- end
30
-
31
- it "should inspect the given object" do
32
- @hashy.expects(:inspect)
33
- @scheme.unparse(@hashy)
34
- end
35
- end
36
-
37
- end
@@ -1,37 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
-
3
- describe Daijobu::Scheme::JSON do
4
-
5
- before do
6
- @scheme = Daijobu::Scheme::JSON.new
7
- end
8
-
9
- describe "#parse" do
10
- before do
11
- @stringy = '{ "thing" : 10 }'
12
- end
13
-
14
- it "should parse the given string with the JSON module" do
15
- ::JSON.expects(:parse).with(@stringy)
16
- @scheme.parse(@stringy)
17
- end
18
-
19
- describe "when the input string is nil" do
20
- it "should return nil" do
21
- @scheme.parse(nil).should be_nil
22
- end
23
- end
24
- end
25
-
26
- describe "#unparse" do
27
- before do
28
- @hashy = { "thing" => 10 }
29
- end
30
-
31
- it "should unparse the given object with the JSON module" do
32
- ::JSON.expects(:unparse).with(@hashy)
33
- @scheme.unparse(@hashy)
34
- end
35
- end
36
-
37
- end
@@ -1,37 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
-
3
- describe Daijobu::Scheme::Marshal do
4
-
5
- before do
6
- @scheme = Daijobu::Scheme::Marshal.new
7
- end
8
-
9
- describe "#parse" do
10
- before do
11
- @stringy = "\004\b{\006\"\nthingi\017"
12
- end
13
-
14
- it "should parse the given string with the Marshal module" do
15
- ::Marshal.expects(:load).with(@stringy)
16
- @scheme.parse(@stringy)
17
- end
18
-
19
- describe "when the input string is nil" do
20
- it "should return nil" do
21
- @scheme.parse(nil).should be_nil
22
- end
23
- end
24
- end
25
-
26
- describe "#unparse" do
27
- before do
28
- @hashy = { "thing" => 10 }
29
- end
30
-
31
- it "should unparse the given object with the JSON module" do
32
- ::Marshal.expects(:dump).with(@hashy)
33
- @scheme.unparse(@hashy)
34
- end
35
- end
36
-
37
- end
@@ -1,35 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
-
3
- describe Daijobu::Scheme::Raw do
4
-
5
- before do
6
- @scheme = Daijobu::Scheme::Raw.new
7
- end
8
-
9
- describe "#parse" do
10
- before do
11
- @stringy = '{ "thing" => 10 }'
12
- end
13
-
14
- it "should do nothing to the given string" do
15
- @scheme.parse(@stringy).should == @stringy
16
- end
17
-
18
- describe "when the input string is nil" do
19
- it "should return nil" do
20
- @scheme.parse(nil).should be_nil
21
- end
22
- end
23
- end
24
-
25
- describe "#unparse" do
26
- before do
27
- @hashy = { "thing" => 10 }
28
- end
29
-
30
- it "should do nothing to the given object" do
31
- @scheme.unparse(@hashy).should == @hashy
32
- end
33
- end
34
-
35
- end
@@ -1,37 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
-
3
- describe Daijobu::Scheme::YAML do
4
-
5
- before do
6
- @scheme = Daijobu::Scheme::YAML.new
7
- end
8
-
9
- describe "#parse" do
10
- before do
11
- @stringy = '{ "thing" : 10 }'
12
- end
13
-
14
- it "should parse the given string with the YAML module" do
15
- ::YAML.expects(:load).with(@stringy)
16
- @scheme.parse(@stringy)
17
- end
18
-
19
- describe "when the input string is nil" do
20
- it "should return nil" do
21
- @scheme.parse(nil).should be_nil
22
- end
23
- end
24
- end
25
-
26
- describe "#unparse" do
27
- before do
28
- @hashy = { "thing" => 10 }
29
- end
30
-
31
- it "should unparse the given object with the JSON module" do
32
- ::YAML.expects(:dump).with(@hashy)
33
- @scheme.unparse(@hashy)
34
- end
35
- end
36
-
37
- end