dm-is-reflective 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +15 -0
- data/CHANGES.md +8 -0
- data/Gemfile +5 -1
- data/README.md +103 -86
- data/Rakefile +1 -14
- data/bin/dm-is-reflective +4 -0
- data/dm-is-reflective.gemspec +24 -12
- data/lib/dm-is-reflective.rb +4 -5
- data/lib/dm-is-reflective/adapters/data_objects_adapter.rb +136 -0
- data/lib/dm-is-reflective/adapters/mysql_adapter.rb +63 -0
- data/lib/dm-is-reflective/adapters/postgres_adapter.rb +80 -0
- data/lib/dm-is-reflective/adapters/sqlite_adapter.rb +57 -0
- data/lib/dm-is-reflective/{is/reflective.rb → reflective.rb} +18 -7
- data/lib/dm-is-reflective/runner.rb +87 -0
- data/lib/dm-is-reflective/test.rb +279 -0
- data/lib/dm-is-reflective/version.rb +2 -6
- data/task/gemgem.rb +7 -6
- data/test/test_mysql.rb +23 -0
- data/test/test_postgres.rb +23 -0
- data/test/test_sqlite.rb +10 -0
- data/test/test_to_source.rb +55 -0
- metadata +43 -46
- data/lib/dm-is-reflective/is/adapters/data_objects_adapter.rb +0 -138
- data/lib/dm-is-reflective/is/adapters/mysql_adapter.rb +0 -64
- data/lib/dm-is-reflective/is/adapters/postgres_adapter.rb +0 -82
- data/lib/dm-is-reflective/is/adapters/sqlite_adapter.rb +0 -61
- data/test/abstract.rb +0 -252
- data/test/test_dm-is-reflective.rb +0 -53
@@ -0,0 +1,63 @@
|
|
1
|
+
|
2
|
+
module DmIsReflective::MysqlAdapter
|
3
|
+
include DataMapper
|
4
|
+
|
5
|
+
def storages
|
6
|
+
select('SHOW TABLES')
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
# construct needed table metadata
|
11
|
+
def reflective_query_storage storage
|
12
|
+
sql = <<-SQL
|
13
|
+
SELECT column_name, column_default, is_nullable, data_type,
|
14
|
+
character_maximum_length, column_key, extra
|
15
|
+
FROM `information_schema`.`columns`
|
16
|
+
WHERE `table_schema` = ? AND `table_name` = ?
|
17
|
+
SQL
|
18
|
+
|
19
|
+
# TODO: can we fix this shit in dm-mysql-adapter?
|
20
|
+
path = options[:path] || options['path'] ||
|
21
|
+
options[:database] || options['database']
|
22
|
+
|
23
|
+
select(Ext::String.compress_lines(sql), path.sub('/', ''), storage)
|
24
|
+
end
|
25
|
+
|
26
|
+
def reflective_field_name field
|
27
|
+
field.column_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def reflective_primitive field
|
31
|
+
field.data_type
|
32
|
+
end
|
33
|
+
|
34
|
+
def reflective_attributes field, attrs = {}
|
35
|
+
attrs[:serial] = true if field.extra == 'auto_increment'
|
36
|
+
attrs[:key] = true if field.column_key == 'PRI'
|
37
|
+
|
38
|
+
attrs[:allow_nil] = field.is_nullable == 'YES'
|
39
|
+
attrs[:default] = field.column_default if
|
40
|
+
field.column_default
|
41
|
+
|
42
|
+
attrs[:length] = field.character_maximum_length if
|
43
|
+
field.character_maximum_length
|
44
|
+
|
45
|
+
attrs
|
46
|
+
end
|
47
|
+
|
48
|
+
def reflective_lookup_primitive primitive
|
49
|
+
case primitive.upcase
|
50
|
+
when 'YEAR' ; Integer
|
51
|
+
when /\w*INT(EGER)?( SIGNED| UNSIGNED)?( ZEROFILL)?/
|
52
|
+
; Integer
|
53
|
+
when /(DOUBLE|FLOAT|DECIMAL)( SIGNED| UNSIGNED)?( ZEROFILL)?/
|
54
|
+
; Property::Decimal
|
55
|
+
when /\w*BLOB|\w*BINARY|ENUM|SET|CHAR/; String
|
56
|
+
when 'TIME' ; Time
|
57
|
+
when 'DATE' ; Date
|
58
|
+
when 'DATETIME', 'TIMESTAMP' ; DateTime
|
59
|
+
when 'BOOL', 'BOOLEAN' ; Property::Boolean
|
60
|
+
when /\w*TEXT/ ; Property::Text
|
61
|
+
end || super(primitive)
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
module DmIsReflective::PostgresAdapter
|
3
|
+
include DataMapper
|
4
|
+
|
5
|
+
def storages
|
6
|
+
sql = <<-SQL
|
7
|
+
SELECT table_name FROM "information_schema"."tables"
|
8
|
+
WHERE table_schema = current_schema()
|
9
|
+
SQL
|
10
|
+
|
11
|
+
select(Ext::String.compress_lines(sql))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def reflective_query_storage storage
|
16
|
+
sql = <<-SQL
|
17
|
+
SELECT column_name FROM "information_schema"."key_column_usage"
|
18
|
+
WHERE table_schema = current_schema() AND table_name = ?
|
19
|
+
SQL
|
20
|
+
|
21
|
+
keys = select(Ext::String.compress_lines(sql), storage).to_set
|
22
|
+
|
23
|
+
sql = <<-SQL
|
24
|
+
SELECT column_name, column_default, is_nullable,
|
25
|
+
character_maximum_length, udt_name
|
26
|
+
FROM "information_schema"."columns"
|
27
|
+
WHERE table_schema = current_schema() AND table_name = ?
|
28
|
+
SQL
|
29
|
+
|
30
|
+
select(Ext::String.compress_lines(sql), storage).map{ |struct|
|
31
|
+
struct.instance_eval <<-RUBY
|
32
|
+
def key?
|
33
|
+
#{keys.member?(struct.column_name)}
|
34
|
+
end
|
35
|
+
RUBY
|
36
|
+
struct
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def reflective_field_name field
|
41
|
+
field.column_name
|
42
|
+
end
|
43
|
+
|
44
|
+
def reflective_primitive field
|
45
|
+
field.udt_name
|
46
|
+
end
|
47
|
+
|
48
|
+
def reflective_attributes field, attrs = {}
|
49
|
+
# strip data type
|
50
|
+
field.column_default.gsub!(/(.*?)::[\w\s]*/, '\1') if
|
51
|
+
field.column_default
|
52
|
+
|
53
|
+
attrs[:serial] = true if field.column_default =~ /nextval\('\w+'\)/
|
54
|
+
attrs[:key] = true if field.key?
|
55
|
+
attrs[:allow_nil] = field.is_nullable == 'YES'
|
56
|
+
# strip string quotation
|
57
|
+
attrs[:default] = field.column_default.gsub(/^'(.*?)'$/, '\1') if
|
58
|
+
field.column_default && !attrs[:serial]
|
59
|
+
|
60
|
+
if field.character_maximum_length
|
61
|
+
attrs[:length] = field.character_maximum_length
|
62
|
+
elsif field.udt_name.upcase == 'TEXT'
|
63
|
+
attrs[:length] = Property::Text.length
|
64
|
+
end
|
65
|
+
|
66
|
+
attrs
|
67
|
+
end
|
68
|
+
|
69
|
+
def reflective_lookup_primitive primitive
|
70
|
+
case primitive.upcase
|
71
|
+
when /^INT\d+$/ ; Integer
|
72
|
+
when /^FLOAT\d+$/ ; Float
|
73
|
+
when 'VARCHAR', 'BPCHAR'; String
|
74
|
+
when 'TIMESTAMP', 'DATE'; DateTime
|
75
|
+
when 'TEXT' ; Property::Text
|
76
|
+
when 'BOOL' ; Property::Boolean
|
77
|
+
when 'NUMERIC' ; Property::Decimal
|
78
|
+
end || super(primitive)
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
module DmIsReflective::SqliteAdapter
|
3
|
+
include DataMapper
|
4
|
+
|
5
|
+
def storages
|
6
|
+
sql = <<-SQL
|
7
|
+
SELECT name
|
8
|
+
FROM sqlite_master
|
9
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
10
|
+
SQL
|
11
|
+
|
12
|
+
select(Ext::String.compress_lines(sql))
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def reflective_query_storage storage
|
17
|
+
select('PRAGMA table_info(?)', storage)
|
18
|
+
end
|
19
|
+
|
20
|
+
def reflective_field_name field
|
21
|
+
field.name
|
22
|
+
end
|
23
|
+
|
24
|
+
def reflective_primitive field
|
25
|
+
field.type.gsub(/\(\d+\)/, '')
|
26
|
+
end
|
27
|
+
|
28
|
+
def reflective_attributes field, attrs = {}
|
29
|
+
if field.pk != 0
|
30
|
+
attrs[:key] = true
|
31
|
+
attrs[:serial] = true
|
32
|
+
end
|
33
|
+
attrs[:allow_nil] = field.notnull == 0
|
34
|
+
attrs[:default] = field.dflt_value[1..-2] if field.dflt_value
|
35
|
+
|
36
|
+
if field.type.upcase == 'TEXT'
|
37
|
+
attrs[:length] = Property::Text.length
|
38
|
+
else
|
39
|
+
ergo = field.type.match(/\((\d+)\)/)
|
40
|
+
size = ergo && ergo[1].to_i
|
41
|
+
attrs[:length] = size if size
|
42
|
+
end
|
43
|
+
|
44
|
+
attrs
|
45
|
+
end
|
46
|
+
|
47
|
+
def reflective_lookup_primitive primitive
|
48
|
+
case primitive.upcase
|
49
|
+
when 'INTEGER' ; Integer
|
50
|
+
when 'REAL', 'NUMERIC'; Float
|
51
|
+
when 'VARCHAR' ; String
|
52
|
+
when 'TIMESTAMP' ; DateTime
|
53
|
+
when 'BOOLEAN' ; Property::Boolean
|
54
|
+
when 'TEXT' ; Property::Text
|
55
|
+
end || super(primitive)
|
56
|
+
end
|
57
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
|
2
|
-
module
|
3
|
-
|
4
|
-
|
2
|
+
module DmIsReflective
|
3
|
+
autoload :VERSION, 'dm-is-reflective/version'
|
4
|
+
|
5
|
+
include DataMapper
|
5
6
|
|
6
7
|
def is_reflective
|
7
8
|
extend ClassMethod
|
@@ -74,8 +75,18 @@ module Reflective
|
|
74
75
|
finalize if respond_to?(:finalize)
|
75
76
|
result
|
76
77
|
end
|
77
|
-
end # of ClassMethod
|
78
78
|
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
def to_source scope=nil
|
80
|
+
<<-RUBY
|
81
|
+
class #{scope}::#{name} < #{superclass}
|
82
|
+
include DataMapper::Resource
|
83
|
+
#{
|
84
|
+
properties.map do |prop|
|
85
|
+
"property :#{prop.name}, #{prop.class.name}, #{prop.options}"
|
86
|
+
end.join("\n")
|
87
|
+
}
|
88
|
+
end
|
89
|
+
RUBY
|
90
|
+
end
|
91
|
+
end # of ClassMethod
|
92
|
+
end # of DmIsReflective
|
@@ -0,0 +1,87 @@
|
|
1
|
+
|
2
|
+
require 'dm-is-reflective'
|
3
|
+
|
4
|
+
module DmIsReflective::Runner
|
5
|
+
module_function
|
6
|
+
def options
|
7
|
+
@options ||=
|
8
|
+
[['-s, --scope SCOPE' ,
|
9
|
+
'SCOPE where the models should go (default: Object)' ],
|
10
|
+
['-o, --output DIRECTORY' ,
|
11
|
+
'DIRECTORY where the output goes (default: dm-is-reflective)'],
|
12
|
+
['-h, --help' , 'Print this message' ],
|
13
|
+
['-v, --version', 'Print the version' ]]
|
14
|
+
end
|
15
|
+
|
16
|
+
def run argv=ARGV
|
17
|
+
puts(help) and exit if argv.empty?
|
18
|
+
generate(*parse(argv))
|
19
|
+
end
|
20
|
+
|
21
|
+
def generate uri, scope, output
|
22
|
+
require 'fileutils'
|
23
|
+
FileUtils.mkdir_p(output)
|
24
|
+
DataMapper.setup(:default, uri).auto_genclass!(:scope => scope).
|
25
|
+
each do |model|
|
26
|
+
path = "#{output}/#{model.name.gsub(/::/, '').
|
27
|
+
gsub(/([A-Z])/, '_\1').
|
28
|
+
downcase[1..-1]}.rb"
|
29
|
+
File.open(path, 'w') do |file|
|
30
|
+
file.puts model.to_source
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse argv
|
36
|
+
uri, scope, output = ['sqlite::memory:', Object, 'dm-is-reflective']
|
37
|
+
until argv.empty?
|
38
|
+
case arg = argv.shift
|
39
|
+
when /^-s=?(.+)?/, /^--scope=?(.+)?/
|
40
|
+
name = $1 || argv.shift
|
41
|
+
scope = if Object.const_defined?(name)
|
42
|
+
Object.const_get(name)
|
43
|
+
else
|
44
|
+
mkconst_p(name)
|
45
|
+
end
|
46
|
+
|
47
|
+
when /^-o=?(.+)?/, /^--output=?(.+)?/
|
48
|
+
output = $1 || argv.shift
|
49
|
+
|
50
|
+
when /^-h/, '--help'
|
51
|
+
puts(help)
|
52
|
+
exit
|
53
|
+
|
54
|
+
when /^-v/, '--version'
|
55
|
+
puts(DmIsReflective::VERSION)
|
56
|
+
exit
|
57
|
+
|
58
|
+
else
|
59
|
+
uri = arg
|
60
|
+
end
|
61
|
+
end
|
62
|
+
[uri, scope, output]
|
63
|
+
end
|
64
|
+
|
65
|
+
def mkconst_p name
|
66
|
+
name.split('::').inject(Object) do |ret, mod|
|
67
|
+
if Object.const_defined?(mod)
|
68
|
+
ret.const_get(mod)
|
69
|
+
else
|
70
|
+
ret.const_set(mod, Module.new)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def help
|
76
|
+
maxn = options.transpose.first.map(&:size).max
|
77
|
+
maxd = options.transpose.last .map(&:size).max
|
78
|
+
"Usage: dm-is-reflective DATABASE_URI\n" +
|
79
|
+
options.map{ |(name, desc)|
|
80
|
+
if desc.empty?
|
81
|
+
name
|
82
|
+
else
|
83
|
+
sprintf(" %-*s %-*s", maxn, name, maxd, desc)
|
84
|
+
end
|
85
|
+
}.join("\n")
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
|
2
|
+
require 'bacon'
|
3
|
+
Bacon.summary_on_exit
|
4
|
+
|
5
|
+
require 'dm-core'
|
6
|
+
require 'dm-migrations'
|
7
|
+
require 'dm-is-reflective'
|
8
|
+
|
9
|
+
module Abstract
|
10
|
+
class User
|
11
|
+
include DataMapper::Resource
|
12
|
+
has n, :comments
|
13
|
+
|
14
|
+
property :id, Serial
|
15
|
+
property :login, String, :length => 70
|
16
|
+
property :sig, Text
|
17
|
+
property :created_at, DateTime
|
18
|
+
|
19
|
+
is :reflective
|
20
|
+
end
|
21
|
+
|
22
|
+
class SuperUser
|
23
|
+
include DataMapper::Resource
|
24
|
+
property :id, Serial
|
25
|
+
property :bool, Boolean
|
26
|
+
|
27
|
+
is :reflective
|
28
|
+
end
|
29
|
+
|
30
|
+
class Comment
|
31
|
+
include DataMapper::Resource
|
32
|
+
belongs_to :user, :required => false
|
33
|
+
|
34
|
+
property :id, Serial
|
35
|
+
property :title, String, :length => 50, :default => 'default title',
|
36
|
+
:allow_nil => false
|
37
|
+
property :body, Text
|
38
|
+
|
39
|
+
is :reflective
|
40
|
+
end
|
41
|
+
|
42
|
+
Tables = ['abstract_comments', 'abstract_super_users', 'abstract_users']
|
43
|
+
|
44
|
+
AttrCommon = {:allow_nil => true}
|
45
|
+
AttrCommonPK = {:serial => true, :key => true, :allow_nil => false}
|
46
|
+
AttrText = {:length => 65535}.merge(AttrCommon)
|
47
|
+
|
48
|
+
def self.next_id
|
49
|
+
@id ||= 0
|
50
|
+
@id += 1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
include Abstract
|
55
|
+
|
56
|
+
shared :reflective do
|
57
|
+
def user_fields
|
58
|
+
[[:created_at, DateTime, AttrCommon],
|
59
|
+
[:id, DataMapper::Property::Serial, AttrCommonPK],
|
60
|
+
[:login, String, {:length => 70}.merge(AttrCommon)],
|
61
|
+
[:sig, DataMapper::Property::Text, AttrText]]
|
62
|
+
end
|
63
|
+
|
64
|
+
def comment_fields
|
65
|
+
[[:body, DataMapper::Property::Text, AttrText],
|
66
|
+
[:id, DataMapper::Property::Serial, AttrCommonPK],
|
67
|
+
[:title, String, {:length => 50, :default => 'default title',
|
68
|
+
:allow_nil => false}],
|
69
|
+
[:user_id, Integer, AttrCommon]]
|
70
|
+
end
|
71
|
+
|
72
|
+
# there's differences between adapters
|
73
|
+
def super_user_fields
|
74
|
+
mysql = defined?(DataMapper::Adapters::MysqlAdapter) &&
|
75
|
+
DataMapper::Adapters::MysqlAdapter
|
76
|
+
case DataMapper.repository.adapter
|
77
|
+
when mysql
|
78
|
+
# Mysql couldn't tell it's boolean or tinyint
|
79
|
+
[[:bool, Integer, AttrCommon],
|
80
|
+
[:id, DataMapper::Property::Serial, AttrCommonPK]]
|
81
|
+
|
82
|
+
else
|
83
|
+
[[:bool, DataMapper::Property::Boolean, AttrCommon],
|
84
|
+
[:id, DataMapper::Property::Serial, AttrCommonPK]]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
before do
|
89
|
+
@dm = setup_data_mapper
|
90
|
+
[User, Comment, SuperUser].each(&:auto_migrate!)
|
91
|
+
end
|
92
|
+
|
93
|
+
def sort_fields fields
|
94
|
+
fields.sort_by{ |f| f.first.to_s }
|
95
|
+
end
|
96
|
+
|
97
|
+
def create_fake_model
|
98
|
+
model = Class.new
|
99
|
+
model.module_eval do
|
100
|
+
include DataMapper::Resource
|
101
|
+
property :id, DataMapper::Property::Serial
|
102
|
+
is :reflective
|
103
|
+
end
|
104
|
+
Abstract.const_set("Model#{Abstract.next_id}", model)
|
105
|
+
[model, setup_data_mapper]
|
106
|
+
end
|
107
|
+
|
108
|
+
def new_scope
|
109
|
+
Abstract.const_set("Scope#{Abstract.next_id}", Module.new)
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_create_comment
|
113
|
+
Comment.create(:title => 'XD')
|
114
|
+
Comment.first.title.should.eq 'XD'
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_create_user
|
118
|
+
now = Time.now
|
119
|
+
User.create(:created_at => now)
|
120
|
+
User.first.created_at.asctime.should.eq now.asctime
|
121
|
+
now
|
122
|
+
end
|
123
|
+
|
124
|
+
should 'create comment' do
|
125
|
+
test_create_comment
|
126
|
+
end
|
127
|
+
|
128
|
+
should 'create user' do
|
129
|
+
test_create_user
|
130
|
+
end
|
131
|
+
|
132
|
+
should 'storages' do
|
133
|
+
@dm.storages.sort.should.eq Tables
|
134
|
+
sort_fields(@dm.fields('abstract_comments')).should.eq comment_fields
|
135
|
+
end
|
136
|
+
|
137
|
+
should 'reflect all' do
|
138
|
+
test_create_comment # for fixtures
|
139
|
+
model, local_dm = create_fake_model
|
140
|
+
model.storage_names[:default] = 'abstract_comments'
|
141
|
+
|
142
|
+
local_dm.storages.sort.should.eq Tables
|
143
|
+
model.storage_name.should.eq 'abstract_comments'
|
144
|
+
|
145
|
+
model.send :reflect
|
146
|
+
model.all.size .should.eq 1
|
147
|
+
sort_fields(model.fields).should.eq comment_fields
|
148
|
+
model.first.title .should.eq 'XD'
|
149
|
+
end
|
150
|
+
|
151
|
+
should 'reflect and create' do
|
152
|
+
model, local_dm = create_fake_model
|
153
|
+
model.storage_names[:default] = 'abstract_comments'
|
154
|
+
model.send :reflect
|
155
|
+
|
156
|
+
model.create(:title => 'orz')
|
157
|
+
model.first.title.should.eq 'orz'
|
158
|
+
|
159
|
+
model.create
|
160
|
+
model.last.title.should.eq 'default title'
|
161
|
+
end
|
162
|
+
|
163
|
+
should 'storages and fields' do
|
164
|
+
sort_fields(@dm.fields('abstract_users')).should.eq user_fields
|
165
|
+
|
166
|
+
@dm.storages_and_fields.inject({}){ |r, i|
|
167
|
+
key, value = i
|
168
|
+
r[key] = value.sort_by{ |v| v.first.to_s }
|
169
|
+
r
|
170
|
+
}.should.eq('abstract_users' => user_fields ,
|
171
|
+
'abstract_comments' => comment_fields ,
|
172
|
+
'abstract_super_users' => super_user_fields)
|
173
|
+
end
|
174
|
+
|
175
|
+
should 'reflect type' do
|
176
|
+
model, local_dm = create_fake_model
|
177
|
+
model.storage_names[:default] = 'abstract_comments'
|
178
|
+
|
179
|
+
model.send :reflect, DataMapper::Property::Serial
|
180
|
+
model.properties.map(&:name).map(&:to_s).sort.should.eq ['id']
|
181
|
+
|
182
|
+
model.send :reflect, Integer
|
183
|
+
model.properties.map(&:name).map(&:to_s).sort.should.eq \
|
184
|
+
['id', 'user_id']
|
185
|
+
end
|
186
|
+
|
187
|
+
should 'reflect multiple' do
|
188
|
+
model, local_dm = create_fake_model
|
189
|
+
model.storage_names[:default] = 'abstract_users'
|
190
|
+
model.send :reflect, :login, DataMapper::Property::Serial
|
191
|
+
|
192
|
+
model.properties.map(&:name).map(&:to_s).sort.should.eq \
|
193
|
+
['id', 'login']
|
194
|
+
end
|
195
|
+
|
196
|
+
should 'reflect regexp' do
|
197
|
+
model, local_dm = create_fake_model
|
198
|
+
model.storage_names[:default] = 'abstract_comments'
|
199
|
+
model.send :reflect, /id$/
|
200
|
+
|
201
|
+
model.properties.map(&:name).map(&:to_s).sort.should.eq \
|
202
|
+
['id', 'user_id']
|
203
|
+
end
|
204
|
+
|
205
|
+
should 'raise ArgumentError when giving invalid argument' do
|
206
|
+
lambda{
|
207
|
+
User.send :reflect, 29
|
208
|
+
}.should.raise ArgumentError
|
209
|
+
end
|
210
|
+
|
211
|
+
should 'allow empty string' do
|
212
|
+
Comment.new(:title => '').save.should.eq true
|
213
|
+
end
|
214
|
+
|
215
|
+
should 'auto_genclasses' do
|
216
|
+
scope = new_scope
|
217
|
+
@dm.auto_genclass!(:scope => scope).map(&:to_s).sort.should.eq \
|
218
|
+
["#{scope}::AbstractComment",
|
219
|
+
"#{scope}::AbstractSuperUser",
|
220
|
+
"#{scope}::AbstractUser"]
|
221
|
+
|
222
|
+
comment = scope.const_get('AbstractComment')
|
223
|
+
|
224
|
+
sort_fields(comment.fields).should.eq comment_fields
|
225
|
+
|
226
|
+
test_create_comment
|
227
|
+
|
228
|
+
comment.first.title.should.eq 'XD'
|
229
|
+
comment.create(:title => 'orz', :body => 'dm-reflect')
|
230
|
+
comment.last.body.should.eq 'dm-reflect'
|
231
|
+
end
|
232
|
+
|
233
|
+
should 'auto_genclass' do
|
234
|
+
scope = new_scope
|
235
|
+
@dm.auto_genclass!(:scope => scope,
|
236
|
+
:storages => 'abstract_users').map(&:to_s).should.eq \
|
237
|
+
["#{scope}::AbstractUser"]
|
238
|
+
|
239
|
+
user = scope.const_get('AbstractUser')
|
240
|
+
sort_fields(user.fields).should.eq user_fields
|
241
|
+
|
242
|
+
now = test_create_user
|
243
|
+
|
244
|
+
user.first.created_at.asctime.should.eq now.asctime
|
245
|
+
user.create(:login => 'godfat')
|
246
|
+
user.last.login.should.eq 'godfat'
|
247
|
+
end
|
248
|
+
|
249
|
+
should 'auto_genclass with regexp' do
|
250
|
+
scope = new_scope
|
251
|
+
@dm.auto_genclass!(:scope => scope,
|
252
|
+
:storages => /_users$/).map(&:to_s).sort.should.eq \
|
253
|
+
["#{scope}::AbstractSuperUser", "#{scope}::AbstractUser"]
|
254
|
+
|
255
|
+
user = scope.const_get('AbstractSuperUser')
|
256
|
+
sort_fields(user.fields).should.eq sort_fields(SuperUser.fields)
|
257
|
+
end
|
258
|
+
|
259
|
+
should 'reflect return value' do
|
260
|
+
model, local_dm = create_fake_model
|
261
|
+
model.storage_names[:default] = 'abstract_comments'
|
262
|
+
mapped = model.send :reflect, /.*/
|
263
|
+
|
264
|
+
mapped.map(&:object_id).sort.should.eq \
|
265
|
+
model.properties.map(&:object_id).sort
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
module Kernel
|
270
|
+
def eq? rhs
|
271
|
+
self == rhs
|
272
|
+
end
|
273
|
+
|
274
|
+
def require_adapter adapter
|
275
|
+
require "dm-#{adapter}-adapter"
|
276
|
+
rescue LoadError
|
277
|
+
puts "skip #{adapter} test since it's not installed"
|
278
|
+
end
|
279
|
+
end
|