dm-reflection 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|