dymos 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +71 -2
  3. data/lib/dymos/client.rb +16 -0
  4. data/lib/dymos/config.rb +34 -0
  5. data/lib/dymos/model.rb +109 -26
  6. data/lib/dymos/persistence.rb +75 -16
  7. data/lib/dymos/query/base.rb +23 -0
  8. data/lib/dymos/query/create_table.rb +72 -0
  9. data/lib/dymos/query/delete_item.rb +60 -32
  10. data/lib/dymos/query/describe.rb +3 -5
  11. data/lib/dymos/query/get_item.rb +30 -9
  12. data/lib/dymos/query/put_item.rb +67 -41
  13. data/lib/dymos/query/query.rb +112 -26
  14. data/lib/dymos/query/scan.rb +85 -12
  15. data/lib/dymos/query/update_item.rb +97 -50
  16. data/lib/dymos/version.rb +1 -1
  17. data/lib/dymos.rb +6 -4
  18. data/spec/lib/dymos/client_spec.rb +4 -0
  19. data/spec/lib/dymos/config_spec.rb +11 -0
  20. data/spec/lib/dymos/data.yml +154 -0
  21. data/spec/lib/dymos/model_spec.rb +23 -29
  22. data/spec/lib/dymos/query/create_table_spec.rb +56 -0
  23. data/spec/lib/dymos/query/delete_item_spec.rb +22 -79
  24. data/spec/lib/dymos/query/describe_spec.rb +6 -40
  25. data/spec/lib/dymos/query/get_item_spec.rb +8 -47
  26. data/spec/lib/dymos/query/put_item_spec.rb +45 -301
  27. data/spec/lib/dymos/query/query_spec.rb +45 -95
  28. data/spec/lib/dymos/query/scan_spec.rb +49 -43
  29. data/spec/lib/dymos/query/update_item_spec.rb +36 -113
  30. data/spec/lib/dymos/query_spec.rb +150 -0
  31. data/spec/spec_helper.rb +33 -0
  32. metadata +16 -12
  33. data/lib/dymos/command.rb +0 -36
  34. data/lib/dymos/query/attribute.rb +0 -14
  35. data/lib/dymos/query/builder.rb +0 -79
  36. data/lib/dymos/query/expect.rb +0 -65
  37. data/spec/lib/dymos/query/attribute_spec.rb +0 -15
  38. data/spec/lib/dymos/query/builder_spec.rb +0 -23
  39. data/spec/lib/dymos/query/expect_spec.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f5e6582dcf6a0992c22f3d13d6fcb87fec0bd0f
4
- data.tar.gz: bc4a0800891429d8a80eeeeb08c2250f594c3709
3
+ metadata.gz: a14520d556c3eb1c8f019feca759dc4a8c0bf530
4
+ data.tar.gz: b3d32561a640e4c08611a6df6aa62afe6e7e3f8f
5
5
  SHA512:
