chikamichi-marshalize 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *~
2
+ log/*.log
3
+ tmp/**/*
4
+
data/COPYING ADDED
@@ -0,0 +1,14 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar
5
+ 14 rue de Plaisance, 75014 Paris, France
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
14
+
@@ -0,0 +1,18 @@
1
+ = 1.0
2
+
3
+ == 1.0.4
4
+
5
+ - gemspc with Jeweler!
6
+
7
+ == 1.0.2, 1.0.3
8
+
9
+ - gempsec, again
10
+
11
+ == 1.0.1
12
+
13
+ - gemspec
14
+
15
+ == 1.0.0
16
+
17
+ - initial release
18
+ - documentation
@@ -0,0 +1,47 @@
1
+ = Marshalize
2
+
3
+ This Rails plugin provides serialization using Marshal[http://ruby-doc.org/core/classes/Marshal.html] in the same way Rails provides builtin serialization using YAML. In a model, just do
4
+ class Robot < ActiveRecord::Base
5
+ marshalize :features
6
+ end
7
+ and your app will automagically handle the +features+ attribute using Marshal, dumping when saving to the database, loading when fetching from it.
8
+
9
+ You can register any kind object (not just arrays and hashes...). Be aware that Marshal defines a binary format, which may change in incoming Ruby releases and is currently not portable outside the Ruby scripting world. For a portable yet slower alternatives, you may try JSON or YML serializers.
10
+
11
+ == Example
12
+
13
+ class Robot < ActiveRecord::Base
14
+
15
+ marshalize :parameters # The "parameters" attribute will be saved as binary data
16
+
17
+ marshalize :features, RobotFeature # You can define what kind of object is to be marshalized
18
+ # An error will be raised if another class is provided for
19
+ # this attribute
20
+
21
+ serialize :status, Array # Marshalization plays well with YAML classic serialization
22
+
23
+ end
24
+
25
+ == Installation
26
+
27
+ === as a plugin:
28
+
29
+ In your Rails application root directory:
30
+
31
+ script/plugin install git://github.com/chikamichi/marshalize.git
32
+
33
+ === as a gem:
34
+
35
+ Not yet!
36
+
37
+ No rake task needed.
38
+
39
+ == TODO
40
+
41
+ * +super+ stuff
42
+ * manage blobs, not just text field (buffer limitations)
43
+ * rake tasks to manage marshalized data recovering in case Marshal went astray (say, a new version breaking things down)
44
+
45
+ == License
46
+
47
+ Released under the WTFPL (http://sam.zoy.org/wtfpl/COPYING)
@@ -0,0 +1,29 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the marshalize plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ begin
17
+ require 'jeweler'
18
+ Jeweler::Tasks.new do |gemspec|
19
+ gemspec.name = "marshalize"
20
+ gemspec.summary = %q{Marshalize is a Rails plugin enabling ActiveRecord attributes serialization using the Marshal binary converter library.}
21
+ gemspec.description = %q{This Rails plugin provides serialization using Marshal in the same way Rails provides builtin serialization using YAML. You can register any kind object (not just arrays and hashes…). Be aware that Marshal defines a binary format, which may change in incoming Ruby releases and is currently not portable outside the Ruby scripting world. For a portable yet slower alternatives, you may try JSON or YML serializers.}
22
+ gemspec.email = "jd@vauguet.fr"
23
+ gemspec.homepage = "http://github.com/chikamichi/marshalize"
24
+ gemspec.authors = ["Jean-Denis Vauguet"]
25
+ end
26
+ rescue LoadError
27
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
28
+ end
29
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.4
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ ./script/generate marshalize Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,8 @@
1
+ class MarshalizeGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ # m.directory "lib"
5
+ # m.template 'README', "README"
6
+ end
7
+ end
8
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ # Include hook code here
File without changes
@@ -0,0 +1,283 @@
1
+ require 'activesupport'
2
+
3
+ # This module is split in several submodules for selective inclusion purpose
4
+ module Marshalization #:nodoc:
5
+
6
+ VERSION = 0.1
7
+
8
+ module Dirty #:nodoc:
9
+
10
+ private
11
+
12
+ def update_with_marshalization
13
+ if partial_updates?
14
+ # Serialized and marshalized attributes should always be written in case they've been
15
+ # changed in place.
16
+ update_without_dirty(self.class.marshalized_attributes.keys)
17
+ else
18
+ update_without_dirty
19
+ end
20
+
21
+ update_with_dirty # an implicit call to super, which would fail as it is a private method
22
+ # update_with_dirty is not
23
+ end
24
+
25
+ def self.included(receiver)
26
+ receiver.alias_method_chain :update, :marshalization
27
+ end
28
+ end
29
+
30
+ #module Timestamp #:nodoc:
31
+
32
+ #private
33
+
34
+ #def update_with_timestamps(*args) #:nodoc:
35
+ #if record_timestamps && (!partial_updates? || changed?)
36
+ #t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
37
+ #write_attribute('updated_at', t) if respond_to?(:updated_at)
38
+ #write_attribute('updated_on', t) if respond_to?(:updated_on)
39
+ #end
40
+ #update_without_timestamps()
41
+ #end
42
+ #end
43
+
44
+ module Base # :nodoc:
45
+
46
+ marshalized_attributes ||= Hash.new
47
+
48
+ module ClassMethods #:nodoc:
49
+
50
+ # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
51
+ # then specify the name of that attribute using this method and it will be handled automatically.
52
+ # The serialization is done through Marshal. If +class_name+ is specified, the serialized object must be of that
53
+ # class on retrieval or SerializationTypeMismatch will be raised.
54
+ #
55
+ # ==== Parameters
56
+ #
57
+ # * +attr_name+ - The field name that should be serialized.
58
+ # * +class_name+ - Optional, class name that the object type should be equal to.
59
+ #
60
+ # ==== Example
61
+ # # Serialize a preferences attribute
62
+ # class User
63
+ # serialize :preferences
64
+ # end
65
+ def marshalize(attr_name, class_name = Object)
66
+ attr_name = attr_name.to_s
67
+ marshalized_attributes[attr_name] = class_name
68
+ end
69
+
70
+ # Returns a hash of all the attributes that have been specified for serialization as keys and their class restriction as values.
71
+ def marshalized_attributes
72
+ read_inheritable_attribute(:attr_marshalized) or write_inheritable_attribute(:attr_marshalized, {})
73
+ end
74
+
75
+ end # class: Marshalization::Base::ClassMethods
76
+
77
+ module InstanceMethods #:nodoc:
78
+
79
+ # Encode an attribute with ActiveSupport::Base64.encode64
80
+ def encode_attribute(attribute)
81
+ ActiveSupport::Base64.encode64(attribute)
82
+ end
83
+
84
+ # Decode an attribute with ActiveSupport::Base64.decode64
85
+ def decode_attribute(attribute)
86
+ ActiveSupport::Base64.decode64(attribute)
87
+ end
88
+
89
+ # Returns a copy of the attributes hash where all the values have been safely quoted for use in
90
+ # an SQL statement.
91
+ def attributes_with_quotes_with_marshalization(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
92
+
93
+ quoted = {}
94
+ connection = self.class.connection
95
+ attribute_names.each do |name|
96
+ if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
97
+
98
+ if self.class.marshalized_attributes.has_key?(name)
99
+ # smart-quoting Marshal-dumped values
100
+ quoted[name] = "'#{@attributes[name].to_s}'"
101
+ else
102
+ value = read_attribute(name)
103
+
104
+ # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
105
+ if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))
106
+ value = value.to_yaml
107
+ end
108
+
109
+ quoted[name] = connection.quote(value, column)
110
+ end
111
+ end
112
+ end
113
+ include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
114
+
115
+ # TODO in order to get rid of the aliasing, handle marshalized attributes, then call super
116
+ # passing all attributes but marshalized's. In fact, do it the reverse order so that the
117
+ # quoted hash is not made empty in super after we filled it
118
+
119
+ #attribute_names.delete_if { |attribute| self.class.marshalized_attributes.has_key?(attribute) }
120
+ ##do stuff with marshalized_attributes.each -> quoted[name]
121
+ #puts "~~~~~~"
122
+ #puts attribute_names
123
+ #super(true, true, attribute_names)
124
+
125
+ end
126
+ end # module: Marshalization::Base::InstanceMethods
127
+
128
+ def self.included(receiver)
129
+ receiver.send :include, InstanceMethods
130
+ receiver.send :extend, ClassMethods
131
+
132
+ unless receiver.respond_to?(:attributes_with_quotes_without_marshalization)
133
+ receiver.instance_eval do
134
+ alias_method_chain :attributes_with_quotes, :marshalization
135
+ end
136
+ end
137
+
138
+ #unless receiver.respond_to?(:update_with_dirty_without_marshalization)
139
+ #receiver.instance_eval do
140
+ #alias_method_chain :update_with_dirty, :marshalization
141
+ #end
142
+ #end
143
+ end
144
+
145
+ end # module: Marshalization::Base
146
+
147
+ module AttributeMethods
148
+
149
+ module ClassMethods
150
+
151
+ # This refactoring may break with incoming Rails releases since there is no call to super.
152
+ # See below for some explanations -- IMO it's too much work for a plugin, so this method
153
+ # is kind of a bold patch in itself
154
+ def define_attribute_methods
155
+ return if generated_methods?
156
+ columns_hash.each do |name, column|
157
+ if instance_method_already_implemented?(name)
158
+ else
159
+ if self.serialized_attributes[name]
160
+ define_read_method_for_serialized_attribute(name)
161
+ elsif self.marshalized_attributes[name]
162
+ define_read_method_for_marshalized_attribute(name)
163
+ elsif create_time_zone_conversion_attribute?(name, column)
164
+ define_read_method_for_time_zone_conversion(name)
165
+ else
166
+ define_read_method(name.to_sym, name, column)
167
+ end
168
+ end
169
+
170
+ unless instance_method_already_implemented?("#{name}=")
171
+ if create_time_zone_conversion_attribute?(name, column)
172
+ define_write_method_for_time_zone_conversion(name)
173
+ elsif self.marshalized_attributes[name]
174
+ define_write_method_for_marshalized_attribute(name.to_sym)
175
+ else
176
+ define_write_method(name.to_sym)
177
+ end
178
+ end
179
+
180
+ unless instance_method_already_implemented?("#{name}?")
181
+ define_question_method(name)
182
+ end
183
+ end
184
+
185
+ # attempt to have a call to super...
186
+ # it implies refactoring much of the columns handling in base.rb,
187
+ # because super (the original define_attribute_methods) calls
188
+ # several helpers which mess up with the prior marshalized
189
+ # attributes definition process: columns_hash(), calling columns(),
190
+ # hence the current connection.
191
+ #
192
+ # that's why it's a TODO and will probably stay as it
193
+
194
+ #marshalized_attributes.each do |name, class_name|
195
+ #unless instance_method_already_implemented?(name)
196
+ #define_read_method_for_marshalized_attribute(name)
197
+ #end
198
+
199
+ #unless instance_method_already_implemented?("#{name}=")
200
+ #define_write_method_for_marshalized_attribute(name.to_sym)
201
+ #end
202
+
203
+ #unless instance_method_already_implemented?("#{name}?")
204
+ #define_question_method(name)
205
+ #end
206
+ #end
207
+
208
+ #generated_methods.clear
209
+ #@columns_hash.delete_if {|column_name, column| marshalized_attributes.has_key?(column_name) }
210
+ #super
211
+ #columns_hash
212
+ ## TODO must add the suffix versions (= ?)
213
+ #generated_methods.merge(marshalized_attributes.keys)
214
+ end
215
+
216
+ # Define read method for marshalized attribute.
217
+ def define_read_method_for_marshalized_attribute(attr_name)
218
+ evaluate_attribute_method attr_name, "def #{attr_name}; unmarshalize_attribute('#{attr_name.to_sym}'); end"
219
+ end
220
+
221
+ def define_write_method_for_marshalized_attribute(attr_name)
222
+ evaluate_attribute_method attr_name, "def #{attr_name}=(new_value); marshalize_attribute('#{attr_name.to_sym}', new_value); end", "#{attr_name}="
223
+ end
224
+
225
+ end # class: Marshalization::AttributeMethods::ClassMethods
226
+
227
+ # Initialize an empty marshalized attribute.
228
+ # Prevents initialization error (marshal: data too short)
229
+ def initialize_marshalized_attribute(name, class_name)
230
+ @attributes[name] = encode_attribute(Marshal.dump(class_name.new))
231
+ end
232
+
233
+ def read_attribute(attr_name)
234
+ attr_name = attr_name.to_s
235
+ if !(value = @attributes[attr_name]).nil?
236
+ if column = column_for_attribute(attr_name)
237
+ if unmarshalizable_attribute?(attr_name, column)
238
+ unmarshalize_attribute(attr_name.to_sym)
239
+ else
240
+ super(attr_name.to_sym)
241
+ end
242
+ end
243
+ else
244
+ end
245
+ end
246
+
247
+ def marshalize_attribute(attr_name, value)
248
+ attr_name = attr_name.to_s
249
+ @attributes_cache.delete(attr_name)
250
+ if self.class.marshalized_attributes[attr_name]
251
+ dumped = encode_attribute(Marshal.dump(value))
252
+ @attributes[attr_name] = dumped
253
+ end
254
+ end
255
+
256
+ # Returns the unmarshalized object of the attribute.
257
+ def unmarshalize_attribute(attr_name)
258
+ attr_name = attr_name.to_s
259
+ if @attributes[attr_name].nil? || @attributes[attr_name] == ''
260
+ initialize_marshalized_attribute(attr_name, self.class.marshalized_attributes[attr_name])
261
+ end
262
+ unmarshalized_object = Marshal.load(decode_attribute(@attributes[attr_name]))
263
+
264
+ if unmarshalized_object.is_a?(self.class.marshalized_attributes[attr_name]) || unmarshalized_object.nil?
265
+ return unmarshalized_object
266
+ else
267
+ raise ActiveRecord::SerializationTypeMismatch,
268
+ "#{attr_name} was supposed to be a #{self.class.marshalized_attributes[attr_name]}, but was a #{unmarshalized_object.class.to_s}"
269
+ end
270
+ end
271
+
272
+ # Returns true if the attribute is of a text column and marked for marshalization.
273
+ def unmarshalizable_attribute?(attr_name, column)
274
+ column.text? && self.class.marshalized_attributes[attr_name]
275
+ end
276
+
277
+ def self.included(receiver)
278
+ receiver.send :extend, ClassMethods
279
+ end
280
+
281
+ end # module: Marshalization::AttributeMethods
282
+
283
+ end # module: Marshalization
@@ -0,0 +1,5 @@
1
+ require 'marshalize'
2
+
3
+ ActiveRecord::Base.send :include, Marshalization::Base
4
+ ActiveRecord::Base.send :include, Marshalization::AttributeMethods
5
+ ActiveRecord::Base.send :include, Marshalization::Dirty
@@ -0,0 +1,21 @@
1
+ sqlite:
2
+ :adapter: sqlite
3
+ :dbfile: vendor/plugins/marshalize/test/marshalize_plugin.sqlite.db
4
+
5
+ sqlite3:
6
+ :adapter: sqlite3
7
+ :dbfile: vendor/plugins/marshalize/test/marshalize_plugin.sqlite3.db
8
+
9
+ postgresql:
10
+ :adapter: postgresql
11
+ :username: jd
12
+ :password: zpFrKad8
13
+ :database: test_plugin_marshalize
14
+ :min_messages: ERROR
15
+
16
+ #mysql:
17
+ #:adapter: mysql
18
+ #:host: localhost
19
+ #:username: jd
20
+ #:password: zpFrKad8
21
+ #:database: marshalize_plugin_test
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class ExtendActiveRecordTest < Test::Unit::TestCase
4
+
5
+ class Bird < ActiveRecord::Base
6
+ marshalize :songs
7
+ marshalize :locations, Array
8
+ serialize :friends, Hash
9
+ end
10
+
11
+ def test_marshalize_the_songs_attribute
12
+
13
+ load_schema
14
+
15
+
16
+ test_songs = {'Il fait beau' => 'texte de la chanson...', 'Perche sur une branche' => 'piou piou piou!'}
17
+
18
+ piou = Bird.new
19
+
20
+
21
+ piou.name = "Coco"
22
+ piou.songs = test_songs
23
+
24
+
25
+ piou.save!
26
+
27
+
28
+ retrieved = Bird.find_by_id piou.id
29
+
30
+
31
+ assert_equal test_songs, retrieved.songs
32
+
33
+ #piou.locations_will_change!
34
+ piou.name = "Bibi"
35
+ piou.songs = 23
36
+ test_locations = ["Milan", "Paris"]
37
+ piou.locations = test_locations
38
+ test_friends = {"toucan" => 2, "lark sparrow" => 10}
39
+ piou.friends = test_friends
40
+ piou.save!
41
+
42
+ retrieved = Bird.find_by_id piou.id
43
+
44
+ assert_equal "Bibi", retrieved.name
45
+
46
+ assert_kind_of(Array, retrieved.locations, "Locations are stored in an array.")
47
+ assert_equal test_locations, retrieved.locations
48
+
49
+ assert_kind_of(Hash, retrieved.friends, "Friends are stored in a hash.")
50
+ assert_equal test_friends, retrieved.friends
51
+
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :birds, :force => true do |t|
3
+ t.string :name
4
+ t.text :locations
5
+ t.text :songs
6
+ t.text :friends
7
+ #t.timestamps
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'active_support/test_case'
4
+
5
+ ENV['RAILS_ENV'] = 'test'
6
+ ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
7
+
8
+ require 'test/unit'
9
+ require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
10
+
11
+ def load_schema
12
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
13
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
14
+
15
+ db_adapter = ENV['DB']
16
+
17
+ # no db passed, try one of these fine config-free DBs before bombing.
18
+ db_adapter ||=
19
+ begin
20
+ require 'sqlite'
21
+ 'sqlite'
22
+ rescue MissingSourceFile
23
+ begin
24
+ require 'sqlite3'
25
+ 'sqlite3'
26
+ rescue MissingSourceFile
27
+ end
28
+ end
29
+
30
+ if db_adapter.nil?
31
+ raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
32
+ end
33
+
34
+ ActiveRecord::Base.establish_connection(config[db_adapter])
35
+ load(File.dirname(__FILE__) + "/schema.rb")
36
+ require File.dirname(__FILE__) + '/../rails/init.rb'
37
+ end
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chikamichi-marshalize
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Jean-Denis Vauguet
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-27 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: "This Rails plugin provides serialization using Marshal in the same way Rails provides builtin serialization using YAML. You can register any kind object (not just arrays and hashes\xE2\x80\xA6). Be aware that Marshal defines a binary format, which may change in incoming Ruby releases and is currently not portable outside the Ruby scripting world. For a portable yet slower alternatives, you may try JSON or YML serializers."
17
+ email: jd@vauguet.fr
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - ChangeLog.rdoc
24
+ - README.rdoc
25
+ files:
26
+ - .gitignore
27
+ - COPYING
28
+ - ChangeLog.rdoc
29
+ - README.rdoc
30
+ - Rakefile
31
+ - VERSION
32
+ - generators/marshalize/USAGE
33
+ - generators/marshalize/marshalize_generator.rb
34
+ - init.rb
35
+ - install.rb
36
+ - lib/marshalize.rb
37
+ - rails/init.rb
38
+ - test/database.yml
39
+ - test/marshalize_test.rb
40
+ - test/schema.rb
41
+ - test/test_helper.rb
42
+ - uninstall.rb
43
+ has_rdoc: false
44
+ homepage: http://github.com/chikamichi/marshalize
45
+ licenses:
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --charset=UTF-8
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.5
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Marshalize is a Rails plugin enabling ActiveRecord attributes serialization using the Marshal binary converter library.
70
+ test_files:
71
+ - test/schema.rb
72
+ - test/marshalize_test.rb
73
+ - test/test_helper.rb