fauxsql 0.1.2 → 0.2.0
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.
- data/.gitignore +2 -0
- data/Gemfile +1 -1
- data/VERSION +1 -1
- data/fauxsql.gemspec +6 -2
- data/lib/fauxsql/attribute_list.rb +28 -11
- data/lib/fauxsql/attribute_manymany.rb +11 -0
- data/lib/fauxsql/attribute_map.rb +21 -9
- data/lib/fauxsql/attribute_wrapper.rb +17 -6
- data/lib/fauxsql/attributes.rb +5 -0
- data/lib/fauxsql/dereferenced_attribute.rb +5 -3
- data/lib/fauxsql/dsl.rb +73 -6
- data/lib/fauxsql/list_wrapper.rb +5 -2
- data/lib/fauxsql/manymany_wrapper.rb +43 -0
- data/lib/fauxsql/map_wrapper.rb +10 -2
- data/lib/fauxsql/options.rb +5 -0
- data/lib/fauxsql.rb +38 -9
- data/test/helper.rb +11 -4
- data/test/test_fauxsql.rb +215 -21
- metadata +7 -3
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -18,5 +18,5 @@ gem 'dm-validations', :git => 'git://github.com/datamapper/
|
|
18
18
|
gem 'dm-aggregates', :git => 'git://github.com/datamapper/dm-aggregates.git'
|
19
19
|
gem 'dm-constraints', :git => 'git://github.com/datamapper/dm-constraints.git'
|
20
20
|
gem 'dm-observer', :git => 'git://github.com/datamapper/dm-observer.git'
|
21
|
-
gem "dm-core", :git => "git://github.com/
|
21
|
+
gem "dm-core", :git => "git://github.com/datamapper/dm-core.git", "branch" => "next"
|
22
22
|
gem "datamapper"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/fauxsql.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{fauxsql}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Collin Miller"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-05-06}
|
13
13
|
s.description = %q{And description}
|
14
14
|
s.email = %q{collintmiller@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -27,12 +27,16 @@ Gem::Specification.new do |s|
|
|
27
27
|
"fauxsql.gemspec",
|
28
28
|
"lib/fauxsql.rb",
|
29
29
|
"lib/fauxsql/attribute_list.rb",
|
30
|
+
"lib/fauxsql/attribute_manymany.rb",
|
30
31
|
"lib/fauxsql/attribute_map.rb",
|
31
32
|
"lib/fauxsql/attribute_wrapper.rb",
|
33
|
+
"lib/fauxsql/attributes.rb",
|
32
34
|
"lib/fauxsql/dereferenced_attribute.rb",
|
33
35
|
"lib/fauxsql/dsl.rb",
|
34
36
|
"lib/fauxsql/list_wrapper.rb",
|
37
|
+
"lib/fauxsql/manymany_wrapper.rb",
|
35
38
|
"lib/fauxsql/map_wrapper.rb",
|
39
|
+
"lib/fauxsql/options.rb",
|
36
40
|
"test/helper.rb",
|
37
41
|
"test/test_fauxsql.rb"
|
38
42
|
]
|
@@ -5,29 +5,46 @@ module Fauxsql
|
|
5
5
|
def <<(attribute)
|
6
6
|
super Fauxsql.dereference_fauxsql_attribute(attribute)
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def [] index
|
10
10
|
Fauxsql.resolve_fauxsql_attribute super(index)
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def first
|
14
14
|
self[0]
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def last
|
18
18
|
self[length - 1]
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
|
+
def all
|
22
|
+
map{|item| Fauxsql.resolve_fauxsql_attribute item }
|
23
|
+
end
|
24
|
+
|
21
25
|
def equals list
|
22
26
|
map_resolved == list
|
23
27
|
end
|
24
28
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
29
|
+
def each
|
30
|
+
super{|item| yield(Fauxsql.resolve_fauxsql_attribute(item)) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def each_with_index
|
34
|
+
super{|item, index| yield(Fauxsql.resolve_fauxsql_attribute(item), index) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def -(others)
|
38
|
+
others = others.map{|other| Fauxsql.dereference_fauxsql_attribute(other).hash }
|
39
|
+
reject!{|one| others.include?(one.hash) }
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Always being not eql is expensive
|
44
|
+
# TODO make this work without this hack
|
45
|
+
def eql?(other)
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
|
32
49
|
end
|
33
50
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Fauxsql
|
2
|
+
# AttributeMap is an Hash that dereferences and resolves fauxsql attributes
|
3
|
+
# when setting/reading members in the Hash
|
4
|
+
class AttributeManymany < AttributeList
|
5
|
+
# Always being not eql is expensive
|
6
|
+
# TODO make this work without this hack
|
7
|
+
def eql?(other)
|
8
|
+
return false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -1,18 +1,31 @@
|
|
1
1
|
module Fauxsql
|
2
2
|
# AttributeMap is an Hash that dereferences and resolves fauxsql attributes
|
3
3
|
# when setting/reading members in the Hash
|
4
|
+
def self.dereference_fauxsql_key(key)
|
5
|
+
real_key = Fauxsql.dereference_fauxsql_attribute(key)
|
6
|
+
if (real_key.is_a? Fauxsql::DereferencedAttribute)
|
7
|
+
real_key = real_key.hash
|
8
|
+
end
|
9
|
+
real_key
|
10
|
+
end
|
11
|
+
|
4
12
|
class AttributeMap < Hash
|
5
|
-
# We dereference and resolve the key because in Ruby _any_ object
|
13
|
+
# We dereference and resolve the key because in Ruby not _any_ object
|
6
14
|
# can be a hash key. Even a DataMapper record.
|
7
15
|
def []= key, value
|
8
|
-
real_key = Fauxsql.
|
16
|
+
real_key = Fauxsql.dereference_fauxsql_key(key)
|
9
17
|
real_value = Fauxsql.dereference_fauxsql_attribute(value)
|
10
|
-
super real_key
|
18
|
+
super real_key, real_value
|
11
19
|
end
|
12
20
|
|
13
21
|
def [] key
|
14
|
-
real_key = Fauxsql.
|
15
|
-
Fauxsql.resolve_fauxsql_attribute super(real_key
|
22
|
+
real_key = Fauxsql.dereference_fauxsql_key(key)
|
23
|
+
Fauxsql.resolve_fauxsql_attribute super(real_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete key
|
27
|
+
real_key = Fauxsql.dereference_fauxsql_key(key)
|
28
|
+
super(real_key)
|
16
29
|
end
|
17
30
|
|
18
31
|
def each(&block)
|
@@ -30,7 +43,7 @@ module Fauxsql
|
|
30
43
|
end
|
31
44
|
|
32
45
|
def resolve_key(key)
|
33
|
-
if key.respond_to?(:match) && key.match(/^.+Fauxsql::DereferencedAttribute.+@
|
46
|
+
if key.respond_to?(:match) && key.match(/^.+Fauxsql::DereferencedAttribute.+@lookup_key.+$/)
|
34
47
|
Fauxsql.resolve_fauxsql_attribute Fauxsql::DereferencedAttribute.load(key)
|
35
48
|
else
|
36
49
|
key
|
@@ -41,11 +54,10 @@ module Fauxsql
|
|
41
54
|
Fauxsql.resolve_fauxsql_attribute value
|
42
55
|
end
|
43
56
|
|
57
|
+
# Always being not eql is expensive
|
58
|
+
# TODO make this work without this hack
|
44
59
|
def eql?(other)
|
45
60
|
return false
|
46
|
-
raise [self, other.inspect].inspect
|
47
61
|
end
|
48
|
-
|
49
|
-
|
50
62
|
end
|
51
63
|
end
|
@@ -1,20 +1,31 @@
|
|
1
1
|
module Fauxsql
|
2
2
|
class AttributeWrapper
|
3
|
+
class MissingOptions < ArgumentError
|
4
|
+
def initialize(missing, options)
|
5
|
+
super "Missing option :#{missing} in #{options.inspect}"
|
6
|
+
end
|
7
|
+
end
|
3
8
|
extend ActiveSupport::Concern
|
4
9
|
attr_reader:attribute
|
5
10
|
attr_reader:record
|
6
11
|
attr_reader:name
|
12
|
+
attr_reader:options
|
7
13
|
|
8
|
-
def initialize(attribute, record, name)
|
9
|
-
|
14
|
+
def initialize(attribute, record, name, options)
|
15
|
+
raise MissingOptions if options.nil?
|
16
|
+
@attribute, @record, @name, @options = attribute, record, name, options
|
10
17
|
@record.fauxsql_attributes[name] ||= attribute
|
11
18
|
end
|
12
19
|
|
20
|
+
def get_nested_record(vals)
|
21
|
+
model = vals[:type].constantize
|
22
|
+
raise MissingOptions.new(:nest, options) unless options[:nest]
|
23
|
+
raise InvalidNesting unless options[:nest].include?(model)
|
24
|
+
model.get(vals["#{model.name}_id".to_sym])
|
25
|
+
end
|
26
|
+
|
13
27
|
def dirty!
|
14
|
-
|
15
|
-
value = yield
|
16
|
-
record.attribute_set(:fauxsql_attributes, record.fauxsql_attributes)
|
17
|
-
value
|
28
|
+
Fauxsql.dirty!(record){ yield }
|
18
29
|
end
|
19
30
|
end
|
20
31
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "active_support/inflector"
|
1
2
|
module Fauxsql
|
2
3
|
# DereferencedAttribute stores objects that quack like DataMapper::Resource
|
3
4
|
# This is the object that Fauxsql stores in the database when a
|
@@ -6,12 +7,12 @@ module Fauxsql
|
|
6
7
|
class DereferencedAttribute
|
7
8
|
@@identity_map = {}
|
8
9
|
def initialize(attribute)
|
9
|
-
@klass = attribute.class
|
10
|
+
@klass = attribute.class.to_s
|
10
11
|
@lookup_key = attribute.key
|
11
12
|
end
|
12
13
|
|
13
14
|
def resolve
|
14
|
-
@klass.get(*@lookup_key)
|
15
|
+
ActiveSupport::Inflector.constantize(@klass.to_s).get(*@lookup_key)
|
15
16
|
end
|
16
17
|
|
17
18
|
def dump
|
@@ -20,7 +21,8 @@ module Fauxsql
|
|
20
21
|
alias hash dump
|
21
22
|
|
22
23
|
def self.get(attribute)
|
23
|
-
|
24
|
+
dereferenced = new(attribute)
|
25
|
+
@@identity_map[dereferenced.hash] ||= dereferenced
|
24
26
|
end
|
25
27
|
|
26
28
|
def self.load(dump)
|
data/lib/fauxsql/dsl.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
require "active_support/inflector"
|
1
2
|
module Fauxsql
|
2
3
|
module DSL
|
4
|
+
class InvalidNesting < StandardError; end
|
3
5
|
# DSL method to define a named Fauxsql attribute
|
4
6
|
#
|
5
7
|
# calling with 'power' is like writing:
|
@@ -10,8 +12,9 @@ module Fauxsql
|
|
10
12
|
# def power=(value)
|
11
13
|
# set_fauxsql_attribute(:power, value)
|
12
14
|
# end
|
13
|
-
def attribute(attribute_name)
|
14
|
-
|
15
|
+
def attribute(attribute_name, options={})
|
16
|
+
fauxsql_options[attribute_name] = normalize_options!(options)
|
17
|
+
class_eval <<EORUBY, __FILE__, __LINE__
|
15
18
|
def #{attribute_name}
|
16
19
|
get_fauxsql_attribute(:#{attribute_name})
|
17
20
|
end
|
@@ -20,6 +23,15 @@ module Fauxsql
|
|
20
23
|
set_fauxsql_attribute(:#{attribute_name}, value)
|
21
24
|
end
|
22
25
|
EORUBY
|
26
|
+
|
27
|
+
if options[:nest]
|
28
|
+
class_eval <<EORUBY, __FILE__, __LINE__
|
29
|
+
def #{attribute_name}_attributes=(vals)
|
30
|
+
vals = Fauxsql::DSL.normalize_nested_vals!(vals)
|
31
|
+
#{attribute_name} = #{attribute_name}.get_nested_record(vals)
|
32
|
+
end
|
33
|
+
EORUBY
|
34
|
+
end
|
23
35
|
end
|
24
36
|
|
25
37
|
# DSL method to define a named Fauxsql list
|
@@ -28,12 +40,26 @@ EORUBY
|
|
28
40
|
# def squad_members
|
29
41
|
# get_fauxsql_list(:squad_members)
|
30
42
|
# end
|
31
|
-
def list(attribute_name)
|
32
|
-
|
43
|
+
def list(attribute_name, options={})
|
44
|
+
fauxsql_options[attribute_name] = normalize_options!(options)
|
45
|
+
class_eval <<EORUBY, __FILE__, __LINE__
|
33
46
|
def #{attribute_name}
|
34
47
|
get_fauxsql_list(:#{attribute_name})
|
35
48
|
end
|
36
49
|
EORUBY
|
50
|
+
|
51
|
+
if options[:nest]
|
52
|
+
class_eval <<EORUBY, __FILE__, __LINE__
|
53
|
+
def #{attribute_name}=(attrs)
|
54
|
+
#{attribute_name}.clear
|
55
|
+
attrs.each do |index, vals|
|
56
|
+
vals = Fauxsql::DSL.normalize_nested_vals!(vals)
|
57
|
+
record = #{attribute_name}.get_nested_record(vals)
|
58
|
+
#{attribute_name} << record if record unless vals[:_delete]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
EORUBY
|
62
|
+
end
|
37
63
|
end
|
38
64
|
|
39
65
|
# DSL method to define a named Fauxsql map
|
@@ -42,12 +68,53 @@ EORUBY
|
|
42
68
|
# def mitigates
|
43
69
|
# get_fauxsql_map(:mitigates)
|
44
70
|
# end
|
45
|
-
def map(attribute_name)
|
46
|
-
|
71
|
+
def map(attribute_name, options={})
|
72
|
+
fauxsql_options[attribute_name] = normalize_options!(options)
|
73
|
+
class_eval <<EORUBY, __FILE__, __LINE__
|
47
74
|
def #{attribute_name}
|
48
75
|
get_fauxsql_map(:#{attribute_name})
|
49
76
|
end
|
50
77
|
EORUBY
|
78
|
+
|
79
|
+
if options[:nest]
|
80
|
+
class_eval <<EORUBY, __FILE__, __LINE__
|
81
|
+
def #{attribute_name}=(attrs)
|
82
|
+
deletes = []
|
83
|
+
attrs.each do |index, vals|
|
84
|
+
vals = Fauxsql::DSL.normalize_nested_vals!(vals)
|
85
|
+
key = #{attribute_name}.get_nested_record(vals)
|
86
|
+
#{attribute_name}[key] = vals[:value]
|
87
|
+
deletes << key if vals[:_delete]
|
88
|
+
end
|
89
|
+
deletes.each{ |key| #{attribute_name}.delete(key) }
|
90
|
+
end
|
91
|
+
EORUBY
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# DSL method to define a named Fauxsql manymany relationship
|
96
|
+
#
|
97
|
+
# calling with 'friends' is like writing:
|
98
|
+
# def friends
|
99
|
+
# get_fauxsql_manymany(:friends, Other, :through => :friends)
|
100
|
+
# end
|
101
|
+
def manymany(attribute_name, classes, options)
|
102
|
+
define_method attribute_name do
|
103
|
+
get_fauxsql_manymany(attribute_name, classes, options)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def normalize_options!(options)
|
110
|
+
options[:nest] = [options[:nest]] if options[:nest] unless options[:nest].is_a?(Array)
|
111
|
+
options.freeze
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.normalize_nested_vals!(vals)
|
115
|
+
vals[:_delete] = true if vals[:_delete] == "1" # Rails forms return "1" for nested attributes deletion.
|
116
|
+
vals[:_delete] = false unless vals[:_delete] == true
|
117
|
+
vals.freeze
|
51
118
|
end
|
52
119
|
end
|
53
120
|
end
|
data/lib/fauxsql/list_wrapper.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
-
require "active_support/dependencies"
|
2
1
|
require "active_support/core_ext/module/delegation"
|
3
2
|
module Fauxsql
|
4
3
|
class ListWrapper < AttributeWrapper
|
5
4
|
alias list attribute
|
6
|
-
delegate :[], :==,
|
5
|
+
delegate :[], :==, :first, :last, :each, :each_with_index, :map, :all, :equals, :to => :list
|
7
6
|
|
8
7
|
def <<(item)
|
9
8
|
dirty! { list << item }
|
10
9
|
end
|
10
|
+
|
11
|
+
def clear
|
12
|
+
dirty! { list.clear }
|
13
|
+
end
|
11
14
|
end
|
12
15
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "active_support/core_ext/module/delegation"
|
2
|
+
require 'active_support/core_ext/array/extract_options'
|
3
|
+
module Fauxsql
|
4
|
+
class ManymanyWrapper < AttributeWrapper
|
5
|
+
class ThroughOptionMissing < StandardError; end
|
6
|
+
class InvalidManymanyAssociationClass < StandardError; end
|
7
|
+
|
8
|
+
alias list attribute
|
9
|
+
delegate :-, :[], :==, :first, :last, :each, :each_with_index, :map, :all, :equals, :to => :list
|
10
|
+
|
11
|
+
def initialize(attribute, record, name, *classes)
|
12
|
+
super(attribute, record, name, {})
|
13
|
+
options = classes.extract_options!
|
14
|
+
raise ThroughOptionMissing unless options[:through]
|
15
|
+
@classes, @through = classes, options[:through]
|
16
|
+
end
|
17
|
+
|
18
|
+
def <<(other)
|
19
|
+
raise InvalidManymanyAssociationClass unless @classes.include?(other.class)
|
20
|
+
other.send(@through).clean_push(record)
|
21
|
+
clean_push(other)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(*others)
|
25
|
+
raise InvalidManymanyAssociationClass if (@classes - others.map{|other| other.class }).any?
|
26
|
+
clean_subtract(others)
|
27
|
+
others.each{ |other| other.send(@through).clean_subtract([record]) }
|
28
|
+
# # DataMapper.transaction do # TODO the transaction?
|
29
|
+
others.each{|other| other.save}
|
30
|
+
record.save
|
31
|
+
# end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
def clean_push(other)
|
36
|
+
dirty! { list << other }
|
37
|
+
end
|
38
|
+
|
39
|
+
def clean_subtract(others)
|
40
|
+
dirty! { @attribute = list - others }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/fauxsql/map_wrapper.rb
CHANGED
@@ -1,12 +1,20 @@
|
|
1
|
-
require "active_support/dependencies"
|
2
1
|
require "active_support/core_ext/module/delegation"
|
3
2
|
module Fauxsql
|
4
3
|
class MapWrapper < AttributeWrapper
|
5
4
|
alias map attribute
|
6
|
-
delegate :[], :each, :keys, :resolve_key, :resolve_value, :to => :map
|
5
|
+
delegate :[], :each, :each_with_index, :keys, :resolve_key, :resolve_value, :to => :map
|
7
6
|
|
8
7
|
def []=(key, value)
|
8
|
+
value = value.send(options[:value_type]) if options[:value_type]
|
9
9
|
dirty! { map[key] = value }
|
10
10
|
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
options[:value_type] ? map[key].send(options[:value_type]) : map[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete(key)
|
17
|
+
dirty! { map.delete(key) }
|
18
|
+
end
|
11
19
|
end
|
12
20
|
end
|
data/lib/fauxsql.rb
CHANGED
@@ -6,48 +6,69 @@ require 'pathname'
|
|
6
6
|
# Internal Libs
|
7
7
|
root = Pathname.new(__FILE__).dirname.expand_path
|
8
8
|
require root+'fauxsql/dereferenced_attribute'
|
9
|
+
require root+'fauxsql/attributes'
|
10
|
+
require root+'fauxsql/options'
|
9
11
|
require root+'fauxsql/attribute_list'
|
10
12
|
require root+'fauxsql/attribute_map'
|
13
|
+
require root+'fauxsql/attribute_manymany'
|
11
14
|
require root+'fauxsql/attribute_wrapper'
|
12
15
|
require root+'fauxsql/map_wrapper'
|
13
16
|
require root+'fauxsql/list_wrapper'
|
17
|
+
require root+'fauxsql/manymany_wrapper'
|
14
18
|
require root+'fauxsql/dsl'
|
15
19
|
module Fauxsql
|
16
20
|
extend ActiveSupport::Concern
|
17
21
|
|
18
22
|
included do
|
19
|
-
|
23
|
+
# Property is lazy. Benchmark for lazy loading shows
|
24
|
+
# performance is up to 5x slower when accessing fauxsql attributes.
|
25
|
+
property :fauxsql_attributes, Object,
|
26
|
+
:default => lambda{|*| Fauxsql::Attributes.new },
|
27
|
+
:lazy => false
|
20
28
|
extend Fauxsql::DSL
|
29
|
+
cattr_accessor :fauxsql_options
|
30
|
+
self.fauxsql_options = Fauxsql::Options.new
|
21
31
|
end
|
22
32
|
|
23
33
|
# Getter method for attributes defined as:
|
24
34
|
# attribute :attribute_name
|
25
35
|
def get_fauxsql_attribute(attribute_name)
|
26
36
|
attribute = fauxsql_attributes[attribute_name]
|
27
|
-
Fauxsql.resolve_fauxsql_attribute(attribute)
|
37
|
+
value = Fauxsql.resolve_fauxsql_attribute(attribute)
|
38
|
+
|
39
|
+
options = fauxsql_options[attribute_name]
|
40
|
+
options and options[:type] ? value.send(options[:type]) : value
|
28
41
|
end
|
29
42
|
|
30
43
|
# Setter method for attributes defined as:
|
31
44
|
# attribute :attribute_name
|
32
45
|
def set_fauxsql_attribute(attribute_name, value)
|
46
|
+
options = fauxsql_options[attribute_name]
|
47
|
+
value = value.send(options[:type]) if options and options[:type]
|
48
|
+
|
33
49
|
attribute = Fauxsql.dereference_fauxsql_attribute(value)
|
34
|
-
fauxsql_attributes[attribute_name] = attribute
|
50
|
+
Fauxsql.dirty!(self){ fauxsql_attributes[attribute_name] = attribute }
|
35
51
|
end
|
36
52
|
|
37
53
|
# Gets a reference to an AttributeList object. AttributeList quacks like
|
38
54
|
# a Ruby Array. Except it uses Fauxsql's dereference and resolve strategy to
|
39
55
|
# store members.
|
40
|
-
def get_fauxsql_list(
|
41
|
-
list = fauxsql_attributes[
|
42
|
-
ListWrapper.new(list, self,
|
56
|
+
def get_fauxsql_list(attribute_name)
|
57
|
+
list = fauxsql_attributes[attribute_name] || AttributeList.new
|
58
|
+
ListWrapper.new(list, self, attribute_name, fauxsql_options[attribute_name])
|
43
59
|
end
|
44
60
|
|
45
61
|
# Gets a reference to an AttributeMap object. AttributeMap quacks like
|
46
62
|
# a Ruby Hash. Except it uses Fauxsql's dereference and resolve strategy to
|
47
63
|
# store keys and values.
|
48
|
-
def get_fauxsql_map(
|
49
|
-
map = fauxsql_attributes[
|
50
|
-
MapWrapper.new(map, self,
|
64
|
+
def get_fauxsql_map(attribute_name)
|
65
|
+
map = fauxsql_attributes[attribute_name] || AttributeMap.new
|
66
|
+
MapWrapper.new(map, self, attribute_name, fauxsql_options[attribute_name])
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_fauxsql_manymany(attribute_name, classes, options)
|
70
|
+
manymany = fauxsql_attributes[attribute_name] || AttributeManymany.new
|
71
|
+
ManymanyWrapper.new(manymany, self, attribute_name, classes, options)
|
51
72
|
end
|
52
73
|
|
53
74
|
# When setting values, all attributes pass through this method.
|
@@ -71,4 +92,12 @@ module Fauxsql
|
|
71
92
|
attribute
|
72
93
|
end
|
73
94
|
end
|
95
|
+
|
96
|
+
def self.dirty!(record)
|
97
|
+
record.attribute_set(:fauxsql_attributes, record.fauxsql_attributes.dup)
|
98
|
+
value = yield
|
99
|
+
record.attribute_set(:fauxsql_attributes, record.fauxsql_attributes)
|
100
|
+
value
|
101
|
+
end
|
102
|
+
|
74
103
|
end
|
data/test/helper.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'test/unit'
|
3
3
|
require 'shoulda'
|
4
|
-
|
4
|
+
require 'dm-core'
|
5
5
|
require 'datamapper'
|
6
6
|
DataMapper.setup(:default, "sqlite3://:memory:")
|
7
7
|
|
@@ -10,8 +10,15 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
10
10
|
require 'fauxsql'
|
11
11
|
|
12
12
|
class Test::Unit::TestCase
|
13
|
-
def
|
14
|
-
@faux
|
15
|
-
|
13
|
+
def checkpoint!
|
14
|
+
if @faux
|
15
|
+
@faux.save
|
16
|
+
@faux.reload
|
17
|
+
end
|
18
|
+
|
19
|
+
if @other
|
20
|
+
@other.save
|
21
|
+
@other.reload
|
22
|
+
end
|
16
23
|
end
|
17
24
|
end
|
data/test/test_fauxsql.rb
CHANGED
@@ -18,9 +18,13 @@ class FauxObject
|
|
18
18
|
property :id, Serial
|
19
19
|
property :type, Discriminator
|
20
20
|
attribute :name
|
21
|
-
attribute :record
|
22
|
-
|
23
|
-
|
21
|
+
attribute :record, :nest => FauxObject
|
22
|
+
attribute :number, :type => :to_i
|
23
|
+
list :things, :nest => FauxObject
|
24
|
+
map :dictionary, :nest => FauxObject
|
25
|
+
map :numbers, :value_type => :to_i
|
26
|
+
|
27
|
+
manymany :others, FauxObject, :through => :others#, :nest => true TODO implement nesting on manymany
|
24
28
|
end
|
25
29
|
|
26
30
|
class OtherFauxObject < FauxObject; end
|
@@ -47,21 +51,29 @@ class TestFauxsql < Test::Unit::TestCase
|
|
47
51
|
|
48
52
|
should "persist attributes" do
|
49
53
|
@faux.name = "MyName"
|
50
|
-
|
54
|
+
checkpoint!
|
51
55
|
assert_equal "MyName", @faux.name
|
52
56
|
end
|
53
57
|
|
58
|
+
should "update attributes" do
|
59
|
+
@faux.name = "AName"
|
60
|
+
checkpoint!
|
61
|
+
@faux.name = "OtherName"
|
62
|
+
checkpoint!
|
63
|
+
assert_equal "OtherName", @faux.name
|
64
|
+
end
|
65
|
+
|
54
66
|
should "persist lists" do
|
55
67
|
@faux.things << :hello
|
56
68
|
@faux.things << :goodbye
|
57
|
-
|
69
|
+
checkpoint!
|
58
70
|
assert @faux.things == [:hello, :goodbye]
|
59
71
|
end
|
60
72
|
|
61
73
|
should "persist maps" do
|
62
74
|
@faux.dictionary[:a] = 1
|
63
75
|
@faux.dictionary[:b] = 2
|
64
|
-
|
76
|
+
checkpoint!
|
65
77
|
assert_equal 1, @faux.dictionary[:a]
|
66
78
|
assert_equal 2, @faux.dictionary[:b]
|
67
79
|
end
|
@@ -69,14 +81,14 @@ class TestFauxsql < Test::Unit::TestCase
|
|
69
81
|
should "dereference and resolve objects that include Fauxsql" do
|
70
82
|
has_fauxsql = OtherFauxObject.create
|
71
83
|
@faux.record = has_fauxsql
|
72
|
-
|
84
|
+
checkpoint!
|
73
85
|
assert_equal has_fauxsql, @faux.record
|
74
86
|
end
|
75
87
|
|
76
88
|
should "dereference and resolve dm objects with simple keys" do
|
77
89
|
simple_key = SimpleKey.create
|
78
90
|
@faux.record = simple_key
|
79
|
-
|
91
|
+
checkpoint!
|
80
92
|
assert @faux.fauxsql_attributes[:record].is_a?(Fauxsql::DereferencedAttribute)
|
81
93
|
assert_equal simple_key, @faux.record
|
82
94
|
end
|
@@ -84,7 +96,7 @@ class TestFauxsql < Test::Unit::TestCase
|
|
84
96
|
should "deference and resolve dm objects with complex keys" do
|
85
97
|
complex_key = ComplexKey.create(:string => "string", :integer => 1)
|
86
98
|
@faux.record = complex_key
|
87
|
-
|
99
|
+
checkpoint!
|
88
100
|
assert @faux.fauxsql_attributes[:record].is_a?(Fauxsql::DereferencedAttribute)
|
89
101
|
assert_equal complex_key, @faux.record
|
90
102
|
end
|
@@ -94,24 +106,36 @@ class TestFauxsql < Test::Unit::TestCase
|
|
94
106
|
@faux.things << :hello
|
95
107
|
@faux.things << simple
|
96
108
|
@faux.things << :goodbye
|
97
|
-
|
98
|
-
assert_equal [:hello, simple, :goodbye], @faux.things.
|
109
|
+
checkpoint!
|
110
|
+
assert_equal [:hello, simple, :goodbye], @faux.things.all
|
99
111
|
end
|
100
|
-
|
112
|
+
|
101
113
|
should "derefencenc and resolve fauxsql objects in lists" do
|
102
114
|
has_fauxsql = OtherFauxObject.create
|
103
115
|
@faux.things << :hello
|
104
116
|
@faux.things << has_fauxsql
|
105
117
|
@faux.things << :goodbye
|
106
|
-
|
107
|
-
assert_equal [:hello, has_fauxsql, :goodbye], @faux.things.
|
118
|
+
checkpoint!
|
119
|
+
assert_equal [:hello, has_fauxsql, :goodbye], @faux.things.all
|
120
|
+
end
|
121
|
+
|
122
|
+
should "derefencenc and resolve fauxsql objects in lists when calling each/each_with_index" do
|
123
|
+
has_fauxsql = OtherFauxObject.create
|
124
|
+
@faux.things << has_fauxsql
|
125
|
+
checkpoint!
|
126
|
+
@faux.things.each_with_index do |thing, index|
|
127
|
+
assert_equal has_fauxsql, thing
|
128
|
+
end
|
129
|
+
@faux.things.each do |thing|
|
130
|
+
assert_equal has_fauxsql, thing
|
131
|
+
end
|
108
132
|
end
|
109
133
|
|
110
134
|
should "derference and resolve dm objects with fauxsql in maps" do
|
111
135
|
has_fauxsql1 = OtherFauxObject.create
|
112
136
|
has_fauxsql2 = OtherFauxObject.create
|
113
137
|
@faux.dictionary[has_fauxsql1] = has_fauxsql2
|
114
|
-
|
138
|
+
checkpoint!
|
115
139
|
assert_equal has_fauxsql2, @faux.dictionary[has_fauxsql1]
|
116
140
|
end
|
117
141
|
|
@@ -120,7 +144,7 @@ class TestFauxsql < Test::Unit::TestCase
|
|
120
144
|
simple2 = SimpleKey.create
|
121
145
|
@faux.dictionary[simple1] = simple2
|
122
146
|
assert_equal SimpleKey, @faux.dictionary.keys.first.class
|
123
|
-
|
147
|
+
checkpoint!
|
124
148
|
assert_equal simple2, @faux.dictionary[simple1]
|
125
149
|
end
|
126
150
|
|
@@ -128,7 +152,7 @@ class TestFauxsql < Test::Unit::TestCase
|
|
128
152
|
simple1 = SimpleKey.create
|
129
153
|
simple2 = SimpleKey.create
|
130
154
|
@faux.dictionary[simple1] = simple2
|
131
|
-
|
155
|
+
checkpoint!
|
132
156
|
@faux.dictionary.each do |key, value|
|
133
157
|
assert_equal simple1, key
|
134
158
|
assert_equal simple2, value
|
@@ -138,18 +162,188 @@ class TestFauxsql < Test::Unit::TestCase
|
|
138
162
|
should "not choke on normal values in hash when calling #each" do
|
139
163
|
simple1 = SimpleKey.create
|
140
164
|
@faux.dictionary[simple1] = 33
|
141
|
-
|
165
|
+
checkpoint!
|
142
166
|
@faux.dictionary.each{|key, value| }
|
143
167
|
assert true, "choked on normal values in #each"
|
144
168
|
end
|
145
|
-
|
169
|
+
|
146
170
|
should "persist changes to maps" do
|
147
171
|
simple1 = SimpleKey.create
|
148
172
|
@faux.dictionary[simple1] = 33
|
149
|
-
|
173
|
+
checkpoint!
|
150
174
|
@faux.dictionary[simple1] = 50
|
151
|
-
|
175
|
+
checkpoint!
|
152
176
|
assert_equal 50, @faux.dictionary[simple1]
|
153
177
|
end
|
178
|
+
|
179
|
+
should "persist changes to lists" do
|
180
|
+
has_fauxsql = OtherFauxObject.create
|
181
|
+
@faux.things << :hello
|
182
|
+
@faux.things << has_fauxsql
|
183
|
+
@faux.things << :goodbye
|
184
|
+
checkpoint!
|
185
|
+
@faux.things.clear
|
186
|
+
checkpoint!
|
187
|
+
assert_equal [], @faux.things.all
|
188
|
+
end
|
189
|
+
|
190
|
+
should "delete items from maps" do
|
191
|
+
simple1 = SimpleKey.create
|
192
|
+
@faux.dictionary[simple1] = 33
|
193
|
+
checkpoint!
|
194
|
+
@faux.dictionary.delete(simple1)
|
195
|
+
checkpoint!
|
196
|
+
assert_equal nil, @faux.dictionary[simple1]
|
197
|
+
end
|
198
|
+
|
199
|
+
should "obey typecasting directives for attributes" do
|
200
|
+
@faux.number = "33"
|
201
|
+
checkpoint!
|
202
|
+
assert_equal 33, @faux.number
|
203
|
+
end
|
204
|
+
|
205
|
+
should "obey typecasting directives for map values" do
|
206
|
+
@faux.numbers[:a] = "400"
|
207
|
+
checkpoint!
|
208
|
+
assert_equal 400, @faux.numbers[:a]
|
209
|
+
end
|
210
|
+
|
211
|
+
should "obey typecasting directives for map keys" do
|
212
|
+
assert false
|
213
|
+
end
|
214
|
+
|
215
|
+
should "obey typecasting directives for list items" do
|
216
|
+
assert false
|
217
|
+
end
|
218
|
+
|
219
|
+
context "with :nested => *" do
|
220
|
+
context "on a map" do
|
221
|
+
should "accept nested attributes" do
|
222
|
+
other = FauxObject.create
|
223
|
+
@faux.dictionary = { "0" => {
|
224
|
+
:type => other.class.name,
|
225
|
+
"#{other.class.name}_id".intern => other.id,
|
226
|
+
:value => "Nested"
|
227
|
+
}}
|
228
|
+
checkpoint!
|
229
|
+
assert_equal "Nested", @faux.dictionary[other]
|
230
|
+
end
|
231
|
+
|
232
|
+
should "delete nested attributes" do
|
233
|
+
other = FauxObject.create
|
234
|
+
@faux.dictionary = { "0" => {
|
235
|
+
:type => other.class.name,
|
236
|
+
"#{other.class.name}_id".intern => other.id,
|
237
|
+
:value => "Nested"
|
238
|
+
}}
|
239
|
+
checkpoint!
|
240
|
+
@faux.dictionary = { "0" => {
|
241
|
+
:type => other.class.name,
|
242
|
+
"#{other.class.name}_id".intern => other.id,
|
243
|
+
:_delete => true
|
244
|
+
}}
|
245
|
+
checkpoint!
|
246
|
+
assert_equal [], @faux.dictionary.keys
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
context "on a list" do
|
251
|
+
should "accept nested attributes" do
|
252
|
+
other = FauxObject.create
|
253
|
+
@faux.things = { "0" => {
|
254
|
+
:type => other.class.name,
|
255
|
+
"#{other.class.name}_id".intern => other.id
|
256
|
+
}}
|
257
|
+
checkpoint!
|
258
|
+
assert_equal [other], @faux.things.all
|
259
|
+
end
|
260
|
+
|
261
|
+
should "delete nested attributes" do
|
262
|
+
other = FauxObject.create
|
263
|
+
@faux.things = { "0" => {
|
264
|
+
:type => other.class.name,
|
265
|
+
"#{other.class.name}_id".intern => other.id
|
266
|
+
}}
|
267
|
+
checkpoint!
|
268
|
+
@faux.things = { "0" => {
|
269
|
+
:type => other.class.name,
|
270
|
+
"#{other.class.name}_id".intern => other.id,
|
271
|
+
:_delete => true
|
272
|
+
}}
|
273
|
+
checkpoint!
|
274
|
+
assert_equal [], @faux.things.all
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
context "on an attribute" do
|
279
|
+
should "accept nested attribute" do
|
280
|
+
assert false
|
281
|
+
# other = FauxObject.create
|
282
|
+
# @faux.record = {
|
283
|
+
# :type => other.class.name,
|
284
|
+
# "#{other.class.name}_id".intern => other.id
|
285
|
+
# }
|
286
|
+
# checkpoint!
|
287
|
+
# assert_equal other, @faux.record
|
288
|
+
end
|
289
|
+
|
290
|
+
should "delete nested attribute" do
|
291
|
+
assert false
|
292
|
+
# other = FauxObject.create
|
293
|
+
# @faux.record = {
|
294
|
+
# :type => other.class.name,
|
295
|
+
# "#{other.class.name}_id".intern => other.id
|
296
|
+
# }
|
297
|
+
# checkpoint!
|
298
|
+
# other = FauxObject.create
|
299
|
+
# @faux.record = {
|
300
|
+
# :type => other.class.name,
|
301
|
+
# "#{other.class.name}_id".intern => other.id,
|
302
|
+
# :_delete => true
|
303
|
+
# }
|
304
|
+
# checkpoint!
|
305
|
+
# assert_equal nil, @faux.record
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context "with a manymany relationship" do
|
311
|
+
setup do
|
312
|
+
@faux = FauxObject.create!
|
313
|
+
@other = FauxObject.create!
|
314
|
+
@faux.others << @other
|
315
|
+
checkpoint!
|
316
|
+
end
|
317
|
+
|
318
|
+
should "associate manymany relationships" do
|
319
|
+
assert_equal [@faux], @other.others.all # LOL "LOST" JOKE
|
320
|
+
assert_equal [@other], @faux.others.all
|
321
|
+
end
|
322
|
+
|
323
|
+
should "delete from manymany relationships" do
|
324
|
+
third = FauxObject.create
|
325
|
+
@faux.others << third
|
326
|
+
checkpoint!
|
327
|
+
@faux.others.delete(@other)
|
328
|
+
checkpoint!
|
329
|
+
third.save; third.reload
|
330
|
+
assert_equal [third], @faux.others.all
|
331
|
+
assert_equal [], @other.others.all
|
332
|
+
end
|
333
|
+
|
334
|
+
# TODO think about paranoid deletion
|
335
|
+
end
|
336
|
+
|
337
|
+
should "allow changing of hash key when key is record" do
|
338
|
+
assert false
|
339
|
+
end
|
340
|
+
|
341
|
+
should "return keys for dictionary stores" do
|
342
|
+
@faux.dictionary['a'] = 1
|
343
|
+
@faux.dictionary['b'] = 1
|
344
|
+
@faux.dictionary['c'] = 1
|
345
|
+
checkpoint!
|
346
|
+
assert_equal ['a', 'b', 'c'], @faux.dictionary.keys
|
347
|
+
end
|
154
348
|
end
|
155
349
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 1
|
8
7
|
- 2
|
9
|
-
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Collin Miller
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-05-06 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -87,12 +87,16 @@ files:
|
|
87
87
|
- fauxsql.gemspec
|
88
88
|
- lib/fauxsql.rb
|
89
89
|
- lib/fauxsql/attribute_list.rb
|
90
|
+
- lib/fauxsql/attribute_manymany.rb
|
90
91
|
- lib/fauxsql/attribute_map.rb
|
91
92
|
- lib/fauxsql/attribute_wrapper.rb
|
93
|
+
- lib/fauxsql/attributes.rb
|
92
94
|
- lib/fauxsql/dereferenced_attribute.rb
|
93
95
|
- lib/fauxsql/dsl.rb
|
94
96
|
- lib/fauxsql/list_wrapper.rb
|
97
|
+
- lib/fauxsql/manymany_wrapper.rb
|
95
98
|
- lib/fauxsql/map_wrapper.rb
|
99
|
+
- lib/fauxsql/options.rb
|
96
100
|
- test/helper.rb
|
97
101
|
- test/test_fauxsql.rb
|
98
102
|
has_rdoc: true
|