cerealize 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,4 @@
1
+ = cerealize changes history
2
+
3
+ === cerealize 0.8.0, 2010-04-18
4
+ * release early, release often
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
data/README ADDED
@@ -0,0 +1,63 @@
1
+ = cerealize 0.8.0
2
+ by Cardinal Blue ( http://cardinalblue.com )
3
+
4
+ == LINKS:
5
+
6
+ * github[http://github.com/cardinalblue/cerealize]
7
+ * rubygems[http://rubygems.org/gems/cerealize]
8
+
9
+ == DESCRIPTION:
10
+
11
+ Serialize out of the Cerealize Box
12
+ - a drop-in replacement for ActiveRecord's serialize
13
+
14
+ It can auto transcode old encoding (yaml if you're using AR's serialize),
15
+ to new encoding (marshal, json, you name it) without any migration.
16
+
17
+ Current supported encoding:
18
+ 1. YAML
19
+ 2. Marshal
20
+ 3. JSON (planned)
21
+
22
+ Current supported ORM:
23
+ 1. ActiveRecord (tested with 2.3.5)
24
+ 2. DataMapper (planned)
25
+
26
+ == SYNOPSIS:
27
+
28
+ require 'cerealize'
29
+ class User < ActiveRecord::Base
30
+ # when force_encoding set to false (this is default), cerealize is
31
+ # smart enough to transcode old encoding (e.g. yaml) to new encoding
32
+ # (i.e. marshal)
33
+ cerealize :data, Hash, :encoding => :marshal, :force_encoding => false
34
+ end
35
+
36
+ == REQUIREMENTS:
37
+
38
+ * tested with MRI 1.8.7 and 1.9.1
39
+ * one of your desired ORM
40
+
41
+ == INSTALL:
42
+
43
+ > gem install cerealize
44
+ or
45
+ > script/plugin install git://github.com/cardinalblue/cerealize.git
46
+
47
+ == LICENSE:
48
+
49
+ Apache License 2.0
50
+
51
+ Copyright (c) 2010, Cardinal Blue
52
+
53
+ Licensed under the Apache License, Version 2.0 (the "License");
54
+ you may not use this file except in compliance with the License.
55
+ You may obtain a copy of the License at
56
+
57
+ http://www.apache.org/licenses/LICENSE-2.0
58
+
59
+ Unless required by applicable law or agreed to in writing, software
60
+ distributed under the License is distributed on an "AS IS" BASIS,
61
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
62
+ See the License for the specific language governing permissions and
63
+ limitations under the License.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'bones'
5
+ rescue LoadError
6
+ abort '### Please install the "bones" gem ###'
7
+ end
8
+
9
+ ensure_in_path 'lib'
10
+ proj = 'cerealize'
11
+ require "#{proj}/version"
12
+
13
+ Bones{
14
+ ruby_opts [''] # silence warning for now
15
+
16
+ version Cerealize::VERSION
17
+
18
+ depend_on 'activerecord', :version => '>=2.3.5'
19
+ # depend_on 'activerecord', :development => true, :version => '>=2.3.5'
20
+
21
+ name proj
22
+ url "http://github.com/cardinalblue/#{proj}"
23
+ authors 'Cardinal Blue'
24
+ email 'dev (XD) cardinalblue.com'
25
+
26
+ history_file 'CHANGES'
27
+ readme_file 'README'
28
+ ignore_file '.gitignore'
29
+ rdoc.include ['\w+']
30
+ }
31
+
32
+ CLEAN.include Dir['**/*.rbc']
33
+
34
+ task :default do
35
+ Rake.application.options.show_task_pattern = /./
36
+ Rake.application.display_tasks_and_comments
37
+ end
data/TODO ADDED
@@ -0,0 +1,7 @@
1
+ = cerealize todo list
2
+
3
+ * cleanup test cases... use proper way to setup encoding
4
+ * extract options, instead of hardcoded into method closure (block)
5
+ * extract base64 decorator, then we can use pure marshal
6
+ * datamapper support
7
+ * json support
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ # encoding: utf-8
2
+ require 'cerealize'
data/lib/cerealize.rb ADDED
@@ -0,0 +1,135 @@
1
+ # encoding: utf-8
2
+
3
+ require 'active_record'
4
+ autoload :YAML, 'yaml'
5
+
6
+ module Cerealize
7
+ module Codec
8
+ autoload 'Yaml', 'cerealize/codec/yaml'
9
+ autoload 'Marshal', 'cerealize/codec/marshal'
10
+ end
11
+ class NoSuchCodec < ArgumentError; end
12
+ class NoSuitableCodec < RuntimeError ; end
13
+
14
+ #
15
+ # Dirty functionality: note that *_changed? and changed? work,
16
+ # but *_was, *_change, and changes do NOT work.
17
+ #
18
+
19
+ def self.included(base)
20
+ base.send :extend, ClassMethods
21
+ end
22
+
23
+ module_function
24
+ def codecs
25
+ @codecs ||= Codec.constants.sort.map{ |codec_name|
26
+ Codec.const_get(codec_name)
27
+ }
28
+ end
29
+
30
+ def codec_names
31
+ @codec_names ||= codecs.map{ |codec|
32
+ codec.to_s.sub(/(\w+::)+/, '').downcase.to_sym
33
+ }
34
+ end
35
+
36
+ def codec_detect(str)
37
+ codecs.find{ |codec| codec.yours?(str) }
38
+ end
39
+
40
+ def codec_get(codec_name)
41
+ Codec.const_get(codec_name.to_s.capitalize)
42
+ rescue NameError
43
+ raise NoSuchCodec.new(codec_name)
44
+ end
45
+
46
+ def encode(obj, codec)
47
+ return nil unless obj
48
+ codec.encode(obj)
49
+ end
50
+
51
+ def decode(str, codec=nil)
52
+ return nil unless str
53
+ codec ||= codec_detect(str)
54
+
55
+ if codec && codec.yours?(str)
56
+ codec.decode(str)
57
+ else
58
+ raise NoSuitableCodec.new("#{str[0..46]}...")
59
+ end
60
+ end
61
+
62
+ module ClassMethods
63
+
64
+ def cerealize property, klass=nil, options={}
65
+ field_pre = "@#{property}_pre".to_sym
66
+ field_cache = "@#{property}".to_sym
67
+ codec = Cerealize.codec_get(options[:encoding] || :marshal)
68
+ force_encoding = options[:force_encoding]
69
+
70
+ # Invariants:
71
+ # - instance_variable_defined?(field_cache) IFF the READER or WRITER has been called
72
+ # - instance_variable_defined?(field_pre) IFF the READER was called BEFORE
73
+ # any WRITER
74
+
75
+ # READER method
76
+ #
77
+ define_method property do
78
+ # Rails.logger.debug "#{property} (READER)"
79
+
80
+ # See if no assignment yet
81
+ if !instance_variable_defined?(field_cache)
82
+
83
+ # Save property if not already saved
84
+ if !instance_variable_defined?(field_pre)
85
+ instance_variable_set(field_pre, read_attribute(property))
86
+ end
87
+
88
+ # Set cached from pre
89
+ v = Cerealize.decode(instance_variable_get(field_pre),
90
+ force_encoding && codec )
91
+ raise ActiveRecord::SerializationTypeMismatch, "expected #{klass}, got #{v.class}" \
92
+ if klass && !v.nil? && !v.kind_of?(klass)
93
+ instance_variable_set(field_cache, v)
94
+ end
95
+
96
+ # Return cached
97
+ instance_variable_get(field_cache)
98
+ end
99
+
100
+ # WRITER method
101
+ #
102
+ define_method "#{property}=" do |v|
103
+ # Rails.logger.debug "#{property}=#{v}"
104
+ send "#{property}_will_change!" if instance_variable_get(field_cache) != v
105
+ instance_variable_set(field_cache, v)
106
+ end
107
+
108
+ # Callback for before_save
109
+ #
110
+ define_method "#{property}_update_if_dirty" do
111
+ # Rails.logger.debug "#{property}_update_if_dirty"
112
+
113
+ # See if we have a new cur value
114
+ if instance_variable_defined?(field_cache)
115
+ v = instance_variable_get(field_cache)
116
+ v_enc = Cerealize.encode(v, codec)
117
+
118
+ # See if no pre at all (i.e. it was written to before being read),
119
+ # or if different. When comparing, compare both marshalized string,
120
+ # and Object ==.
121
+ #
122
+ if !instance_variable_defined?(field_pre) ||
123
+ (v_enc != instance_variable_get(field_pre) &&
124
+ v != Cerealize.decode(instance_variable_get(field_pre),
125
+ force_encoding && codec ))
126
+ write_attribute(property, v_enc)
127
+ end
128
+ end
129
+ remove_instance_variable(field_pre) if instance_variable_defined?(field_pre)
130
+ remove_instance_variable(field_cache) if instance_variable_defined?(field_cache)
131
+ end
132
+ before_save("#{property}_update_if_dirty")
133
+ end
134
+ end
135
+ end # of Cerealize
@@ -0,0 +1,21 @@
1
+
2
+ module Cerealize
3
+ module Codec; end
4
+ module Codec::Marshal
5
+ module_function
6
+
7
+ # Version 4.8 of Marshalize always base64s to "BA"
8
+ # TODO: extract base64 decorator, then we can use pure marshal
9
+ def yours?(str)
10
+ str[0..1] == 'BA'
11
+ end
12
+
13
+ def encode(obj)
14
+ [Marshal.dump(obj)].pack('m*')
15
+ end
16
+
17
+ def decode(str)
18
+ Marshal.load(str.unpack('m*').first)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+
2
+ module Cerealize
3
+ module Codec; end
4
+ module Codec::Yaml
5
+ module_function
6
+
7
+ # See YAML spec (though might fail if "directives"?)
8
+ def yours?(str)
9
+ str[0..2] == '---'
10
+ end
11
+
12
+ def encode(obj)
13
+ YAML.dump(obj)
14
+ end
15
+
16
+ def decode(str)
17
+ YAML.load(str)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module Cerealize
3
+ VERSION = '0.8.0'
4
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ def set_encoding(encoding)
4
+ Boat.send :cerealize, :captain, nil, :encoding => encoding
5
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ ActiveRecord::Base.establish_connection(
4
+ :adapter => 'sqlite3',
5
+ :database => ':memory:'
6
+ )
7
+
8
+ class Boat < ActiveRecord::Base
9
+ include Cerealize
10
+ cerealize :captain
11
+ cerealize :cargo, Blob
12
+ end
13
+
14
+ ActiveRecord::Base.connection.create_table :boats, :force => true do |t|
15
+ t.string :name
16
+ t.integer :tonnage
17
+ t.string :captain
18
+ t.string :cargo
19
+ end
20
+
21
+
22
+
23
+ class Cat < ActiveRecord::Base
24
+ include Cerealize
25
+ cerealize :name, String, :encoding => :yaml
26
+ cerealize :tail, Array, :encoding => :marshal, :force_encoding => true
27
+ cerealize :food, Hash, :encoding => :marshal
28
+ end
29
+
30
+ ActiveRecord::Base.connection.create_table :cats, :force => true do |t|
31
+ t.text :name
32
+ t.text :tail
33
+ t.text :food
34
+ end
data/test/stub.rb ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ class Person
4
+ def initialize(name, age=nil)
5
+ self.name = name
6
+ self.age = age
7
+ end
8
+
9
+ ATTR = [ :name, :age, :hat, :pocket ]
10
+ attr_accessor(*ATTR)
11
+
12
+ def ==(other)
13
+ ATTR.all?{|m| send(m) == other.send(m) }
14
+ end
15
+ def eql?(other)
16
+ ATTR.all?{|m| send(m).eql? other.send(m) }
17
+ end
18
+ end
19
+
20
+ class Hat
21
+ def initialize(color); self.color = color; end
22
+ attr_accessor :color
23
+ end
24
+
25
+ class Blob; end
@@ -0,0 +1,242 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems' if RUBY_VERSION < '1.9.1'
4
+ require 'cerealize'
5
+
6
+ require 'test/stub'
7
+ require 'test/helper'
8
+ require 'test/helper_active_record'
9
+
10
+ require 'test/unit'
11
+
12
+ class AllCodecTest < Test::Unit::TestCase
13
+ def setup
14
+ Boat.delete_all
15
+ end
16
+
17
+ def teardown
18
+ end
19
+
20
+ Cerealize.codec_names.each do |encoding|
21
+
22
+ define_method "test_#{encoding}_basic" do
23
+ set_encoding(encoding)
24
+
25
+ b = Boat.create(:name => 'pequod')
26
+
27
+ b.captain = Person.new('ahab')
28
+ b.save!
29
+
30
+ b = Boat.find_by_name('pequod')
31
+ assert_equal Person, b.captain.class
32
+ assert_equal 'ahab', b.captain.name
33
+
34
+ b.captain = Person.new('ishmael') # make sure not frozen!
35
+ assert_equal 'ishmael', b.captain.name
36
+ b.captain.name = 'call me ishmael'
37
+ b.save!
38
+
39
+ b = Boat.find_by_name('pequod')
40
+ assert_equal 'call me ishmael', b.captain.name
41
+ end
42
+
43
+ ['string', {:key1 => 'v', :key2 => [3,4]}].each do |captain|
44
+ define_method "test_#{encoding}_for_#{captain}_changed?" do
45
+ b = Boat.create(:captain => captain)
46
+ assert !b.changed?
47
+ b.captain = b.captain
48
+ assert !b.changed?
49
+ b.captain = captain
50
+ assert !b.changed?
51
+ b.captain = captain.dup
52
+ assert !b.changed?
53
+ b.captain = case captain
54
+ when String; captain.reverse
55
+ when Hash; captain.merge(:key2 => [1,2])
56
+ end
57
+ assert b.changed?
58
+ end
59
+ end
60
+
61
+ [ :none, :read, :read_write, :write ].each do |action|
62
+
63
+ define_method "test_#{encoding}_partial_update_#{action}" do
64
+ set_encoding(encoding)
65
+
66
+ Boat.delete_all
67
+
68
+ b1 = Boat.create :name => 'titanic', :tonnage => 1000
69
+ b1.captain = Person.new 'smith'
70
+ b1.save!
71
+
72
+ b1 = Boat.find_by_name 'titanic'
73
+ b1.tonnage += 1000
74
+ if [:read, :read_write].include?(action)
75
+ b1.captain
76
+ end
77
+ if [:read_write, :write].include?(action)
78
+ b1.captain = Person.new 'dawson'
79
+ end
80
+
81
+ b2 = Boat.find_by_name 'titanic'
82
+ b2.captain = Person.new 'rose'
83
+ b2.save!
84
+
85
+ b1.save! # Potentially overwriting save!
86
+
87
+ b3 = Boat.find_by_name 'titanic'
88
+ if [:read_write, :write].include?(action)
89
+ assert_equal 'dawson', b3.captain.name
90
+ elsif action == :none
91
+ assert_equal 'rose', b3.captain.name
92
+ elsif action == :read
93
+ assert %w{ rose dawson }.include?(b3.captain.name),
94
+ "captain.name is #{b3.captain.name}"
95
+ end
96
+ end
97
+ end
98
+
99
+ define_method "test_#{encoding}_just_save" do
100
+ set_encoding(encoding)
101
+ b1 = Boat.new :name => 'alinghi'
102
+ b1.save!
103
+ assert_equal nil, Boat.find_by_name('alinghi').captain
104
+ end
105
+
106
+ define_method "test_#{encoding}_just_save_writing" do
107
+ set_encoding(encoding)
108
+ b1 = Boat.new :name => 'surprise'
109
+ b1.captain = Person.new 'aubrey'
110
+ b1.save!
111
+ assert_equal 'aubrey', Boat.find_by_name('surprise').captain.name
112
+ end
113
+
114
+ define_method "test_#{encoding}_just_reading" do
115
+ set_encoding(encoding)
116
+ b1 = Boat.create :name => 'black pearl', :captain => Person.new('sparrow')
117
+ b2 = Boat.find_by_name 'black pearl'
118
+ assert_equal 'sparrow', b2.captain.name
119
+ end
120
+
121
+ define_method "test_#{encoding}_deep_change" do
122
+ set_encoding(encoding)
123
+ b1 = Boat.create :name => 'bounty', :captain => (Person.new('bligh'))
124
+
125
+ b2 = Boat.find_by_name 'bounty'
126
+ b2.captain.hat = Hat.new('blue')
127
+ b2.save!
128
+
129
+ assert_equal 'blue', Boat.find_by_name('bounty').captain.hat.color
130
+ end
131
+
132
+ define_method "test_#{encoding}_deeper_change_object" do
133
+ set_encoding(encoding)
134
+ b1 = Boat.create :name => 'bounty', :captain => (Person.new('bligh'))
135
+
136
+ b2 = Boat.find_by_name 'bounty'
137
+ b2.captain.pocket = [ :pounds ]
138
+ b2.save!
139
+
140
+ b2 = Boat.find_by_name 'bounty'
141
+ b2.captain.pocket << :francs
142
+ b2.save!
143
+
144
+ assert_equal [ :pounds, :francs ], Boat.find_by_name('bounty').captain.pocket
145
+ end
146
+
147
+ define_method "test_#{encoding}_deeper_change_hash" do
148
+ set_encoding(encoding)
149
+ b1 = Boat.create :name => 'bounty', :captain => { :name => :bligh }
150
+
151
+ b2 = Boat.find_by_name 'bounty'
152
+ b2.captain[:pocket] = [ :pounds ]
153
+ b2.save!
154
+
155
+ b2 = Boat.find_by_name 'bounty'
156
+ b2.captain[:pocket] << :francs
157
+ b2.save!
158
+
159
+ assert_equal [ :pounds, :francs ], Boat.find_by_name('bounty').captain[:pocket]
160
+ end
161
+
162
+
163
+ define_method "test_#{encoding}_class_checking" do
164
+ set_encoding(encoding)
165
+ Boat.create :name => 'lollypop', :cargo => Blob.new
166
+ Boat.find_by_name 'lollypop'
167
+
168
+ Boat.create :name => 'minerva', :cargo => "WRONG KIND"
169
+ assert_raise ActiveRecord::SerializationTypeMismatch do
170
+ Boat.find_by_name('minerva').cargo
171
+ end
172
+ end
173
+
174
+ define_method "test_#{encoding}_simple_hash" do
175
+ set_encoding(encoding)
176
+ b = Boat.new
177
+ h = { :name => 'skipper' }
178
+ b.captain = h
179
+ assert_equal h, b.captain
180
+ b.save
181
+ assert_equal h, b.captain
182
+ b.captain[:actor] = 'alan hale'
183
+ assert_equal 'alan hale', b.captain[:actor]
184
+ end
185
+
186
+ define_method "test_#{encoding}_check_if_really_saved" do
187
+ set_encoding(encoding)
188
+ b = Boat.new
189
+ b.captain = { :name => 'ramius' }
190
+ b.save
191
+
192
+ b2 = Boat.find(b.id)
193
+ assert_equal b.captain, b2.captain
194
+ b2.captain[:nationality] = 'russian'
195
+ b2.save
196
+
197
+ b3 = Boat.find(b2.id)
198
+ assert_equal 'russian', b3.captain[:nationality]
199
+ end
200
+
201
+ define_method "test_#{encoding}_array_cerealize" do
202
+ set_encoding(encoding)
203
+ b = Boat.new
204
+ b.captain = [123, 456]
205
+ b.save
206
+ assert_equal [123, 456], b.captain
207
+ assert_equal [123, 456], Boat.find(b.id).captain
208
+ end
209
+
210
+ define_method "test_#{encoding}_repeated_saves" do
211
+ set_encoding(encoding)
212
+ b = Boat.new
213
+ b.captain = { :name => 'kirk', :age => 23 }
214
+ b.save
215
+
216
+ assert_equal 23, b.captain[:age]
217
+ assert_equal 23, Boat.first.captain[:age]
218
+
219
+ b.captain[:age] += 1
220
+ b.save
221
+ assert_equal 24, b.captain[:age]
222
+ assert_equal 24, Boat.first.captain[:age]
223
+
224
+ b.captain[:age] += 1
225
+ b.save
226
+ b.captain[:age] += 1
227
+ b.save
228
+ assert_equal 26, b.captain[:age]
229
+ assert_equal 26, Boat.first.captain[:age]
230
+
231
+ b.captain[:age] += 1
232
+ b.save
233
+ b.captain[:age] += 1
234
+ b.save
235
+ b.captain[:age] += 1
236
+ b.save
237
+ assert_equal 29, b.captain[:age]
238
+ assert_equal 29, Boat.first.captain[:age]
239
+
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems' if RUBY_VERSION < '1.9.1'
4
+ require 'cerealize'
5
+
6
+ require 'test/stub'
7
+ require 'test/helper'
8
+ require 'test/helper_active_record'
9
+
10
+ require 'test/unit'
11
+
12
+ class BasicTest < Test::Unit::TestCase
13
+ def setup
14
+ Boat.delete_all
15
+ end
16
+
17
+ def teardown
18
+ end
19
+
20
+ def test_encoding_yaml
21
+ set_encoding(:yaml)
22
+ Boat.create :name => 'yamato', :captain => Person.new('kosaku')
23
+ s = Boat.connection.select_value("SELECT captain FROM boats WHERE name='yamato';")
24
+ assert s[0..2] = '---'
25
+ end
26
+
27
+ def test_encoding_marshal
28
+ set_encoding(:marshal)
29
+ Boat.create :name => 'santa maria', :captain => Person.new('columbus')
30
+ s = Boat.connection.select_value("SELECT captain FROM boats WHERE name='santa maria';")
31
+ assert s[0..1] = 'BA'
32
+ end
33
+
34
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems' if RUBY_VERSION < '1.9.1'
4
+ require 'cerealize'
5
+
6
+ require 'test/stub'
7
+ require 'test/helper'
8
+ require 'test/helper_active_record'
9
+
10
+ require 'test/unit'
11
+
12
+ class TranscodeTest < Test::Unit::TestCase
13
+ def setup
14
+ Cat.delete_all
15
+ end
16
+
17
+ def teardown
18
+ end
19
+
20
+ def test_no_such_codec
21
+ assert_raise Cerealize::NoSuchCodec do
22
+ Cat.dup.send(:cerealize, :bad, Hash, :encoding => :blah)
23
+ end
24
+ end
25
+
26
+ def test_no_suitable_codec_if_force_encoding
27
+ cat = Cat.new
28
+ cat[:tail] = '---'
29
+ cat.save
30
+ id = cat.id
31
+ assert_raise Cerealize::NoSuitableCodec do
32
+ Cat.find(id).tail
33
+ end
34
+ end
35
+
36
+ def test_auto_transcode_yaml
37
+ name = 'Nine Tails'
38
+ marshaled_name = Cerealize::Codec::Marshal.encode(name)
39
+ cat = Cat.new
40
+ cat[:name] = marshaled_name
41
+ cat.save
42
+ id = cat.id
43
+
44
+ new_cat = Cat.find(id)
45
+ assert_equal name, new_cat.name
46
+ new_cat.name.reverse!
47
+
48
+ # no change if not saved yet
49
+ assert_equal marshaled_name, new_cat[:name]
50
+ new_cat.save
51
+
52
+ # should be transcode into YAML
53
+ assert Cerealize::Codec::Yaml.yours?(new_cat[:name])
54
+ new_cat.reload
55
+ assert Cerealize::Codec::Yaml.yours?(new_cat[:name])
56
+ end
57
+
58
+ def test_auto_transcode_marshal
59
+ food = {:a => :b}
60
+ yamled_food = Cerealize::Codec::Yaml.encode(food)
61
+ cat = Cat.new
62
+ cat[:food] = yamled_food
63
+ cat.save
64
+ cat.reload
65
+
66
+ assert_equal food, cat.food
67
+ assert_equal yamled_food, cat[:food]
68
+ cat.food.merge!(:c => :d)
69
+ cat.save
70
+ cat.reload
71
+
72
+ assert Cerealize::Codec::Marshal.yours?(cat[:food])
73
+ assert_equal food.merge(:c => :d), cat.food
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cerealize
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 8
8
+ - 0
9
+ version: 0.8.0
10
+ platform: ruby
11
+ authors:
12
+ - Cardinal Blue
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-19 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 3
30
+ - 5
31
+ version: 2.3.5
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: bones
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 3
43
+ - 4
44
+ - 1
45
+ version: 3.4.1
46
+ type: :development
47
+ version_requirements: *id002
48
+ description: " Serialize out of the Cerealize Box\n - a drop-in replacement for ActiveRecord's serialize\n\n It can auto transcode old encoding (yaml if you're using AR's serialize),\n to new encoding (marshal, json, you name it) without any migration.\n\n Current supported encoding:\n 1. YAML\n 2. Marshal\n 3. JSON (planned)\n\n Current supported ORM:\n 1. ActiveRecord (tested with 2.3.5)\n 2. DataMapper (planned)"
49
+ email: dev (XD) cardinalblue.com
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files:
55
+ - CHANGES
56
+ - LICENSE
57
+ - README
58
+ - Rakefile
59
+ - TODO
60
+ files:
61
+ - CHANGES
62
+ - LICENSE
63
+ - README
64
+ - Rakefile
65
+ - TODO
66
+ - init.rb
67
+ - lib/cerealize.rb
68
+ - lib/cerealize/codec/marshal.rb
69
+ - lib/cerealize/codec/yaml.rb
70
+ - lib/cerealize/version.rb
71
+ - test/helper.rb
72
+ - test/helper_active_record.rb
73
+ - test/stub.rb
74
+ - test/test_all_codec.rb
75
+ - test/test_basic.rb
76
+ - test/test_transcode.rb
77
+ has_rdoc: true
78
+ homepage: http://github.com/cardinalblue/cerealize
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options:
83
+ - --main
84
+ - README
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ segments:
99
+ - 0
100
+ version: "0"
101
+ requirements: []
102
+
103
+ rubyforge_project: cerealize
104
+ rubygems_version: 1.3.6
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Serialize out of the Cerealize Box - a drop-in replacement for ActiveRecord's serialize It can auto transcode old encoding (yaml if you're using AR's serialize), to new encoding (marshal, json, you name it) without any migration
108
+ test_files:
109
+ - test/test_all_codec.rb
110
+ - test/test_basic.rb
111
+ - test/test_transcode.rb