Permalink
Browse files

Database-back the in-memory deserialized representations of objects

  • Loading branch information...
1 parent 4f5935e commit f034ac9c98e7fcc74b8c3a922ab1ab24cad53045 @RISCfuture committed Feb 22, 2011
Showing with 103 additions and 40 deletions.
  1. +1 −0 .gitignore
  2. +1 −0 Gemfile
  3. +2 −0 Gemfile.lock
  4. +2 −2 README.textile
  5. +54 −12 lib/json_serialize.rb
  6. +29 −25 spec/json_serialize_spec.rb
  7. +14 −1 spec/spec_helper.rb
View
@@ -19,6 +19,7 @@ rdoc
pkg
.bundle
.rvmrc
+test.sqlite
## PROJECT::DOCUMENTATION
.yardoc
View
@@ -9,4 +9,5 @@ group :development do
gem 'yard'
gem 'RedCloth', require: 'redcloth'
gem 'rspec'
+ gem 'sqlite3'
end
View
@@ -30,6 +30,7 @@ GEM
rspec-expectations (2.5.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.5.0)
+ sqlite3 (1.3.3)
tzinfo (0.3.24)
yard (0.6.4)
@@ -42,4 +43,5 @@ DEPENDENCIES
activesupport
jeweler
rspec
+ sqlite3
yard
View
@@ -19,12 +19,12 @@ Firstly, add the gem to your Rails project's @Gemfile@:
gem 'json_serialize'
</code></pre>
-Then, extend your model with the @JsonSerialize@ module, and call the
+Then, include into your model the @JsonSerialize@ module, and call the
@json_serialize@ method to indicate which fields should be serialized:
<pre><code>
class MyModel < ActiveRecord::Base
- extend JsonSerialize
+ include JsonSerialize
json_serialize :favorites, :preferences
end
</code></pre>
View
@@ -2,27 +2,69 @@
#
# @example Basic usage
# class MyModel < ActiveRecord::Base
-# extend JsonSerialize
+# include JsonSerialize
# json_serialize :some_field
# end
module JsonSerialize
+ extend ActiveSupport::Concern
- # @overload json_serialize(field, ...)
- # Marks one or more fields as JSON-serialized. These fields are stored in
- # the database as JSON and encoded/decoded automatically upon read/write.
- # @param [Symbol] field The database field to JSON-serialize.
+ module ClassMethods
+ # @overload json_serialize(field, ...)
+ # Marks one or more fields as JSON-serialized. These fields are stored in
+ # the database as JSON and encoded/decoded automatically upon read/write.
+ # @param [Symbol] field The database field to JSON-serialize.
- def json_serialize(*fields)
- fields.each do |field|
- define_method field do
- value = read_attribute(field)
- value.nil? ? nil : ActiveSupport::JSON.decode(value)
+ def json_serialize(*fields)
+ fields.each do |field|
+ define_method field do
+ if instance_variable_defined?(field_ivar(field)) then
+ instance_variable_get field_ivar(field)
+ else
+ encoded = read_attribute(field)
+ decoded = encoded.nil? ? nil : ActiveSupport::JSON.decode(encoded)
+ instance_variable_set field_ivar(field), decoded
+ decoded
+ end
+ end
+
+ define_method :"#{field}=" do |value|
+ write_attribute field, (value.nil? ? nil : ActiveSupport::JSON.encode(value))
+ instance_variable_set field_ivar(field), value
+ end
end
- define_method :"#{field}=" do |value|
- write_attribute field, (value.nil? ? nil : ActiveSupport::JSON.encode(value))
+ define_method :serialize_json_values do
+ fields.each do |field|
+ if instance_variable_defined?(field_ivar(field)) then
+ send :"#{field}=", instance_variable_get(field_ivar(field))
+ end
+ end
end
+
+ define_method :reload_with_refresh_json_ivars do |*args|
+ res = reload_without_refresh_json_ivars *args
+ fields.each { |field| remove_instance_variable field_ivar(field) if instance_variable_defined?(field_ivar(field)) }
+ res
+ end
+
+ define_method :update_with_refresh_json_ivars do |*args|
+ res = update_without_refresh_json_ivars(*args)
+ fields.each { |field| remove_instance_variable field_ivar(field) if instance_variable_defined?(field_ivar(field)) }
+ res
+ end
+
+ before_validation :serialize_json_values
+ alias_method_chain :reload, :refresh_json_ivars
+ alias_method_chain :update, :refresh_json_ivars
+ end
+ end
+
+ module InstanceMethods
+ private
+
+ def field_ivar(name)
+ :"@_deserialized_#{name}"
end
end
end
@@ -1,43 +1,47 @@
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
-module SpecSupport
- class JsonSerializeTester
- extend JsonSerialize
- json_serialize :field
- def read_attribute(field) @value end
- def write_attribute(field, value) @value = value end
- def get() @value end
- def set(value) @value = value end
- end
-end
-
describe JsonSerialize do
- before :each do
- @object = SpecSupport::JsonSerializeTester.new
- end
-
describe "#json_serialize" do
- context "getter" do
+ context "[getter]" do
it "should JSON-decode the value" do
- @object.set(ActiveSupport::JSON.encode({ foo: 'bar' }))
- @object.field.should eql({ 'foo' => 'bar' })
+ require 'logger'
+ object = Json.create!
+ Json.update_all(data: '{"foo":"bar"}')
+ object.reload.data.should eql('foo' => 'bar')
end
it "should return nil if the value is nil" do
- @object.set nil
- @object.field.should be_nil
+ object = Json.create!
+ Json.update_all(data: nil)
+ object.reload.data.should be_nil
end
end
- context "setter" do
+ context "[setter]" do
it "should JSON-encode the value" do
- @object.field = { foo: 'bar' }
- @object.get.should eql({ foo: 'bar' }.to_json)
+ object = Json.create!(data: { foo: 'bar' })
+ object.send(:read_attribute, :data).should eql({ foo: 'bar' }.to_json)
end
it "should leave nil as nil" do
- @object.field = nil
- @object.get.should be_nil
+ object = Json.create!(data: nil)
+ object.send(:read_attribute, :data).should be_nil
+ end
+ end
+
+ context "[database backing]" do
+ it "should back the instance object with the database" do
+ object = Json.create!(data: { foo: 'bar' })
+ object.data[:foo2] = 'bar2'
+ object.save!
+ object.data.should eql('foo' => 'bar', 'foo2' => 'bar2')
+ end
+
+ it "should clear the in-memory reference on reload" do
+ object = Json.create!(data: { foo: 'bar' })
+ object.data[:foo2] = 'bar2'
+ object.reload
+ object.data.should eql('foo' => 'bar')
end
end
end
View
@@ -5,6 +5,19 @@
require 'json_serialize'
+ActiveRecord::Base.establish_connection(
+ adapter: 'sqlite3',
+ database: 'test.sqlite'
+)
+
+class Json < ActiveRecord::Base
+ include JsonSerialize
+ json_serialize :data
+end
+
RSpec.configure do |config|
-
+ config.before(:each) do
+ Json.connection.execute "DROP TABLE IF EXISTS jsons"
+ Json.connection.execute "CREATE TABLE jsons (id INTEGER PRIMARY KEY ASC, data TEXT)"
+ end
end

0 comments on commit f034ac9

Please sign in to comment.