dm-reflection 0.0.5
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/.document +5 -0
- data/.gitignore +25 -0
- data/LICENSE +21 -0
- data/README.rdoc +77 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/dm-reflection.gemspec +79 -0
- data/lib/dm-reflection/adapters/mysql.rb +95 -0
- data/lib/dm-reflection/adapters/persevere.rb +98 -0
- data/lib/dm-reflection/adapters/postgres.rb +126 -0
- data/lib/dm-reflection/adapters/sqlite3.rb +85 -0
- data/lib/dm-reflection/builders/source_builder.rb +326 -0
- data/lib/dm-reflection/reflection.rb +73 -0
- data/lib/dm-reflection/version.rb +5 -0
- data/lib/dm-reflection.rb +6 -0
- data/spec/persevere_reflection_spec.rb +86 -0
- data/spec/rcov.opts +6 -0
- data/spec/reflection_spec.rb +108 -0
- data/spec/source_builder_spec.rb +130 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +41 -0
- data/tasks/changelog.rake +20 -0
- data/tasks/ci.rake +1 -0
- data/tasks/metrics.rake +36 -0
- data/tasks/spec.rake +25 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +131 -0
@@ -0,0 +1,326 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Reflection
|
3
|
+
module Builders
|
4
|
+
module Source
|
5
|
+
|
6
|
+
def to_ruby
|
7
|
+
Model.to_ruby(self)
|
8
|
+
end
|
9
|
+
|
10
|
+
class Model
|
11
|
+
|
12
|
+
attr_accessor :model
|
13
|
+
|
14
|
+
def self.to_ruby(model)
|
15
|
+
builder = new(model)
|
16
|
+
raise DataMapper::IncompleteModelError unless builder.complete_model?
|
17
|
+
builder.to_ruby
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def to_ruby
|
22
|
+
ruby = "class #{model}\n"
|
23
|
+
ruby += "\n"
|
24
|
+
ruby += " include DataMapper::Resource\n"
|
25
|
+
property_definitions(key_properties).each do |key_definition|
|
26
|
+
ruby += "\n #{key_definition}"
|
27
|
+
end
|
28
|
+
ruby += "\n"
|
29
|
+
property_definitions(foreign_key_properties).each do |foreign_key_definition|
|
30
|
+
ruby += "\n #{foreign_key_definition}"
|
31
|
+
end
|
32
|
+
property_definitions(regular_properties).each do |property_definition|
|
33
|
+
ruby += "\n #{property_definition}"
|
34
|
+
end
|
35
|
+
ruby += "\n"
|
36
|
+
relationship_definitions(many_to_one_relationships).each do |relationship_definition|
|
37
|
+
ruby += "\n #{relationship_definition}"
|
38
|
+
end
|
39
|
+
relationship_definitions(one_to_one_relationships).each do |relationship_definition|
|
40
|
+
ruby += "\n #{relationship_definition}"
|
41
|
+
end
|
42
|
+
relationship_definitions(one_to_many_relationships).each do |relationship_definition|
|
43
|
+
ruby += "\n #{relationship_definition}"
|
44
|
+
end
|
45
|
+
relationship_definitions(many_to_many_relationships).each do |relationship_definition|
|
46
|
+
ruby += "\n #{relationship_definition}"
|
47
|
+
end
|
48
|
+
ruby += "\n"
|
49
|
+
ruby += "\nend\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
def key_properties
|
55
|
+
model.key
|
56
|
+
end
|
57
|
+
|
58
|
+
def foreign_key_properties
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
|
62
|
+
def regular_properties
|
63
|
+
model.properties - model.key - foreign_key_properties
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def many_to_one_relationships(repository = :default)
|
68
|
+
model.relationships(repository).select do |_, relationship|
|
69
|
+
relationship.is_a?(DataMapper::Associations::ManyToOne::Relationship)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def one_to_one_relationships(repository = :default)
|
74
|
+
model.relationships(repository).select do |_, relationship|
|
75
|
+
relationship.is_a?(DataMapper::Associations::OneToOne::Relationship)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def one_to_many_relationships(repository = :default)
|
80
|
+
model.relationships(repository).select do |_, relationship|
|
81
|
+
relationship.is_a?(DataMapper::Associations::OneToMany::Relationship) &&
|
82
|
+
!relationship.is_a?(DataMapper::Associations::ManyToMany::Relationship)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def many_to_many_relationships(repository = :default)
|
87
|
+
model.relationships(repository).select do |_, relationship|
|
88
|
+
relationship.is_a?(DataMapper::Associations::ManyToMany::Relationship)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def complete_model?
|
94
|
+
key_properties.any?
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def initialize(model)
|
101
|
+
@model = model
|
102
|
+
end
|
103
|
+
|
104
|
+
def property_definitions(properties)
|
105
|
+
properties.map { |property| Property.for(property).to_ruby }
|
106
|
+
end
|
107
|
+
|
108
|
+
def relationship_definitions(relationships)
|
109
|
+
relationships.map { |_, relationship| Relationship.for(relationship).to_ruby }
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
module OptionBuilder
|
116
|
+
|
117
|
+
def options
|
118
|
+
option_string(prioritized_options + rest_options)
|
119
|
+
end
|
120
|
+
|
121
|
+
def option_priorities
|
122
|
+
[]
|
123
|
+
end
|
124
|
+
|
125
|
+
def prioritized_options
|
126
|
+
[]
|
127
|
+
end
|
128
|
+
|
129
|
+
def rest_options
|
130
|
+
backend_options.select { |k,v| !option_priorities.include?(k) && !irrelevant_options.include?(k) }
|
131
|
+
end
|
132
|
+
|
133
|
+
def irrelevant_options
|
134
|
+
[]
|
135
|
+
end
|
136
|
+
|
137
|
+
def backend_options
|
138
|
+
@backend_options ||= backend.options.dup
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def option_string(relevant_options)
|
145
|
+
relevant_options.map do |key, value|
|
146
|
+
", #{key.inspect} => #{value == 'Resource' ? value : value.inspect}"
|
147
|
+
end.join
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
class Property
|
154
|
+
|
155
|
+
include OptionBuilder
|
156
|
+
|
157
|
+
attr_accessor :backend
|
158
|
+
|
159
|
+
def self.for(backend)
|
160
|
+
new(backend)
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_ruby
|
164
|
+
"property :#{name}, #{type}#{options}"
|
165
|
+
end
|
166
|
+
|
167
|
+
def name
|
168
|
+
backend.name
|
169
|
+
end
|
170
|
+
|
171
|
+
def type
|
172
|
+
Extlib::Inflection.demodulize(backend.type)
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
def options
|
177
|
+
backend.type == DataMapper::Types::Serial ? '' : super
|
178
|
+
end
|
179
|
+
|
180
|
+
def option_priorities
|
181
|
+
[ :key, :required, :unique, :unique_index ]
|
182
|
+
end
|
183
|
+
|
184
|
+
def prioritized_options
|
185
|
+
option_priorities.inject([]) do |memo, name|
|
186
|
+
if name == :required
|
187
|
+
memo << [ name, backend_options[name] ] if backend_options[name] && !backend_options[:key]
|
188
|
+
else
|
189
|
+
memo << [ name, backend_options[name] ] if backend_options[name]
|
190
|
+
end
|
191
|
+
memo
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def irrelevant_options
|
196
|
+
[]
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def initialize(backend)
|
202
|
+
@backend = backend
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
class Relationship
|
208
|
+
|
209
|
+
include OptionBuilder
|
210
|
+
|
211
|
+
attr_accessor :backend
|
212
|
+
|
213
|
+
def self.for(backend)
|
214
|
+
case backend
|
215
|
+
when DataMapper::Associations::ManyToOne::Relationship then ManyToOne. new(backend)
|
216
|
+
when DataMapper::Associations::OneToOne::Relationship then OneToOne. new(backend)
|
217
|
+
when DataMapper::Associations::OneToMany::Relationship then OneToMany. new(backend)
|
218
|
+
when DataMapper::Associations::ManyToMany::Relationship then ManyToMany.new(backend)
|
219
|
+
else
|
220
|
+
raise ArgumentError, "#{backend.class} is no valid datamapper relationship"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def to_ruby
|
225
|
+
"#{type} #{cardinality}, :#{name}#{options}"
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
def type
|
230
|
+
raise NotImplementedError
|
231
|
+
end
|
232
|
+
|
233
|
+
def name
|
234
|
+
backend.name
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
def cardinality
|
239
|
+
"#{min}..#{max}"
|
240
|
+
end
|
241
|
+
|
242
|
+
def min
|
243
|
+
backend.min
|
244
|
+
end
|
245
|
+
|
246
|
+
def max
|
247
|
+
backend.max
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
def option_priorities
|
252
|
+
[ :through, :constraint ]
|
253
|
+
end
|
254
|
+
|
255
|
+
def prioritized_options
|
256
|
+
option_priorities.inject([]) do |memo, name|
|
257
|
+
if name == :through && through = backend_options[:through]
|
258
|
+
value = through.is_a?(Symbol) ? through : Extlib::Inflection.demodulize(through)
|
259
|
+
memo << [ :through, value ]
|
260
|
+
end
|
261
|
+
memo
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def irrelevant_options
|
266
|
+
[ :min, :max, :parent_repository_name, :child_repository_name ]
|
267
|
+
end
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
def initialize(backend)
|
272
|
+
@backend = backend
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
276
|
+
|
277
|
+
class ManyToOne < Relationship
|
278
|
+
def to_ruby
|
279
|
+
"#{type} :#{name}#{options}"
|
280
|
+
end
|
281
|
+
def type
|
282
|
+
:belongs_to
|
283
|
+
end
|
284
|
+
def cardinality
|
285
|
+
''
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
class OneToOne < Relationship
|
290
|
+
def type
|
291
|
+
:has
|
292
|
+
end
|
293
|
+
def cardinality
|
294
|
+
min == 1 && max == 1 ? '1' : super
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
class OneToMany < Relationship
|
299
|
+
def type
|
300
|
+
:has
|
301
|
+
end
|
302
|
+
def cardinality
|
303
|
+
max_string = max == Infinity ? 'n' : max.to_s
|
304
|
+
|
305
|
+
if min == 0 || min == max
|
306
|
+
max_string
|
307
|
+
elsif max == Infinity
|
308
|
+
"#{min}..#{max_string}"
|
309
|
+
else
|
310
|
+
super
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
class ManyToMany < OneToMany
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
# Ensure activation when this file is required
|
324
|
+
Model.append_extensions(Reflection::Builders::Source)
|
325
|
+
|
326
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Reflection
|
3
|
+
##
|
4
|
+
# Main reflection method reflects models out of a repository.
|
5
|
+
# @param [Slug] repository is the key to the repository that will be reflected.
|
6
|
+
# @param [Constant] namespace is the namespace into which the reflected models will be added
|
7
|
+
# @param [Boolean] overwrite indicates the reflected models should replace existing models or not.
|
8
|
+
# @return [DataMapper::Model Array] the reflected models.
|
9
|
+
#
|
10
|
+
def self.reflect(repository, namespace = Object, overwrite = false)
|
11
|
+
adapter = DataMapper.repository(repository).adapter
|
12
|
+
|
13
|
+
models = []
|
14
|
+
|
15
|
+
adapter.get_storage_names.each do |storage_name|
|
16
|
+
namespace_parts = storage_name.split('__').map do |part|
|
17
|
+
Extlib::Inflection.classify(part)
|
18
|
+
end
|
19
|
+
|
20
|
+
model_name = namespace_parts.pop
|
21
|
+
|
22
|
+
namespace = if namespace_parts.any?
|
23
|
+
Object.make_module(namespace_parts.join('::'))
|
24
|
+
else
|
25
|
+
Object
|
26
|
+
end
|
27
|
+
|
28
|
+
next if namespace.const_defined?(model_name) && !overwrite
|
29
|
+
|
30
|
+
anonymous_model = DataMapper::Model.new do
|
31
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
32
|
+
storage_names[#{repository.inspect}]='#{storage_name}'
|
33
|
+
RUBY
|
34
|
+
unless repository == DataMapper::Repository.default_name
|
35
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
36
|
+
def self.default_repository_name
|
37
|
+
#{repository.inspect}
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
model = namespace.const_set(model_name, anonymous_model)
|
44
|
+
|
45
|
+
adapter.get_properties(storage_name).each do |attribute|
|
46
|
+
attribute.delete_if { |k,v| v.nil? }
|
47
|
+
model.property(attribute.delete(:name).to_sym, attribute.delete(:type), attribute)
|
48
|
+
end
|
49
|
+
|
50
|
+
models << model
|
51
|
+
end
|
52
|
+
models
|
53
|
+
end
|
54
|
+
end # module Reflection
|
55
|
+
|
56
|
+
module Adapters
|
57
|
+
extendable do
|
58
|
+
##
|
59
|
+
# Glue method that will register reflection extensions for adapters if the adapters are loaded.
|
60
|
+
#
|
61
|
+
# @param [Constant] const_name is the constant defined by the adapter.
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
def const_added(const_name)
|
65
|
+
if DataMapper::Reflection.const_defined?(const_name)
|
66
|
+
adapter = const_get(const_name)
|
67
|
+
adapter.send(:include, DataMapper::Reflection.const_get(const_name))
|
68
|
+
end
|
69
|
+
super
|
70
|
+
end # const_added
|
71
|
+
end # extendable block
|
72
|
+
end # module Adapters
|
73
|
+
end # module DataMapper
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
if ENV['ADAPTER'] == 'persevere'
|
4
|
+
|
5
|
+
POST = <<-RUBY
|
6
|
+
class Post
|
7
|
+
|
8
|
+
include DataMapper::Resource
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
|
12
|
+
property :created_at, DateTime
|
13
|
+
property :body, Text, :length => 65535, :lazy => true
|
14
|
+
property :updated_at, DateTime
|
15
|
+
|
16
|
+
|
17
|
+
end
|
18
|
+
RUBY
|
19
|
+
|
20
|
+
POST_COMMENT = <<-RUBY
|
21
|
+
class PostComment
|
22
|
+
|
23
|
+
include DataMapper::Resource
|
24
|
+
|
25
|
+
property :id, Serial
|
26
|
+
|
27
|
+
property :created_at, DateTime
|
28
|
+
property :body, Text, :length => 65535, :lazy => true
|
29
|
+
property :updated_at, DateTime
|
30
|
+
property :score, Integer
|
31
|
+
property :blog_post, DataMapper::Types::JsonReference, :reference => :Post
|
32
|
+
|
33
|
+
|
34
|
+
end
|
35
|
+
RUBY
|
36
|
+
|
37
|
+
PERSEVERE_REFLECTION_SOURCES = [ POST, POST_COMMENT ]
|
38
|
+
|
39
|
+
describe 'The Persevere DataMapper reflection module' do
|
40
|
+
|
41
|
+
before(:each) do
|
42
|
+
@adapter = repository(:default).adapter
|
43
|
+
|
44
|
+
PERSEVERE_REFLECTION_SOURCES.each { |source| eval(source) }
|
45
|
+
|
46
|
+
@models = {
|
47
|
+
:Post => POST,
|
48
|
+
:PostComment => POST_COMMENT,
|
49
|
+
}
|
50
|
+
|
51
|
+
@models.each_key { |model| Extlib::Inflection.constantize(model.to_s).auto_migrate! }
|
52
|
+
Post.send(:property, :comments, DataMapper::Types::JsonReferenceCollection, :reference => :PostComment)
|
53
|
+
Post.auto_upgrade!
|
54
|
+
@models.each_key { |model| remove_model_from_memory( Extlib::Inflection.constantize(model.to_s) ) }
|
55
|
+
end
|
56
|
+
|
57
|
+
after(:each) do
|
58
|
+
@models.each_key do |model_name|
|
59
|
+
next unless Object.const_defined?(model_name)
|
60
|
+
model = Extlib::Inflection.constantize(model_name.to_s)
|
61
|
+
remove_model_from_memory(model)
|
62
|
+
end
|
63
|
+
@models.each_key do |model_name|
|
64
|
+
next unless Object.const_defined?(model_name)
|
65
|
+
model = Extlib::Inflection.constantize(model_name.to_s)
|
66
|
+
remove_model_from_memory(model)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should return an array of tables" do
|
71
|
+
@adapter.get_storage_names.should be_kind_of(Array)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should return a table definition" do
|
75
|
+
result = @adapter.get_properties("post")
|
76
|
+
result.should be_kind_of(Array)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should reflect out JsonReference types" do
|
80
|
+
DataMapper::Reflection.reflect(:default)
|
81
|
+
PostComment.properties[:blog_post].type.should eql DataMapper::Types::JsonReference
|
82
|
+
Post.properties[:comments].type.should eql DataMapper::Types::JsonReferenceCollection
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
data/spec/rcov.opts
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'dm-reflection/builders/source_builder'
|
3
|
+
|
4
|
+
BLOGPOST = <<-RUBY
|
5
|
+
class BlogPost
|
6
|
+
|
7
|
+
include DataMapper::Resource
|
8
|
+
|
9
|
+
property :id, Serial
|
10
|
+
|
11
|
+
property :created_at, DateTime
|
12
|
+
property :body, Text, :length => 65535, :lazy => true
|
13
|
+
property :updated_at, DateTime
|
14
|
+
|
15
|
+
|
16
|
+
end
|
17
|
+
RUBY
|
18
|
+
|
19
|
+
COMMENT = <<-RUBY
|
20
|
+
class Comment
|
21
|
+
|
22
|
+
include DataMapper::Resource
|
23
|
+
|
24
|
+
property :id, Serial
|
25
|
+
|
26
|
+
property :created_at, DateTime
|
27
|
+
property :body, Text, :length => 65535, :lazy => true
|
28
|
+
property :updated_at, DateTime
|
29
|
+
property :score, Integer
|
30
|
+
|
31
|
+
|
32
|
+
end
|
33
|
+
RUBY
|
34
|
+
|
35
|
+
REFLECTION_SOURCES = [ BLOGPOST, COMMENT ]
|
36
|
+
|
37
|
+
describe 'The DataMapper reflection module' do
|
38
|
+
|
39
|
+
before(:each) do
|
40
|
+
@adapter = repository(:default).adapter
|
41
|
+
|
42
|
+
REFLECTION_SOURCES.each { |source| eval(source) }
|
43
|
+
|
44
|
+
@models = {
|
45
|
+
:BlogPost => BLOGPOST,
|
46
|
+
:Comment => COMMENT,
|
47
|
+
}
|
48
|
+
|
49
|
+
@models.each_key { |model| Extlib::Inflection.constantize(model.to_s).auto_migrate! }
|
50
|
+
@models.each_key { |model| remove_model_from_memory( Extlib::Inflection.constantize(model.to_s) ) }
|
51
|
+
end
|
52
|
+
|
53
|
+
after(:each) do
|
54
|
+
@models.each_key do |model_name|
|
55
|
+
next unless Object.const_defined?(model_name)
|
56
|
+
model = Extlib::Inflection.constantize(model_name.to_s)
|
57
|
+
model.auto_migrate_down!
|
58
|
+
remove_model_from_memory(model)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'repository(:name).reflect' do
|
63
|
+
it 'should reflect all the models in a repository' do
|
64
|
+
# Reflect the models back into memory.
|
65
|
+
DataMapper::Reflection.reflect(:default)
|
66
|
+
|
67
|
+
# Iterate through each model in memory and verify the source is the same as the original.
|
68
|
+
# using model.to_ruby
|
69
|
+
@models.each_pair do |model_name, source|
|
70
|
+
model = Extlib::Inflection.constantize(model_name.to_s)
|
71
|
+
reflected_source = model.to_ruby
|
72
|
+
model.to_ruby.should == source
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'reflected model instance' do
|
78
|
+
it 'should respond to default_repository_name and return the correct repo for a reflected model' do
|
79
|
+
# Reflect the models back into memory.
|
80
|
+
DataMapper::Reflection.reflect(:default)
|
81
|
+
|
82
|
+
@models.each_key do |model_name|
|
83
|
+
model = Extlib::Inflection.constantize(model_name.to_s)
|
84
|
+
model.should respond_to(:default_repository_name)
|
85
|
+
model.default_repository_name.should == :default
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'reflective adapter' do
|
91
|
+
it 'should respond to get_storage_names and return an array of models' do
|
92
|
+
@adapter.should respond_to(:get_storage_names)
|
93
|
+
@adapter.get_storage_names.should be_kind_of(Array)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should respond to get_properties and return an array of properties" do
|
97
|
+
@adapter.should respond_to(:get_properties)
|
98
|
+
tables = @adapter.get_storage_names
|
99
|
+
properties = @adapter.get_properties(tables[0])
|
100
|
+
properties.should be_kind_of(Array)
|
101
|
+
a_property = properties[0]
|
102
|
+
a_property.should be_kind_of(Hash)
|
103
|
+
a_property.keys.should include(:name)
|
104
|
+
a_property.keys.should include(:type)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|