frivol 0.1.3 → 0.1.4

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.
@@ -0,0 +1,30 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Classes
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Classes</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Classes</h1>
22
+ <div id="index-entries">
23
+ <a href="classes/Frivol.html">Frivol</a><br />
24
+ <a href="classes/Frivol/ClassMethods.html">Frivol::ClassMethods</a><br />
25
+ <a href="classes/Frivol/Config.html">Frivol::Config</a><br />
26
+ <a href="classes/Time.html">Time</a><br />
27
+ </div>
28
+ </div>
29
+ </body>
30
+ </html>
@@ -0,0 +1,27 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Files
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Files</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Files</h1>
22
+ <div id="index-entries">
23
+ <a href="files/lib/frivol_rb.html">lib/frivol.rb</a><br />
24
+ </div>
25
+ </div>
26
+ </body>
27
+ </html>
@@ -0,0 +1,38 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Methods
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Methods</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Methods</h1>
22
+ <div id="index-entries">
23
+ <a href="classes/Frivol.html#M000005">delete_storage (Frivol)</a><br />
24
+ <a href="classes/Frivol.html#M000006">expire_storage (Frivol)</a><br />
25
+ <a href="classes/Frivol/Config.html#M000012">include_in (Frivol::Config)</a><br />
26
+ <a href="classes/Time.html#M000002">json_create (Time)</a><br />
27
+ <a href="classes/Frivol/Config.html#M000011">redis (Frivol::Config)</a><br />
28
+ <a href="classes/Frivol/Config.html#M000010">redis_config= (Frivol::Config)</a><br />
29
+ <a href="classes/Frivol.html#M000004">retrieve (Frivol)</a><br />
30
+ <a href="classes/Frivol/ClassMethods.html#M000008">storage_expires_in (Frivol::ClassMethods)</a><br />
31
+ <a href="classes/Frivol/ClassMethods.html#M000009">storage_expiry (Frivol::ClassMethods)</a><br />
32
+ <a href="classes/Frivol.html#M000007">storage_key (Frivol)</a><br />
33
+ <a href="classes/Frivol.html#M000003">store (Frivol)</a><br />
34
+ <a href="classes/Time.html#M000001">to_json (Time)</a><br />
35
+ </div>
36
+ </div>
37
+ </body>
38
+ </html>
data/doc/index.html ADDED
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
5
+
6
+ <!--
7
+
8
+ Frivol - Frivolously simple temporary storage backed by Redis
9
+
10
+ -->
11
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
12
+ <head>
13
+ <title>Frivol - Frivolously simple temporary storage backed by Redis</title>
14
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
15
+ </head>
16
+ <frameset rows="20%, 80%">
17
+ <frameset cols="25%,35%,45%">
18
+ <frame src="fr_file_index.html" title="Files" name="Files" />
19
+ <frame src="fr_class_index.html" name="Classes" />
20
+ <frame src="fr_method_index.html" name="Methods" />
21
+ </frameset>
22
+ <frame src="files/lib/frivol_rb.html" name="docwin" />
23
+ </frameset>
24
+ </html>
@@ -0,0 +1,208 @@
1
+
2
+ body {
3
+ font-family: Verdana,Arial,Helvetica,sans-serif;
4
+ font-size: 90%;
5
+ margin: 0;
6
+ margin-left: 40px;
7
+ padding: 0;
8
+ background: white;
9
+ }
10
+
11
+ h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
12
+ h1 { font-size: 150%; }
13
+ h2,h3,h4 { margin-top: 1em; }
14
+
15
+ a { background: #eef; color: #039; text-decoration: none; }
16
+ a:hover { background: #039; color: #eef; }
17
+
18
+ /* Override the base stylesheet's Anchor inside a table cell */
19
+ td > a {
20
+ background: transparent;
21
+ color: #039;
22
+ text-decoration: none;
23
+ }
24
+
25
+ /* and inside a section title */
26
+ .section-title > a {
27
+ background: transparent;
28
+ color: #eee;
29
+ text-decoration: none;
30
+ }
31
+
32
+ /* === Structural elements =================================== */
33
+
34
+ div#index {
35
+ margin: 0;
36
+ margin-left: -40px;
37
+ padding: 0;
38
+ font-size: 90%;
39
+ }
40
+
41
+
42
+ div#index a {
43
+ margin-left: 0.7em;
44
+ }
45
+
46
+ div#index .section-bar {
47
+ margin-left: 0px;
48
+ padding-left: 0.7em;
49
+ background: #ccc;
50
+ font-size: small;
51
+ }
52
+
53
+
54
+ div#classHeader, div#fileHeader {
55
+ width: auto;
56
+ color: white;
57
+ padding: 0.5em 1.5em 0.5em 1.5em;
58
+ margin: 0;
59
+ margin-left: -40px;
60
+ border-bottom: 3px solid #006;
61
+ }
62
+
63
+ div#classHeader a, div#fileHeader a {
64
+ background: inherit;
65
+ color: white;
66
+ }
67
+
68
+ div#classHeader td, div#fileHeader td {
69
+ background: inherit;
70
+ color: white;
71
+ }
72
+
73
+
74
+ div#fileHeader {
75
+ background: #057;
76
+ }
77
+
78
+ div#classHeader {
79
+ background: #048;
80
+ }
81
+
82
+
83
+ .class-name-in-header {
84
+ font-size: 180%;
85
+ font-weight: bold;
86
+ }
87
+
88
+
89
+ div#bodyContent {
90
+ padding: 0 1.5em 0 1.5em;
91
+ }
92
+
93
+ div#description {
94
+ padding: 0.5em 1.5em;
95
+ background: #efefef;
96
+ border: 1px dotted #999;
97
+ }
98
+
99
+ div#description h1,h2,h3,h4,h5,h6 {
100
+ color: #125;;
101
+ background: transparent;
102
+ }
103
+
104
+ div#validator-badges {
105
+ text-align: center;
106
+ }
107
+ div#validator-badges img { border: 0; }
108
+
109
+ div#copyright {
110
+ color: #333;
111
+ background: #efefef;
112
+ font: 0.75em sans-serif;
113
+ margin-top: 5em;
114
+ margin-bottom: 0;
115
+ padding: 0.5em 2em;
116
+ }
117
+
118
+
119
+ /* === Classes =================================== */
120
+
121
+ table.header-table {
122
+ color: white;
123
+ font-size: small;
124
+ }
125
+
126
+ .type-note {
127
+ font-size: small;
128
+ color: #DEDEDE;
129
+ }
130
+
131
+ .xxsection-bar {
132
+ background: #eee;
133
+ color: #333;
134
+ padding: 3px;
135
+ }
136
+
137
+ .section-bar {
138
+ color: #333;
139
+ border-bottom: 1px solid #999;
140
+ margin-left: -20px;
141
+ }
142
+
143
+
144
+ .section-title {
145
+ background: #79a;
146
+ color: #eee;
147
+ padding: 3px;
148
+ margin-top: 2em;
149
+ margin-left: -30px;
150
+ border: 1px solid #999;
151
+ }
152
+
153
+ .top-aligned-row { vertical-align: top }
154
+ .bottom-aligned-row { vertical-align: bottom }
155
+
156
+ /* --- Context section classes ----------------------- */
157
+
158
+ .context-row { }
159
+ .context-item-name { font-family: monospace; font-weight: bold; color: black; }
160
+ .context-item-value { font-size: small; color: #448; }
161
+ .context-item-desc { color: #333; padding-left: 2em; }
162
+
163
+ /* --- Method classes -------------------------- */
164
+ .method-detail {
165
+ background: #efefef;
166
+ padding: 0;
167
+ margin-top: 0.5em;
168
+ margin-bottom: 1em;
169
+ border: 1px dotted #ccc;
170
+ }
171
+ .method-heading {
172
+ color: black;
173
+ background: #ccc;
174
+ border-bottom: 1px solid #666;
175
+ padding: 0.2em 0.5em 0 0.5em;
176
+ }
177
+ .method-signature { color: black; background: inherit; }
178
+ .method-name { font-weight: bold; }
179
+ .method-args { font-style: italic; }
180
+ .method-description { padding: 0 0.5em 0 0.5em; }
181
+
182
+ /* --- Source code sections -------------------- */
183
+
184
+ a.source-toggle { font-size: 90%; }
185
+ div.method-source-code {
186
+ background: #262626;
187
+ color: #ffdead;
188
+ margin: 1em;
189
+ padding: 0.5em;
190
+ border: 1px dashed #999;
191
+ overflow: hidden;
192
+ }
193
+
194
+ div.method-source-code pre { color: #ffdead; overflow: hidden; }
195
+
196
+ /* --- Ruby keyword styles --------------------- */
197
+
198
+ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
199
+
200
+ .ruby-constant { color: #7fffd4; background: transparent; }
201
+ .ruby-keyword { color: #00ffff; background: transparent; }
202
+ .ruby-ivar { color: #eedd82; background: transparent; }
203
+ .ruby-operator { color: #00ffee; background: transparent; }
204
+ .ruby-identifier { color: #ffdead; background: transparent; }
205
+ .ruby-node { color: #ffa07a; background: transparent; }
206
+ .ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
207
+ .ruby-regexp { color: #ffa07a; background: transparent; }
208
+ .ruby-value { color: #7fffd4; background: transparent; }
data/frivol.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{frivol}
8
- s.version = "0.1.3"
8
+ s.version = "0.1.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Marc Heiligers"]
12
- s.date = %q{2010-08-20}
12
+ s.date = %q{2010-08-22}
13
13
  s.description = %q{Simple Redis backed temporary storage intended primarily for use with ActiveRecord models to provide caching}
14
14
  s.email = %q{marc@eternal.co.za}
15
15
  s.extra_rdoc_files = [
@@ -23,6 +23,28 @@ Gem::Specification.new do |s|
23
23
  "README.rdoc",
24
24
  "Rakefile",
25
25
  "VERSION",
26
+ "doc/classes/Frivol.html",
27
+ "doc/classes/Frivol.src/M000003.html",
28
+ "doc/classes/Frivol.src/M000004.html",
29
+ "doc/classes/Frivol.src/M000005.html",
30
+ "doc/classes/Frivol.src/M000006.html",
31
+ "doc/classes/Frivol/ClassMethods.html",
32
+ "doc/classes/Frivol/ClassMethods.src/M000007.html",
33
+ "doc/classes/Frivol/ClassMethods.src/M000008.html",
34
+ "doc/classes/Frivol/Config.html",
35
+ "doc/classes/Frivol/Config.src/M000009.html",
36
+ "doc/classes/Frivol/Config.src/M000010.html",
37
+ "doc/classes/Frivol/Config.src/M000011.html",
38
+ "doc/classes/Time.html",
39
+ "doc/classes/Time.src/M000001.html",
40
+ "doc/classes/Time.src/M000002.html",
41
+ "doc/created.rid",
42
+ "doc/files/lib/frivol_rb.html",
43
+ "doc/fr_class_index.html",
44
+ "doc/fr_file_index.html",
45
+ "doc/fr_method_index.html",
46
+ "doc/index.html",
47
+ "doc/rdoc-style.css",
26
48
  "frivol.gemspec",
27
49
  "lib/frivol.rb",
28
50
  "test/fake_redis.rb",
@@ -45,12 +67,18 @@ Gem::Specification.new do |s|
45
67
  s.specification_version = 3
46
68
 
47
69
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
- s.add_development_dependency(%q<shoulda>, [">= 2"])
70
+ s.add_runtime_dependency(%q<json>, [">= 1.2.0"])
71
+ s.add_runtime_dependency(%q<redis>, [">= 0.1.2"])
72
+ s.add_development_dependency(%q<shoulda>, [">= 2.11.1"])
49
73
  else
50
- s.add_dependency(%q<shoulda>, [">= 2"])
74
+ s.add_dependency(%q<json>, [">= 1.2.0"])
75
+ s.add_dependency(%q<redis>, [">= 0.1.2"])
76
+ s.add_dependency(%q<shoulda>, [">= 2.11.1"])
51
77
  end
52
78
  else
53
- s.add_dependency(%q<shoulda>, [">= 2"])
79
+ s.add_dependency(%q<json>, [">= 1.2.0"])
80
+ s.add_dependency(%q<redis>, [">= 0.1.2"])
81
+ s.add_dependency(%q<shoulda>, [">= 2.11.1"])
54
82
  end
55
83
  end
56
84
 
data/lib/frivol.rb CHANGED
@@ -1,7 +1,81 @@
1
+ # = Frivol - Frivolously simple temporary storage backed by Redis
2
+ # A really simple Redis-backed temporary storage mechanism intended to be used with ActiveRecord,
3
+ # but will work with other ORM's or any classes really.
4
+ #
5
+ # I developed Frivol secifically for use in Mad Mimi (http://madmimi.com) to help with caching
6
+ # of data which requires fairly long running (multi-second) database queries, and also to aid
7
+ # with communication of status from background Resque jobs running on the workers to the front
8
+ # end web servers. Redis was chosen because we already had Resque, which is Redis-backed. Also,
9
+ # unlike memcached, Redis persists it's data to disk, meaning there is far less warmup required
10
+ # when a hot system is restarted. Frivol's design is such that it solves our problem, but I
11
+ # believe it is generic enough to be used in many Rails web projects and even in other types of
12
+ # projects altogether.
13
+ #
14
+ # == Usage
15
+ # Configure Frivol in your configuration, for example in an initializer or in environment.rb
16
+ # REDIS_CONFIG = {
17
+ # :host => "localhost",
18
+ # :port => 6379
19
+ # }
20
+ # Frivol::Config.redis_config = REDIS_CONFIG
21
+ # Now include Frivol in whichever classes you'd like to make use of temporary storage. You can optionally
22
+ # call the <tt>storage_expires_in(time)</tt> class method to set a default expiry. In your methods you can
23
+ # now call the <tt>store(keys_and_values)</tt> and <tt>retrieve(keys_and_defaults)</tt> methods.
24
+ #
25
+ # Defaults in the +retrieve+ method can be symbols, in which case Frivol will check if the class <tt>respond_to?</tt>
26
+ # a method by that name to get the default.
27
+ #
28
+ # The <tt>expire_storage(time)</tt> method can be used to set the expiry time in seconds of the temporary storage.
29
+ # The default is not to expire the storage, in which case it will live for as long as Redis keeps it.
30
+ # <tt>delete_storage</tt>, as the name suggests will immediately delete the storage.
31
+ #
32
+ # Frivol uses the +storage_key+ method to create a base key for storage in Redis. The current implementation uses
33
+ # <tt>"#{self.class.name}-#{id}"</tt> so you'll want to override that method if you have classes that don't
34
+ # respond to id.
35
+ #
36
+ # Frivol also extends Time to allow it to be (de)serialized to JSON, which currently used to store
37
+ # data in Redis.
38
+ # == Example
39
+ # class BigComplexCalcer
40
+ # include Frivol
41
+ # storage_expires_in 600 # temporary storage expires in 10 minutes.
42
+ #
43
+ # def initialize(key)
44
+ # @key = key
45
+ # end
46
+ #
47
+ # def storage_key
48
+ # "frivol-test-#{key}" # override the storage key because we don't respond_to? id
49
+ # end
50
+ #
51
+ # def big_complex_calc
52
+ # retrieve :complex => :do_big_complex_calc # do_big_complex_calc is the method to get the default from
53
+ # end
54
+ #
55
+ # def last_calc_done
56
+ # last = retrieve :last => nil # default is nil
57
+ # return "never" if last.nil?
58
+ # return "#{Time.now - last} seconds ago"
59
+ # end
60
+ #
61
+ # def do_big_complex_calc
62
+ # # Wee! Do some really hard work here...
63
+ # # ...still working...
64
+ # store :complex => result, :last => Time.now # ...and let's keep the result for at least 10 minutes, as well as the last time we did it
65
+ # end
66
+ # end
1
67
  require "json"
2
68
  require "redis"
3
69
 
70
+ # == Frivol
4
71
  module Frivol
72
+ # Store a hash of keys and values.
73
+ #
74
+ # The hash need not be the complete hash of all things stored, just those you want to change.
75
+ # For example, you may call <tt>store :value1 => 1</tt> and then later call <tt>store :value2 => 2</tt>
76
+ # and Frivol will now have stored <tt>{ :value1 => 1, :value => 2 }</tt>. How Frivol stores or retrieves data
77
+ # is intended to be hidden and while it is true that it currently uses a <tt>Hash#to_json</tt> you should not
78
+ # rely on this.
5
79
  def store(keys_and_values)
6
80
  Frivol::Helpers.retrieve_hash self
7
81
  keys_and_values.each do |key, value|
@@ -10,6 +84,15 @@ module Frivol
10
84
  Frivol::Helpers.store_hash self
11
85
  end
12
86
 
87
+ # Retrieve stored values, or defaults.
88
+ #
89
+ # If you retrieve a single key just that value is returned. If you retrieve multiple keys an array of
90
+ # values is returned. You might do:
91
+ # name = retrieve :name => "Marc Heiligers"
92
+ # first_name, last_name = retrieve :first_name => "Marc", :last_name => "Heiligers"
93
+ #
94
+ # If the default is a symbol, Frivol will attempt to get the default from a method named after that symbol.
95
+ # If the class does not <tt>respond_to?</tt> a method by that name, the symbol will assumed to be the default.
13
96
  def retrieve(keys_and_defaults)
14
97
  Frivol::Helpers.retrieve_hash self
15
98
  result = keys_and_defaults.map do |key, default|
@@ -19,31 +102,60 @@ module Frivol
19
102
  result
20
103
  end
21
104
 
105
+ # Deletes the stored values.
106
+ def delete_storage
107
+ Frivol::Helpers.delete_hash self
108
+ end
109
+
110
+ # Expire the stored data in +time+ seconds.
22
111
  def expire_storage(time)
23
112
  return if time.nil?
24
113
  Frivol::Config.redis.expire storage_key, time
25
114
  end
26
115
 
116
+ # The base key used for storage in Redis.
117
+ #
118
+ # This method has been implemented for use with ActiveRecord and uses <tt>"#{self.class.name}-#{id}"</tt>
119
+ # If you are not using ActiveRecord, or using classes that don't respond to id, you should override
120
+ # this method in your class.
27
121
  def storage_key
28
122
  @frivol_key ||= "#{self.class.name}-#{id}"
29
123
  end
30
124
 
125
+ # == Frivol::Config
126
+ # Sets the Frivol configuration (currently only the Redis config), allows access to the configured Redis instance,
127
+ # and has a helper method to include Frivol in a class with an optional storage expiry parameter
31
128
  module Config
129
+ # Set the Redis configuration.
130
+ #
131
+ # Expects a hash such as
132
+ # REDIS_CONFIG = {
133
+ # :host => "localhost",
134
+ # :port => 6379
135
+ # }
136
+ # Frivol::Config.redis_config = REDIS_CONFIG
32
137
  def self.redis_config=(config)
33
138
  @@redis = Redis.new(config)
34
139
  end
35
140
 
141
+ # Returns the configured Redis instance
36
142
  def self.redis
37
143
  @@redis
38
144
  end
39
145
 
146
+ # A convenience method to include Frivol in a class, with an optional storage expiry parameter.
147
+ #
148
+ # For example, you might have the following in environment.rb:
149
+ # Frivol::Config.redis_config = REDIS_CONFIG
150
+ # Frivol::Config.include_in ActiveRecord::Base, 600
151
+ # Which would include Frivol in ActiveRecord::Base and set the default storage expiry to 10 minutes
40
152
  def self.include_in(host_class, storage_expires_in = nil)
41
153
  host_class.send(:include, Frivol)
42
154
  host_class.storage_expires_in storage_expires_in if storage_expires_in
43
155
  end
44
156
  end
45
157
 
46
- module Helpers
158
+ module Helpers #:nodoc:
47
159
  def self.store_hash(instance)
48
160
  hash = instance.instance_variable_get(:@frivol_hash)
49
161
  is_new = instance.instance_variable_get(:@frivol_is_new)
@@ -64,24 +176,37 @@ module Frivol
64
176
  instance.instance_variable_set :@frivol_hash, hash
65
177
  hash
66
178
  end
179
+
180
+ def self.delete_hash(instance)
181
+ key = instance.send(:storage_key)
182
+ Frivol::Config.redis.del key
183
+ instance.instance_variable_set :@frivol_hash, {}
184
+ end
67
185
  end
68
186
 
187
+ # == Frivol::ClassMethods
188
+ # These methods are available on the class level when Frivol is included in the class.
69
189
  module ClassMethods
190
+ # Set the storage expiry time in seconds.
70
191
  def storage_expires_in(time)
71
192
  @frivol_storage_expiry = time
72
193
  end
73
194
 
195
+ # Get the storage expiry time in seconds.
74
196
  def storage_expiry
75
197
  @frivol_storage_expiry
76
198
  end
77
199
  end
78
200
 
79
- def self.included(host)
201
+ def self.included(host) #:nodoc:
80
202
  host.extend(ClassMethods)
81
203
  end
82
204
  end
83
205
 
206
+ # == Time
207
+ # An extension to the Time class which allows Time instances to be serialized by <tt>#to_json</tt> and deserialized by <tt>JSON#parse</tt>.
84
208
  class Time
209
+ # Serialize to JSON
85
210
  def to_json(*a)
86
211
  {
87
212
  'json_class' => self.class.name,
@@ -89,6 +214,7 @@ class Time
89
214
  }.to_json(*a)
90
215
  end
91
216
 
217
+ # Deserialize from JSON
92
218
  def self.json_create(o)
93
219
  Time.parse(*o['data'])
94
220
  end