Skip to content

Commit

Permalink
Document initialization from hash
Browse files Browse the repository at this point in the history
should lead to the same content as building document with []= setter.
Copy the values from the input parameter instead of just connecting
to the existing Hash.
  • Loading branch information
geekq committed Jan 12, 2010
1 parent 6a3b01b commit da86969
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 42 deletions.
10 changes: 6 additions & 4 deletions lib/couchtiny/delegate_doc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ class DelegateDoc
attr_accessor :doc

def initialize(doc = {})
@doc = doc.to_hash
# Copy the values from the input parameter.
@doc = {}
doc.to_hash.each {|k,v| self[k] = v}
end

def to_hash
Expand All @@ -28,15 +30,15 @@ def respond_to?(*m)
def [](k)
@doc[k.to_s]
end

def []=(k,v)
@doc[k.to_s] = v
end

def key?(k)
@doc.key?(k)
end

def has_key?(k)
@doc.has_key?(k)
end
Expand Down
11 changes: 8 additions & 3 deletions lib/couchtiny/property.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def self.property_builder(*names, &blk)
define_method("#{name}=") { |val| self[name] = val }
}
end

property_builder(String, :string) do |klass, name, opts|
klass.class_eval {
define_method(name) { self[name].to_s }
Expand Down Expand Up @@ -87,14 +87,19 @@ def self.property_builder(*names, &blk)
# method takes a single Hash)
#
# property :foo, :type=>Bar

def property(name, opts={})
type = opts[:type]
if builder = BUILDER_MAP[type]
builder.call(self, name, opts)
else
# some arbitrary object
define_method(name) { type.new(self[name] ||= {}) }
define_method(name) {
res = type.new
# connect to subhash
res.doc = self[name] ||= {}
res
}
define_method("#{name}=") { |val|
case val
when nil
Expand Down
93 changes: 58 additions & 35 deletions test/t-document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class TestDocument < Test::Unit::TestCase
assert @d.id
assert @d.rev
end

should "load respecting type_attr (under Foo)" do
d = Foo.get @d.id
f = Foo.get @f.id
Expand All @@ -117,7 +117,7 @@ class TestDocument < Test::Unit::TestCase
assert_equal Bar, b.class
assert_equal Foo, z.class
assert_equal Unattached, u.class

assert_equal "a", d["tag"]
assert_equal "b", f["tag"]
assert_equal "b2", g["tag"]
Expand Down Expand Up @@ -147,8 +147,8 @@ class TestDocument < Test::Unit::TestCase
assert_equal 4, res.size
assert_equal ["a","b","b2","c"], res.collect { |r| r['tag'] }
assert_equal [CouchTiny::Document, Foo, Foo, Bar], res.collect { |r| r.class }
end
end

should "save update" do
old_id = @f.id
old_rev = @f.rev
Expand All @@ -164,27 +164,27 @@ class TestDocument < Test::Unit::TestCase
Foo.get(@f.id)
}
end

context "attachments" do
should "save and retrieve attachment" do
@f.put_attachment "wibble", "foobar"
assert_equal "foobar", @f.get_attachment("wibble")
end

should "retrieve attachment info" do
@f.put_attachment "wibble", "abc"
f = Foo.get @f.id
assert f.has_attachment?("wibble")
assert !f.has_attachment?("bibble")
assert_equal 3, f.attachment_info("wibble")["length"]
end

should "save content type" do
@f.put_attachment "wibble", "foobar", "application/x-foo"
f = Foo.get @f.id
assert_equal "application/x-foo", f['_attachments']['wibble']['content_type']
end

should "destroy attachment" do
@f.put_attachment "wibble", "foobar"
@f.delete_attachment "wibble"
Expand All @@ -200,7 +200,7 @@ class TestDocument < Test::Unit::TestCase
Unattached.get @u.id
}
end

should "load on database" do
b = Unattached.on(Foo.database).get @u.id
assert_equal Unattached, b.class
Expand Down Expand Up @@ -233,7 +233,7 @@ class TestDocument < Test::Unit::TestCase
assert_equal ["id","key","value"], res.first.keys.sort
assert_equal ["a","b","b2","c","d","e"], res.collect {|r| r['key'] }
end

should "return docs" do
res = Foo.view_test_by_tag :include_docs=>true
assert_equal 6, res.size
Expand Down Expand Up @@ -296,26 +296,26 @@ class TestDocument < Test::Unit::TestCase
assert_equal "d", res[1]['tag']
end
end

context "all view" do
should "count" do
assert_equal 2, Foo.count
assert_equal 1, Bar.count
assert_equal 1, CouchTiny::Document.on(Foo.database).count # type=nil
end

# Note: these are quite inefficient as they force a re-reduce across
# the database. Better just to read the overall reduced value (see
# "count grouped" below) and add the elements required.
should "count with options" do
assert_equal 4, Foo.count(:startkey=>"e") # gives Foo x 2, Unattached, Zog
assert_equal 3, Foo.count(:keys=>["Foo","Bar"], :group=>true)
end

should "count all_classes" do
assert_equal 6, Foo.count(:all_classes=>true)
end

should "all" do
fs = Foo.all
assert_equal [Foo, Foo], fs.collect {|r| r.class}
Expand Down Expand Up @@ -360,21 +360,21 @@ class TestDocument < Test::Unit::TestCase
counts = Foo.all(:all_classes=>true, :reduce=>true).first['value']
assert_equal({'null'=>1, 'Bar'=>1, 'Foo'=>2, 'Zog'=>1, 'Unattached'=>1}, counts)
end