6
- metadata.gz: 1053e6b53a07c7cd72357860c883e46b5d38151fb3fe0f0cca29391a36501814b21359442c21d20c64b0b1c875b37a3983020cc3cd705758104e2513883e32ce
7
- data.tar.gz: 1204f174b48bc4a007bfa973738f5e4c06d03e7eab8e6a03e3c38dfefbd05935669ae833ee49c4407ab307e477d3a355aa947c47f8ca924c003d8d20fc04d8e7
6
+ metadata.gz: 3715cc421b3f4453ecfd1e88b1bcb544faf74bf522498bcf77a8f53b2b1119b3563cad5d508a228497ffa2c13d1a0b78f2006783fa6b18de8fd1238fc8403bf6
7
+ data.tar.gz: 1a5ffcbe966d215d3c40605068414ded37b5a692d094ad99d71c9d01c12432f602df60029e59c09c9b13787c53ca4352742db6ebb6358b438893ab56ce3a1664
data/README.md CHANGED
@@ -18,11 +18,80 @@ install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
21
+
22
+ ### テーブル生成
23
+
24
+ ```ruby
25
+ Dymos::Query::CreateTable.name('ProductCatalogs')
26
+ .attributes(category: 'S', title: 'S', ISBN:'S', price:'N')
27
+ .keys(category: 'HASH', title: 'RANGE')
28
+ .gsi([{name: 'global_index_isbn', keys: {ISBN: 'HASH'}, projection: {type: 'INCLUDE', attributes: [:title, :ISBN]}, throughput: {read: 20, write: 10}}])
29
+ .lsi([{name: 'local_index_category_price', keys: {category: 'HASH', price: 'RANGE'}}])
30
+ .throughput(read: 20, write: 10)
31
+ ```
32
+
33
+ ### モデル定義
34
+
35
+ ```ruby
36
+ class Product < Dymos::Model
37
+ table 'ProductCatalogs'
38
+ field :category, :integer
39
+ field :title, :string
40
+ field :ISBN, :string
41
+ field :price, :integer
42
+ field :authors, :array
43
+ field :created_at, :time
44
+ end
45
+ ```
46
+
47
+ ### クエリ
48
+ #### 取得
49
+
50
+ ```ruby
51
+ Product.all
52
+ ```
53
+
54
+ ```ruby
55
+ Product.find('Novels', 'The Catcher in the Rye') #key is category && title
56
+ ```
57
+
58
+ ```ruby
59
+ Product.where(category:'Comics').all
60
+ Product.where(category:'Comics').add_filter(:authors,:contains,'John Smith').all
61
+ Product.where(category:'Comics').desc.one
62
+ Product.index(:local_index_category_price).add_condition(:category,'Comics')add_condition(:price,:gt,10000).all
63
+ ```
64
+
65
+ #### 保存
66
+
67
+ ##### 新規
68
+ ```ruby
69
+ product = Product.new(params)
70
+ product.save!
71
+ ```
72
+
73
+ ##### 更新
74
+ ```ruby
75
+ product = Product.find(conditions)
76
+ product.price += 100
77
+ product.update!
78
+ ```
79
+
80
+ ```ruby
81
+ product = Product.find(conditions)
82
+ product.add(price:100).put(authors:['Andy','Bob','Charlie']).update!
83
+ ```
84
+
85
+ ## 削除
86
+ ```ruby
87
+ product = Product.find(conditions)
88
+ product.add_expected(:price,10000).delete
89
+ ```
90
+
22
91
 
23
92
  ## Contributing
24
93
 
