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
@@ -1,64 +0,0 @@
|
|
1
|
-
|
2
|
-
module DataMapper
|
3
|
-
module Is::Reflective
|
4
|
-
module MysqlAdapter
|
5
|
-
include DataMapper
|
6
|
-
|
7
|
-
def storages
|
8
|
-
select('SHOW TABLES')
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
# construct needed table metadata
|
13
|
-
def reflective_query_storage storage
|
14
|
-
sql = <<-SQL
|
15
|
-
SELECT column_name, column_default, is_nullable, data_type,
|
16
|
-
character_maximum_length, column_key, extra
|
17
|
-
FROM `information_schema`.`columns`
|
18
|
-
WHERE `table_schema` = ? AND `table_name` = ?
|
19
|
-
SQL
|
20
|
-
|
21
|
-
select(Ext::String.compress_lines(sql),
|
22
|
-
options[:path].sub('/', ''), storage)
|
23
|
-
end
|
24
|
-
|
25
|
-
def reflective_field_name field
|
26
|
-
field.column_name
|
27
|
-
end
|
28
|
-
|
29
|
-
def reflective_primitive field
|
30
|
-
field.data_type
|
31
|
-
end
|
32
|
-
|
33
|
-
def reflective_attributes field, attrs = {}
|
34
|
-
attrs[:serial] = true if field.extra == 'auto_increment'
|
35
|
-
attrs[:key] = true if field.column_key == 'PRI'
|
36
|
-
|
37
|
-
attrs[:allow_nil] = field.is_nullable == 'YES'
|
38
|
-
attrs[:default] = field.column_default if
|
39
|
-
field.column_default
|
40
|
-
|
41
|
-
attrs[:length] = field.character_maximum_length if
|
42
|
-
field.character_maximum_length
|
43
|
-
|
44
|
-
attrs
|
45
|
-
end
|
46
|
-
|
47
|
-
def reflective_lookup_primitive primitive
|
48
|
-
case primitive.upcase
|
49
|
-
when 'YEAR' ; Integer
|
50
|
-
when /\w*INT(EGER)?( SIGNED| UNSIGNED)?( ZEROFILL)?/
|
51
|
-
; Integer
|
52
|
-
when /(DOUBLE|FLOAT|DECIMAL)( SIGNED| UNSIGNED)?( ZEROFILL)?/
|
53
|
-
; BigDecimal
|
54
|
-
when /\w*BLOB|\w*BINARY|ENUM|SET|CHAR/; String
|
55
|
-
when 'TIME' ; Time
|
56
|
-
when 'DATE' ; Date
|
57
|
-
when 'DATETIME', 'TIMESTAMP' ; DateTime
|
58
|
-
when 'BOOL', 'BOOLEAN' ; Property::Boolean
|
59
|
-
when /\w*TEXT/ ; Property::Text
|
60
|
-
end || super(primitive)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
|
2
|
-
module DataMapper
|
3
|
-
module Is::Reflective
|
4
|
-
module PostgresAdapter
|
5
|
-
include DataMapper
|
6
|
-
|
7
|
-
def storages
|
8
|
-
sql = <<-SQL
|
9
|
-
SELECT table_name FROM "information_schema"."tables"
|
10
|
-
WHERE table_schema = current_schema()
|
11
|
-
SQL
|
12
|
-
|
13
|
-
select(Ext::String.compress_lines(sql))
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
def reflective_query_storage storage
|
18
|
-
sql = <<-SQL
|
19
|
-
SELECT column_name FROM "information_schema"."key_column_usage"
|
20
|
-
WHERE table_schema = current_schema() AND table_name = ?
|
21
|
-
SQL
|
22
|
-
|
23
|
-
keys = select(Ext::String.compress_lines(sql), storage).to_set
|
24
|
-
|
25
|
-
sql = <<-SQL
|
26
|
-
SELECT column_name, column_default, is_nullable,
|
27
|
-
character_maximum_length, udt_name
|
28
|
-
FROM "information_schema"."columns"
|
29
|
-
WHERE table_schema = current_schema() AND table_name = ?
|
30
|
-
SQL
|
31
|
-
|
32
|
-
select(Ext::String.compress_lines(sql), storage).map{ |struct|
|
33
|
-
struct.instance_eval <<-END_EVAL
|
34
|
-
def key?
|
35
|
-
#{keys.member?(struct.column_name)}
|
36
|
-
end
|
37
|
-
END_EVAL
|
38
|
-
struct
|
39
|
-
}
|
40
|
-
end
|
41
|
-
|
42
|
-
def reflective_field_name field
|
43
|
-
field.column_name
|
44
|
-
end
|
45
|
-
|
46
|
-
def reflective_primitive field
|
47
|
-
field.udt_name
|
48
|
-
end
|
49
|
-
|
50
|
-
def reflective_attributes field, attrs = {}
|
51
|
-
# strip data type
|
52
|
-
field.column_default.gsub!(/(.*?)::[\w\s]*/, '\1') if field.column_default
|
53
|
-
|
54
|
-
attrs[:serial] = true if field.column_default =~ /nextval\('\w+'\)/
|
55
|
-
attrs[:key] = true if field.key?
|
56
|
-
attrs[:allow_nil] = field.is_nullable == 'YES'
|
57
|
-
# strip string quotation
|
58
|
-
attrs[:default] = field.column_default.gsub(/^'(.*?)'$/, '\1') if
|
59
|
-
field.column_default && !attrs[:serial]
|
60
|
-
|
61
|
-
if field.character_maximum_length
|
62
|
-
attrs[:length] = field.character_maximum_length
|
63
|
-
elsif field.udt_name.upcase == 'TEXT'
|
64
|
-
attrs[:length] = Property::Text.length
|
65
|
-
end
|
66
|
-
|
67
|
-
attrs
|
68
|
-
end
|
69
|
-
|
70
|
-
def reflective_lookup_primitive primitive
|
71
|
-
case primitive.upcase
|
72
|
-
when /^INT\d+$/ ; Integer
|
73
|
-
when /^FLOAT\d+$/ ; Float
|
74
|
-
when 'VARCHAR', 'BPCHAR'; String
|
75
|
-
when 'TIMESTAMP', 'DATE'; DateTime
|
76
|
-
when 'TEXT' ; Property::Text
|
77
|
-
when 'BOOL' ; Property::Boolean
|
78
|
-
end || super(primitive)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
|
2
|
-
module DataMapper
|
3
|
-
module Is::Reflective
|
4
|
-
module SqliteAdapter
|
5
|
-
include DataMapper
|
6
|
-
|
7
|
-
def storages
|
8
|
-
sql = <<-SQL
|
9
|
-
SELECT name
|
10
|
-
FROM sqlite_master
|
11
|
-
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
12
|
-
SQL
|
13
|
-
|
14
|
-
select(Ext::String.compress_lines(sql))
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
def reflective_query_storage storage
|
19
|
-
select('PRAGMA table_info(?)', storage)
|
20
|
-
end
|
21
|
-
|
22
|
-
def reflective_field_name field
|
23
|
-
field.name
|
24
|
-
end
|
25
|
-
|
26
|
-
def reflective_primitive field
|
27
|
-
field.type.gsub(/\(\d+\)/, '')
|
28
|
-
end
|
29
|
-
|
30
|
-
def reflective_attributes field, attrs = {}
|
31
|
-
if field.pk != 0
|
32
|
-
attrs[:key] = true
|
33
|
-
attrs[:serial] = true if supports_serial?
|
34
|
-
end
|
35
|
-
attrs[:allow_nil] = field.notnull == 0
|
36
|
-
attrs[:default] = field.dflt_value[1..-2] if field.dflt_value
|
37
|
-
|
38
|
-
if field.type.upcase == 'TEXT'
|
39
|
-
attrs[:length] = Property::Text.length
|
40
|
-
else
|
41
|
-
ergo = field.type.match(/\((\d+)\)/)
|
42
|
-
size = ergo && ergo[1].to_i
|
43
|
-
attrs[:length] = size if size
|
44
|
-
end
|
45
|
-
|
46
|
-
attrs
|
47
|
-
end
|
48
|
-
|
49
|
-
def reflective_lookup_primitive primitive
|
50
|
-
case primitive.upcase
|
51
|
-
when 'INTEGER' ; Integer
|
52
|
-
when 'REAL', 'NUMERIC'; Float
|
53
|
-
when 'VARCHAR' ; String
|
54
|
-
when 'TIMESTAMP' ; DateTime
|
55
|
-
when 'BOOLEAN' ; Property::Boolean
|
56
|
-
when 'TEXT' ; Property::Text
|
57
|
-
end || super(primitive)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
data/test/abstract.rb
DELETED
@@ -1,252 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'dm-core'
|
3
|
-
require 'dm-migrations'
|
4
|
-
require 'dm-is-reflective'
|
5
|
-
|
6
|
-
module Abstract
|
7
|
-
def self.next_id
|
8
|
-
@id ||= 0
|
9
|
-
@id += 1
|
10
|
-
end
|
11
|
-
|
12
|
-
AttrCommon = {:allow_nil => true}
|
13
|
-
AttrCommonPK = {:serial => true, :key => true, :allow_nil => false}
|
14
|
-
AttrText = {:length => 65535}.merge(AttrCommon)
|
15
|
-
|
16
|
-
def user_fields
|
17
|
-
[[:created_at, DateTime, AttrCommon],
|
18
|
-
[:id, DataMapper::Property::Serial, AttrCommonPK],
|
19
|
-
[:login, String, {:length => 70}.merge(AttrCommon)],
|
20
|
-
[:sig, DataMapper::Property::Text, AttrText]]
|
21
|
-
end
|
22
|
-
|
23
|
-
def comment_fields
|
24
|
-
[[:body, DataMapper::Property::Text, AttrText],
|
25
|
-
[:id, DataMapper::Property::Serial, AttrCommonPK],
|
26
|
-
[:title, String, {:length => 50, :default => 'default title',
|
27
|
-
:allow_nil => false}],
|
28
|
-
[:user_id, Integer, AttrCommon]]
|
29
|
-
end
|
30
|
-
|
31
|
-
# there's differences between adapters
|
32
|
-
def super_user_fields
|
33
|
-
Object.const_set(:MysqlTest, Class.new) unless defined?(MysqlTest) # dummy
|
34
|
-
case self
|
35
|
-
when MysqlTest # Mysql couldn't tell it's boolean or tinyint
|
36
|
-
[[:bool, Integer, AttrCommon],
|
37
|
-
[:id, DataMapper::Property::Serial, AttrCommonPK]]
|
38
|
-
|
39
|
-
else
|
40
|
-
[[:bool, DataMapper::Property::Boolean, AttrCommon],
|
41
|
-
[:id, DataMapper::Property::Serial, AttrCommonPK]]
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class User
|
47
|
-
include DataMapper::Resource
|
48
|
-
has n, :comments
|
49
|
-
|
50
|
-
property :id, Serial
|
51
|
-
property :login, String, :length => 70
|
52
|
-
property :sig, Text
|
53
|
-
property :created_at, DateTime
|
54
|
-
|
55
|
-
is :reflective
|
56
|
-
end
|
57
|
-
|
58
|
-
class SuperUser
|
59
|
-
include DataMapper::Resource
|
60
|
-
property :id, Serial
|
61
|
-
property :bool, Boolean
|
62
|
-
|
63
|
-
is :reflective
|
64
|
-
end
|
65
|
-
|
66
|
-
class Comment
|
67
|
-
include DataMapper::Resource
|
68
|
-
belongs_to :user, :required => false
|
69
|
-
|
70
|
-
property :id, Serial
|
71
|
-
property :title, String, :length => 50, :default => 'default title',
|
72
|
-
:allow_nil => false
|
73
|
-
property :body, Text
|
74
|
-
|
75
|
-
is :reflective
|
76
|
-
end
|
77
|
-
|
78
|
-
Tables = ['abstract_comments', 'abstract_super_users', 'abstract_users']
|
79
|
-
|
80
|
-
def sort_fields fields
|
81
|
-
fields.sort_by{ |field|
|
82
|
-
field.first.to_s
|
83
|
-
}
|
84
|
-
end
|
85
|
-
|
86
|
-
def create_fake_model
|
87
|
-
model = Class.new
|
88
|
-
model.module_eval do
|
89
|
-
include DataMapper::Resource
|
90
|
-
property :id, DataMapper::Property::Serial
|
91
|
-
is :reflective
|
92
|
-
end
|
93
|
-
Abstract.const_set("Model#{Abstract.next_id}", model)
|
94
|
-
[model, self.class.setup_data_mapper]
|
95
|
-
end
|
96
|
-
|
97
|
-
attr_reader :dm
|
98
|
-
def setup
|
99
|
-
@dm = self.class.setup_data_mapper
|
100
|
-
[User, Comment, SuperUser].each(&:auto_migrate!)
|
101
|
-
end
|
102
|
-
|
103
|
-
def new_scope
|
104
|
-
self.class.const_set("Scope#{object_id.object_id}", Module.new)
|
105
|
-
end
|
106
|
-
|
107
|
-
def test_storages
|
108
|
-
assert_equal Tables, dm.storages.sort
|
109
|
-
assert_equal comment_fields, sort_fields(dm.fields('abstract_comments'))
|
110
|
-
end
|
111
|
-
|
112
|
-
def test_create_comment
|
113
|
-
Comment.create(:title => 'XD')
|
114
|
-
assert_equal 'XD', Comment.first.title
|
115
|
-
end
|
116
|
-
|
117
|
-
def test_create_user
|
118
|
-
now = Time.now
|
119
|
-
User.create(:created_at => now)
|
120
|
-
assert_equal now.asctime, User.first.created_at.asctime
|
121
|
-
|
122
|
-
return now
|
123
|
-
end
|
124
|
-
|
125
|
-
def test_reflect_all
|
126
|
-
test_create_comment # for fixtures
|
127
|
-
model, local_dm = create_fake_model
|
128
|
-
model.storage_names[:default] = 'abstract_comments'
|
129
|
-
|
130
|
-
assert_equal Tables, local_dm.storages.sort
|
131
|
-
assert_equal 'abstract_comments', model.storage_name
|
132
|
-
|
133
|
-
model.send :reflect
|
134
|
-
assert_equal 1, model.all.size
|
135
|
-
assert_equal comment_fields, sort_fields(model.fields)
|
136
|
-
|
137
|
-
assert_equal 'XD', model.first.title
|
138
|
-
end
|
139
|
-
|
140
|
-
def test_reflect_and_create
|
141
|
-
model, local_dm = create_fake_model
|
142
|
-
model.storage_names[:default] = 'abstract_comments'
|
143
|
-
model.send :reflect
|
144
|
-
|
145
|
-
model.create(:title => 'orz')
|
146
|
-
assert_equal 'orz', model.first.title
|
147
|
-
|
148
|
-
model.create
|
149
|
-
assert_equal 'default title', model.last.title
|
150
|
-
end
|
151
|
-
|
152
|
-
def test_storages_and_fields
|
153
|
-
assert_equal user_fields, sort_fields(dm.fields('abstract_users'))
|
154
|
-
assert_equal( {'abstract_users' => user_fields,
|
155
|
-
'abstract_comments' => comment_fields,
|
156
|
-
'abstract_super_users' => super_user_fields},
|
157
|
-
dm.storages_and_fields.inject({}){ |r, i|
|
158
|
-
key, value = i
|
159
|
-
r[key] = value.sort_by{ |v| v.first.to_s }
|
160
|
-
r
|
161
|
-
} )
|
162
|
-
end
|
163
|
-
|
164
|
-
def test_reflect_type
|
165
|
-
model, local_dm = create_fake_model
|
166
|
-
model.storage_names[:default] = 'abstract_comments'
|
167
|
-
|
168
|
-
model.send :reflect, DataMapper::Property::Serial
|
169
|
-
assert_equal ['id'], model.properties.map(&:name).map(&:to_s).sort
|
170
|
-
|
171
|
-
model.send :reflect, Integer
|
172
|
-
assert_equal ['id', 'user_id'], model.properties.map(&:name).map(&:to_s).sort
|
173
|
-
end
|
174
|
-
|
175
|
-
def test_reflect_multiple
|
176
|
-
model, local_dm = create_fake_model
|
177
|
-
model.storage_names[:default] = 'abstract_users'
|
178
|
-
model.send :reflect, :login, DataMapper::Property::Serial
|
179
|
-
|
180
|
-
assert_equal ['id', 'login'], model.properties.map(&:name).map(&:to_s).sort
|
181
|
-
end
|
182
|
-
|
183
|
-
def test_reflect_regexp
|
184
|
-
model, local_dm = create_fake_model
|
185
|
-
model.storage_names[:default] = 'abstract_comments'
|
186
|
-
model.send :reflect, /id$/
|
187
|
-
|
188
|
-
assert_equal ['id', 'user_id'], model.properties.map(&:name).map(&:to_s).sort
|
189
|
-
end
|
190
|
-
|
191
|
-
def test_invalid_argument
|
192
|
-
assert_raises(ArgumentError){
|
193
|
-
User.send :reflect, 29
|
194
|
-
}
|
195
|
-
end
|
196
|
-
|
197
|
-
def test_allow_empty_string
|
198
|
-
assert Comment.new(:title => '').save
|
199
|
-
end
|
200
|
-
|
201
|
-
def test_auto_genclasses
|
202
|
-
scope = new_scope
|
203
|
-
assert_equal ["#{scope == Object ? '' : "#{scope}::"}AbstractComment",
|
204
|
-
"#{scope}::AbstractSuperUser",
|
205
|
-
"#{scope}::AbstractUser"],
|
206
|
-
dm.auto_genclass!(:scope => scope).map(&:to_s).sort
|
207
|
-
|
208
|
-
comment = scope.const_get('AbstractComment')
|
209
|
-
|
210
|
-
assert_equal comment_fields, sort_fields(comment.fields)
|
211
|
-
|
212
|
-
test_create_comment
|
213
|
-
|
214
|
-
assert_equal 'XD', comment.first.title
|
215
|
-
comment.create(:title => 'orz', :body => 'dm-reflect')
|
216
|
-
assert_equal 'dm-reflect', comment.last.body
|
217
|
-
end
|
218
|
-
|
219
|
-
def test_auto_genclass
|
220
|
-
scope = new_scope
|
221
|
-
assert_equal ["#{scope}::AbstractUser"],
|
222
|
-
dm.auto_genclass!(:scope => scope,
|
223
|
-
:storages => 'abstract_users').map(&:to_s)
|
224
|
-
|
225
|
-
user = scope.const_get('AbstractUser')
|
226
|
-
assert_equal user_fields, sort_fields(user.fields)
|
227
|
-
|
228
|
-
now = test_create_user
|
229
|
-
|
230
|
-
assert_equal now.asctime, user.first.created_at.asctime
|
231
|
-
user.create(:login => 'godfat')
|
232
|
-
assert_equal 'godfat', user.last.login
|
233
|
-
end
|
234
|
-
|
235
|
-
def test_auto_genclass_with_regexp
|
236
|
-
scope = new_scope
|
237
|
-
assert_equal ["#{scope}::AbstractSuperUser", "#{scope}::AbstractUser"],
|
238
|
-
dm.auto_genclass!(:scope => scope,
|
239
|
-
:storages => /_users$/).map(&:to_s).sort
|
240
|
-
|
241
|
-
user = scope.const_get('AbstractSuperUser')
|
242
|
-
assert_equal sort_fields(SuperUser.fields), sort_fields(user.fields)
|
243
|
-
end
|
244
|
-
|
245
|
-
def test_reflect_return_value
|
246
|
-
model, local_dm = create_fake_model
|
247
|
-
model.storage_names[:default] = 'abstract_comments'
|
248
|
-
mapped = model.send :reflect, /.*/
|
249
|
-
|
250
|
-
assert_equal model.properties.map(&:object_id).sort, mapped.map(&:object_id).sort
|
251
|
-
end
|
252
|
-
end
|