should "work on specified database" do
assert_equal 1, Unattached.on(Foo.database).count
assert_equal 1, Unattached.on(Foo.database).all.size
end
end
end

should "create!" do
res = Foo.create!('_id' => 'hello')
assert_equal Foo, res.class
assert_equal 'hello', res.id
Foo.get('hello')
end

context "bulk save" do
setup do
# Note that this works for documents not yet associated with
Expand Down Expand Up @@ -418,7 +418,7 @@ class TestDocument < Test::Unit::TestCase
Foo.bulk_save [n]
assert n['_id']
assert_equal "Foo", n['type']

m = Bar.get n['_id']
assert_equal Foo, m.class
end
Expand All @@ -437,7 +437,7 @@ class TestDocument < Test::Unit::TestCase
assert_equal Bar, b.class
assert_equal Foo, z.class
assert_equal Unattached, u.class

assert_equal "a", d["tag"]
assert_equal "b", f["tag"]
assert_equal "b2", g["tag"]
Expand All @@ -454,7 +454,7 @@ class TestDocument < Test::Unit::TestCase
assert_equal old_id, @f.id
assert_not_equal old_rev, @f.rev
end

should "bulk_destroy" do
assert_equal 6, Foo.count(:all_classes => true)
res = Foo.bulk_destroy [@b, @z]
Expand All @@ -478,11 +478,11 @@ class TestDocument < Test::Unit::TestCase
assert_equal 'type', Foo.type_attr
assert_equal 'Foo', Foo.type_name
end

should "have write accessors" do
begin
db = Foo.class_eval { instance_variable_get :@database }

Foo.use_database :dummy1
Foo.use_design_doc CouchTiny::Design.new('Foo-', true)
Foo.use_type_attr 'my-type'
Expand All @@ -506,7 +506,7 @@ class TestDocument < Test::Unit::TestCase
}
end
end

should "override in subclass only" do
begin
Bar.use_database :dummy1
Expand Down Expand Up @@ -540,7 +540,7 @@ class TestDocument < Test::Unit::TestCase
@klass.use_database Foo.database
@klass.database.recreate_database!
end

should "cleanup_design_docs!" do
id1 = @klass.design_doc.id

Expand All @@ -551,13 +551,13 @@ class TestDocument < Test::Unit::TestCase
@klass.define_view "view2", "function(doc){emit(null,null);}"
id3 = @klass.design_doc.id
@klass.view_view2

assert_equal 3, [id1, id2, id3].uniq.size
assert_equal [id2, id3].sort,
@klass.database.all_docs['rows'].map { |d| d['id'] }.sort

@klass.cleanup_design_docs!

assert_equal [id3], @klass.database.all_docs['rows'].map { |d| d['id'] }
end
end
Expand All @@ -574,11 +574,11 @@ class TestDocument < Test::Unit::TestCase
setup do
Foo.database.recreate_database!
end

should "invoke callbacks" do
@foo = CB.new("hello"=>"world","idattr"=>"12345")
assert_equal [:after_initialize], @foo.log

@foo.log.clear
@foo.save!
assert_equal "12345", @foo.id
Expand All @@ -590,13 +590,13 @@ class TestDocument < Test::Unit::TestCase

res = CB.get "12345"
assert_equal [:after_find, :after_initialize], res.log

@foo.log.clear
@foo.destroy
assert_equal [:before_destroy, :after_destroy], @foo.log
end
end

context "Callbacks with bulk_save" do
setup do
Foo.database.recreate_database!
Expand Down Expand Up @@ -663,7 +663,7 @@ def @c4.after_save
assert_equal [2,93,94], CB.all.collect { |c| c['val'] }.sort
end
end

should "have auto accessor" do
f = AA.new
f.hello = "world"
Expand All @@ -680,9 +680,9 @@ class ::Flurble < CouchTiny::Document; end
Object.send(:remove_const, :Flurble)
class ::Flurble < CouchTiny::Document; end
klass2 = ::Flurble

assert klass1 != klass2, "I expect a new Flurble class object"

res = CouchTiny::Document.on(Foo.database).get('test')
assert ::Flurble === res, "The loaded object should be of the new #{klass2} class (#{klass2.object_id}), but it was #{res.class} (#{res.class.object_id})"
end
Expand All @@ -693,7 +693,7 @@ class ::Flurble < CouchTiny::Document; end
assert_equal Bar, res.class
assert_equal "def", res["abc"]
end

should "default to model class" do
res = Foo.instantiate({"type"=>"junk", "abc"=>"def"})
assert_equal Foo, res.class
Expand Down Expand Up @@ -736,4 +736,27 @@ class ::Flurble < CouchTiny::Document; end
assert_equal ["eccles","moriarty"], res.collect { |r| r["friend"] }.sort
end
end

context "initialization from hash" do
should "lead to same the content as building document with []= setter." do
user1 = Foo.new()
user1[:email] = 'user1@example.com'
user1['age'] = 33
user1.save!
assert_not_nil user1[:email]
assert_not_nil user1['email']

user2 = Foo.new({
:email => 'user2@example.com',
'age' => 37
})
assert_not_nil user2[:email]
assert_not_nil user2['email']
assert_not_nil user2[:age]
assert_not_nil user2['age']
user2.save!
assert_not_nil user2[:email]
assert_not_nil user2['email']
end
end
end

0 comments on commit da86969

Please sign in to comment.