25
- 1. Fork it ( https://github.com/[my-github-username]/dymos/fork )
94
+ 1. Fork it ( https://github.com/hoshina85/dymos/fork )
26
95
  2. Create your feature branch (`git checkout -b my-new-feature`)
27
96
  3. Commit your changes (`git commit -am 'Add some feature'`)
28
97
  4. Push to the branch (`git push origin my-new-feature`)
@@ -0,0 +1,16 @@
1
+ require 'aws-sdk-core'
2
+
3
+ module Dymos
4
+ class Client
5
+ attr_reader :client
6
+
7
+ def initialize(params={})
8
+ config = Aws.config.merge params
9
+ @client ||= Aws::DynamoDB::Client.new(config)
10
+ end
11
+
12
+ def command(name, params)
13
+ @client.send name, params
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ require 'aws-sdk-core'
2
+
3
+ module Dymos
4
+ class Config
5
+
6
+ @default={
7
+ get_item: {},
8
+ create_table: {},
9
+ query: {},
10
+ scan: {},
11
+ put_item: {},
12
+ update_item: {},
13
+ describe_table: {},
14
+ delete_item: {},
15
+ batch_get_item: {},
16
+ batch_write_item: {},
17
+ delete_table: {},
18
+ list_tables: {},
19
+ }
20
+
21
+ class << self
22
+ attr_reader :default
23
+
24
+ def default=(config)
25
+ if Hash === config
26
+ @default = config
27
+ else
28
+ raise ArgumentError, 'configuration object must be a hash'
29
+ end
30
+ end
31
+ end
32
+
33
+ end
34
+ end
data/lib/dymos/model.rb CHANGED
@@ -7,8 +7,7 @@ module Dymos
7
7
  include ActiveModel::Dirty
8
8
  include ActiveModel::Callbacks
9
9
  include Dymos::Persistence
10
- extend Dymos::Command
11
- attr_accessor :metadata
10
+ attr_accessor :metadata, :last_execute_query
12
11
 
13
12
  define_model_callbacks :save
14
13
 
@@ -18,6 +17,36 @@ module Dymos
18
17
  super
19
18
  end
20
19
 
20
+ class << self
21
+ attr_accessor :last_execute_query
22
+
23
+ def method_missing(name, *args, &block)
24
+ methods ||= Dymos::Query::Query.instance_methods(false)+
25
+ Dymos::Query::GetItem.instance_methods(false)+
26
+ Dymos::Query::Scan.instance_methods(false)
27
+ if methods.include? name
28
+ @query||={}
29
+ @query[name]=args
30
+ self
31
+ else
32
+ super
33
+ end
34
+ end
35
+ end
36
+
37
+ def method_missing(name, *args, &block)
38
+ methods ||= Dymos::Query::UpdateItem.instance_methods(false)+
39
+ Dymos::Query::PutItem.instance_methods(false)+
40
+ Dymos::Query::DeleteItem.instance_methods(false)
41
+ if methods.include? name
42
+ @query||={}
43
+ @query[name]=args
44
+ self
45
+ else
46
+ super
47
+ end
48
+ end
49
+
21
50
  def self.field(attr, type, default: nil, desc: nil)
22
51
  fail StandardError('attribute name is invalid') if attr =~ /[\!\?]$/
23
52
  fail StandardError('require "default" option') if (type == :bool && default.nil?)
@@ -32,18 +61,18 @@ module Dymos
32
61
  define_model_callbacks attr
33
62
  define_method(attr) { |raw=false|
34
63
  run_callbacks attr do
35
- val = read_attribute(attr) || default
36
- return val if raw || !val.present?
37
- case type
38
- when :bool
39
- to_b(val)
40
- when :time
41
- Time.parse val
42
- when :integer
43
- val.to_i
44
- else
45
- val
46
- end
64
+ val = read_attribute(attr) || default
65
+ return val if raw || !val.present?
66
+ case type
67
+ when :bool
68
+ to_b(val)
69
+ when :time
70
+ Time.parse val
71
+ when :integer
72
+ val.to_i
73
+ else
74
+ val
75
+ end
47
76
  end
48
77
 
49
78
  }
@@ -61,8 +90,8 @@ module Dymos
61
90
  define_model_callbacks :"set_#{attr}"
62
91
  define_method("#{attr}=") do |value, initialize=false|
63
92
  run_callbacks :"set_#{attr}" do
64
- value = value.iso8601 if self.class.fields.include?(attr) && value.is_a?(Time)
65
- write_attribute(attr, value, initialize)
93
+ value = value.iso8601 if self.class.fields.include?(attr) && value.is_a?(Time)
94
+ write_attribute(attr, value, initialize)
66
95
  end
67
96
  end
68
97
  end
@@ -71,7 +100,6 @@ module Dymos
71
100
  @fields
72
101
  end
73
102
 
74
-
75
103
  def self.table(name)
76
104
  define_singleton_method('table_name') { name }
77
105
  define_method('table_name') { name }
@@ -94,7 +122,21 @@ module Dymos
94
122
  end
95
123
 
96
124
  def self.all
97
- self.scan.execute
125
+ if @query.present? && @query.keys & [:conditions, :add_condition, :where]
126
+ builder = Dymos::Query::Query.new.name(table_name)
127
+ @query.each do |k, v|
128
+ builder.send k, *v
129
+ end
130
+ @query={}
131
+ else
132
+ builder = Dymos::Query::Scan.new.name(table_name)
133
+ end
134
+ _execute(builder)
135
+ end
136
+
137
+ def self.one
138
+ @query[:limit] = 1
139
+ self.all.first
98
140
  end
99
141
 
100
142
  def self.find(key1, key2=nil)
@@ -102,19 +144,28 @@ module Dymos
102
144
  keys={}
103
145
  keys[indexes.first[:attribute_name].to_sym] = key1
104
146
  keys[indexes.last[:attribute_name].to_sym] = key2 if indexes.size > 1
105
- self.get.key(keys).execute
147
+
148
+ builder = Dymos::Query::GetItem.new.name(table_name).key(keys)
149
+ _execute(builder)
150
+ end
151
+ def self._execute(builder)
152
+ query = builder.build
153
+ @last_execute_query = {command: builder.command, query: query}
154
+ response = Dymos::Client.new.command builder.command, query
155
+ to_model(class_name, response)
106
156
  end
107
157
 
108
158
  def self.key_scheme
109
- @key_scheme ||= new.describe_table[:table][:key_schema]
159
+ @key_scheme ||= describe[:table][:key_schema]
110
160
  end
111
161
 
112
162
  def reload!
113
163
  reset_changes
114
164
  end
115
165
 
116
- def describe_table
117
- self.class.send(:describe).execute
166
+ def self.describe
167
+ builder=Dymos::Query::Describe.new.name(table_name)
168
+ Dymos::Client.new.command :describe_table, builder.build
118
169
  end
119
170
 
120
171
  def indexes
@@ -124,10 +175,6 @@ module Dymos
124
175
  scheme.to_h
125
176
  end
126
177
 
127
- def dynamo
128
- @client ||= Aws::DynamoDB::Client.new
129
- end
130
-
131
178
  # @return [String]
132
179
  def self.class_name
133
180
  self.name
@@ -158,5 +205,41 @@ module Dymos
158
205
  end
159
206
  end
160
207
 
208
+ def self.to_model(class_name, res)
209
+ if class_name.present?
210
+ if res.data.respond_to? :items # scan, query
211
+ metadata = extract(res, :items)
212
+ res.data[:items].map do |datum|
213
+ obj = Object.const_get(class_name).new(datum)
214
+ obj.metadata = metadata
215
+ obj.new_record = false
216
+ obj
217
+ end
218
+ elsif res.data.respond_to? :attributes # put_item, update_item
219
+ return nil if res.attributes.nil?
220
+ obj = Object.const_get(class_name).new(res.attributes)
221
+ obj.metadata = extract(res, :attributes)
222
+ obj
223
+ elsif res.respond_to? :data
224
+ if res.data.respond_to? :item # get_item, delete_item
225
+ return nil if res.data.item.nil?
226
+ obj = Object.const_get(class_name).new(res.data.item)
227
+ obj.metadata = extract(res, :item)
228
+ obj.new_record = false
229
+ obj
230
+ else
231
+ res.data.to_hash # describe
232
+ end
233
+ end
234
+ else
235
+ res.data.to_hash #list_tables
236
+ end
237
+
238
+ end
239
+
240
+ def self.extract(res, ignore_key)
241
+ keys = res.data.members.reject { |a| a == ignore_key }
242
+ keys.map { |k| [k, res.data[k]] }.to_h
243
+ end
161
244
  end
162
245
  end
@@ -21,7 +21,7 @@ module Dymos
21
21
 
22
22
  def save(*)
23
23
  run_callbacks :save do
24
- create_or_update
24
+ _put
25
25
  end
26
26
  rescue => e
27
27
  false
@@ -29,39 +29,98 @@ module Dymos
29
29
 
30
30
  def save!(*)
31
31
  run_callbacks :save do
32
- create_or_update || raise(Dymos::RecordNotSaved)
32
+ _put || raise(Dymos::RecordNotSaved)
33
+ end
34
+ end
35
+
36
+ def update(*)
37
+ run_callbacks :save do
38
+ _update
39
+ end
40
+ rescue => e
41
+ false
42
+ end
43
+
44
+ def update!(*)
45
+ run_callbacks :save do
46
+ _update || raise(Dymos::RecordNotSaved)
33
47
  end
34
48
  end
35
49
 
36
50
  def delete
37
- self.class.delete.key(indexes).execute if persisted?
51
+
52
+ if persisted?
53
+ builder = Dymos::Query::DeleteItem.new
54
+
55
+ builder.name(self.table_name).key(indexes).return_values(:all_old)
56
+
57
+ @query.each do |k, v|
58
+ builder.send k, *v
59
+ end if @query.present?
60
+ @query={}
61
+
62
+ query = builder.build
63
+ @last_execute_query = {command: builder.command, query: query}
64
+ Dymos::Client.new.command builder.command, query
65
+ end
38
66
  @destroyed = true
39
67
  freeze
40
68
  end
41
69
 
42
70
  private
43
71
 
44
- def create_or_update
45
- result = new_record? ? _create_record : _update_record
46
- result != false
47
- end
48
-
49
- def _update_record()
72
+ def _put
73
+ send :created_at=, Time.new.iso8601 if respond_to? :created_at if @new_record
50
74
  send :updated_at=, Time.new.iso8601 if respond_to? :updated_at
51
- _execute
75
+ builder = Dymos::Query::PutItem.new
76
+ builder.name(self.table_name).item(attributes).return_values(:all_old)
77
+
78
+ @query.each do |k, v|
79
+ builder.send k, *v
80
+ end if @query.present?
81
+ @query={}
82
+
83
+ _execute(builder)
52
84
  end
53
85
 
54
- def _create_record()
55
- send :created_at=, Time.new.iso8601 if respond_to? :created_at
86
+ def _update
56
87
  send :updated_at=, Time.new.iso8601 if respond_to? :updated_at
57
- _execute
88
+ builder = Dymos::Query::UpdateItem.new
89
+
90
+ builder.name(self.table_name).key(indexes).return_values(:all_old)
91
+
92
+ self.changes.each do |column, change|
93
+ builder.put(column, change[1])
94
+ end
95
+
96
+ @query.each do |k, v|
97
+ builder.send k, *v
98
+ end if @query.present?
99
+ @query={}
100
+
101
+ _execute(builder)
58
102
  end
59
103
 
60
- def _execute()
61
- result = self.class.put.item(attributes).execute
104
+ #
105
+ # def _update_record()
106
+ # send :updated_at=, Time.new.iso8601 if respond_to? :updated_at
107
+ # _execute
108
+ # end
109
+ #
110
+ # def _create_record()
111
+ # send :created_at=, Time.new.iso8601 if respond_to? :created_at
112
+ # send :updated_at=, Time.new.iso8601 if respond_to? :updated_at
113
+ # _execute
114
+ # end
115
+
116
+ def _execute(builder)
117
+ query = builder.build
118
+ @last_execute_query = {command: builder.command, query: query}
119
+ response = Dymos::Client.new.command builder.command, query
120
+ fail raise(Dymos::RecordNotSaved) if response.nil?
62
121
  changes_applied
63
122
  @new_record = false
64
- result
123
+ response.present?
65
124
  end
66
125
 
67
126
  end
@@ -0,0 +1,23 @@
1
+ module Dymos
2
+ module Query
3
+ class Base
4
+ def initialize
5
+ @query={}
6
+ end
7
+
8
+ def command
9
+
10
+ end
11
+
12
+ def name(value)
13
+ @query[:table_name] = value
14
+ self
15
+ end
16
+
17
+ def build(value={})
18
+ value = Dymos::Config.default[command.to_sym].merge value
19
+ @query.merge value
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,72 @@
1
+ module Dymos
2
+ module Query
3
+ class CreateTable < Base
4
+ def command
5
+ 'create_table'
6
+ end
7
+
8
+ def attributes(value)
9
+ @query[:attribute_definitions] = _attributes(value)
10
+ self
11
+ end
12
+
13
+ private def _attributes(value)
14
+ value.map { |k, v|
15
+ {attribute_name: k.to_s, attribute_type: v.to_s}
16
+ }
17
+ end
18
+
19
+ def keys(value)
20
+ @query[:key_schema]=_keys(value)
21
+ self
22
+ end
23
+
24
+ private def _keys(value)
25
+ value.map { |k, v|
26
+ {attribute_name: k.to_s, key_type: v.to_s}
27
+ }
28
+ end
29
+
30
+ def throughput(value)
31
+ @query[:provisioned_throughput] = _throughput(value)
32
+ self
33
+ end
34
+
35
+ private def _throughput(value)
36
+ {
37
+ read_capacity_units: value[:read],
38
+ write_capacity_units: value[:write]
39
+ }
40
+ end
41
+
42
+ def gsi(value)
43
+ @query[:global_secondary_indexes] = value.map { |i|
44
+ index = _index(i)
45
+ index[:provisioned_throughput] = _throughput(i[:throughput])
46
+ index
47
+ }
48
+ self
49
+ end
50
+
51
+ def lsi(value)
52
+ @query[:local_secondary_indexes] = value.map do |i|
53
+ _index(i)
54
+ end
55
+ self
56
+ end
57
+
58
+ private def _index(i)
59
+ index = {}
60
+ index[:index_name] = i[:name]
61
+ index[:key_schema] = _keys(i[:keys])
62
+ index[:projection]= _projection_type(i)
63
+ index[:projection][:non_key_attributes] = i[:projection][:attributes] if i.try(:[], :projection).try(:[], :attributes).present?
64
+ index
65
+ end
66
+
67
+ private def _projection_type(i)
68
+ {projection_type: (i.try(:[], :projection).try(:[], :type) || 'ALL').to_s}
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,47 +1,75 @@
1
1
  module Dymos
2
2
  module Query
3
- class DeleteItem < ::Dymos::Query::Builder
3
+ class DeleteItem < Base
4
+ def command
5
+ 'delete_item'
6
+ end
4
7
 
5
- # @param [String] name
6
- # @return [self]
7
- def key(name)
8
- @key = name
8
+ def key(value)
9
+ @query[:key] = value.deep_stringify_keys
9
10
  self
10
11
  end
11
12
 
12
- def expected(params)
13
- @expected = Hash[params.map do |name, expression|
14
- operator, values = expression.split(' ', 2)
15
- if values.nil?
16
- [name, ::Dymos::Query::Expect.new.condition(operator, nil).data]
17
- else
18
- value1, value2 = values.split(' ')
19
- if value2.present?
20
- [name, ::Dymos::Query::Expect.new.condition(operator, values).data]
21
- else
22
- [name, ::Dymos::Query::Expect.new.condition(operator, value1).data]
23
- end
24
- end
13
+ def expected(value)
14
+ value.map { |v| add_expected(*v) }
15
+ self
16
+ end
25
17
 
26
- end]
18
+ def add_expected(*value)
19
+ if value.count == 2
20
+ column, operator, value = value[0], :eq, value[1]
21
+ else
22
+ column, operator, value = value
23
+ end
24
+ @query[:expected] ||= {}
25
+ @query[:expected].store(*_add_expected(column, operator, value))
27
26
  self
28
27
  end
29
28
 
30
- def query
31
- data = {
32
- table_name: @table_name.to_s,
33
- key: @key,
34
- return_values: @return_values || 'ALL_OLD'
35
- }
29
+ def _add_expected(column, operator, value)
30
+ [column.to_s, {
31
+ attribute_value_list: ([:BETWEEN, :IN].include? operator) ? [*value] : [value],
32
+ comparison_operator: operator.to_s.upcase
33
+ }
34
+ ]
35
+ end
36
36
 
37
- if @expected.present?
38
- data[:expected] = @expected
39
- if @expected.size > 1
40
- data[:conditional_operator] = @conditional_operator || 'AND'
41
- end
42
- end
43
- data
37
+ def conditional_operator(value)
38
+ @query[:conditional_operator] = value.to_s.upcase
39
+ self
40
+ end
41
+
42
+
43
+ def return_values(value)
44
+ @query[:return_values] = value.to_s.upcase
45
+ self
46
+ end
47
+
48
+ def return_consumed_capacity(value)
49
+ @query[:return_consumed_capacity] = value.to_s.upcase
50
+ self
44
51
  end
52
+
53
+ def return_item_collection_metrics(value)
54
+ @query[:return_item_collection_metrics] = value.to_s.upcase
55
+ self
56
+ end
57
+
58
+ def condition_expression(value)
59
+ @query[:condition_expression] = value
60
+ self
61
+ end
62
+
63
+ def expression_attribute_names(value)
64
+ @query[:expression_attribute_names] = value.deep_stringify_keys
65
+ self
66
+ end
67
+
68
+ def expression_attribute_values(value)
69
+ @query[:expression_attribute_values] = value.deep_stringify_keys
70
+ self
71
+ end
72
+
45
73
  end
46
74
  end
47
75
  end
@@ -1,10 +1,8 @@
1
1
  module Dymos
2
2
  module Query
3
- class Describe < ::Dymos::Query::Builder
4
- def query
5
- {
6
- table_name: @table_name.to_s
7
- }
3
+ class Describe < Base
4
+ def command
5
+ 'describe_table'
8
6
  end
9
7
  end
10
8
  end