dm-is-reflective 1.1.0 → 1.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.
- 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
|