diff --git src/main/ruby/hbase/hbase.rb src/main/ruby/hbase/hbase.rb index 2c37840..5ab6a72 100644 --- src/main/ruby/hbase/hbase.rb +++ src/main/ruby/hbase/hbase.rb @@ -45,8 +45,8 @@ module Hbase end # Create new one each time - def table(table, formatter) - ::Hbase::Table.new(configuration, table, formatter) + def table(table, shell) + ::Hbase::Table.new(configuration, table, shell) end def replication_admin(formatter) diff --git src/main/ruby/hbase/table.rb src/main/ruby/hbase/table.rb index 41dcf21..ac26e15 100644 --- src/main/ruby/hbase/table.rb +++ src/main/ruby/hbase/table.rb @@ -25,15 +25,102 @@ include Java module Hbase class Table include HBaseConstants + + # Add the command 'name' to table s.t. the shell command also called via 'name' + # and has an internal method also called 'name'. + # + # e.g. name = scan, adds table.scan which calls Scan.scan + def self.add_shell_command(name) + self.add_command(name, name, name) + end + + # add a named command to the table instance + # + # name - name of the command that should added to the table + # (eg. sending 'scan' here would allow you to do table.scan) + # shell_command - name of the command in the shell + # internal_method_name - name of the method in the shell command to forward the call + def self.add_command(name, shell_command, internal_method_name) + method = name.to_sym + self.class_eval do + define_method method do |*args| + @shell.internal_command(shell_command, internal_method_name, self, *args) + end + end + end + + # General help for the table + # class level so we can call it from anywhere + def self.help + return <<-EOF +Help for table-reference commands. + +You can either create a table via 'create' and then manipulate the table via commands like 'put', 'get', etc. +See the standard help information for how to use each of these commands. + +However, as of 0.96, you can also get a reference to a table, on which you can invoke commands. +For instance, you can get create a table and keep around a reference to it via: + + hbase> t = create 't', 'cf' + +Or, if you have already created the table, you can get a reference to it: + + hbase> t = get_table 't' + +You can do things like call 'put' on the table: + + hbase> t.put 'r', 'cf:q', 'v' + +which puts a row 'r' with column family 'cf', qualifier 'q' and value 'v' into table t. + +To read the data out, you can scan the table: + + hbase> t.scan + +which will read all the rows in table 't'. + +Essentially, any command that takes a table name can also be done via table reference. +Other commands include things like: get, delete, deleteall, +get_all_columns, get_counter, count, incr. These functions, along with +the standard JRuby object methods are also available via tab completion. + +For more information on how to use each of these commands, you can also just type: + + hbase> t.help 'scan' + +which will output more information on how to use that command. + +You can also do general admin actions directly on a table; things like enable, disable, +flush and drop just by typing: + + hbase> t.enable + hbase> t.flush + hbase> t.disable + hbase> t.drop + +Note that after dropping a table, your reference to it becomes useless and further usage +is undefined (and not recommended). +EOF + end + + #--------------------------------------------------------------------------------------------- + + # let external objects read the underlying table object attr_reader :table + # let external objects read the table name + attr_reader :name - def initialize(configuration, table_name, formatter) + def initialize(configuration, table_name, shell) @table = org.apache.hadoop.hbase.client.HTable.new(configuration, table_name) + @name = table_name + @shell = shell end + # Note the below methods are prefixed with '_' to hide them from the average user, as + # they will be much less likely to tab complete to the 'dangerous' internal method #---------------------------------------------------------------------------------------------- # Put a cell 'value' at specified table/row/column - def put(row, column, value, timestamp = nil) + def _put_internal(row, column, value, timestamp = nil) p = org.apache.hadoop.hbase.client.Put.new(row.to_s.to_java_bytes) family, qualifier = parse_column_name(column) if timestamp @@ -46,13 +133,13 @@ module Hbase #---------------------------------------------------------------------------------------------- # Delete a cell - def delete(row, column, timestamp = org.apache.hadoop.hbase.HConstants::LATEST_TIMESTAMP) - deleteall(row, column, timestamp) + def _delete_internal(row, column, timestamp = org.apache.hadoop.hbase.HConstants::LATEST_TIMESTAMP) + deleteall_internal(row, column, timestamp) end #---------------------------------------------------------------------------------------------- # Delete a row - def deleteall(row, column = nil, timestamp = org.apache.hadoop.hbase.HConstants::LATEST_TIMESTAMP) + def _deleteall_internal(row, column = nil, timestamp = org.apache.hadoop.hbase.HConstants::LATEST_TIMESTAMP) d = org.apache.hadoop.hbase.client.Delete.new(row.to_s.to_java_bytes, timestamp, nil) if column family, qualifier = parse_column_name(column) @@ -63,7 +150,7 @@ module Hbase #---------------------------------------------------------------------------------------------- # Increment a counter atomically - def incr(row, column, value = nil) + def _incr_internal(row, column, value = nil) value ||= 1 family, qualifier = parse_column_name(column) @table.incrementColumnValue(row.to_s.to_java_bytes, family, qualifier, value) @@ -71,7 +158,7 @@ module Hbase #---------------------------------------------------------------------------------------------- # Count rows in a table - def count(interval = 1000, caching_rows = 10) + def _count_internal(interval = 1000, caching_rows = 10) # We can safely set scanner caching with the first key only filter scan = org.apache.hadoop.hbase.client.Scan.new scan.cache_blocks = false @@ -98,7 +185,7 @@ module Hbase #---------------------------------------------------------------------------------------------- # Get from table - def get(row, *args) + def _get_internal(row, *args) get = org.apache.hadoop.hbase.client.Get.new(row.to_s.to_java_bytes) maxlength = -1 @@ -188,7 +275,7 @@ module Hbase #---------------------------------------------------------------------------------------------- # Fetches and decodes a counter value from hbase - def get_counter(row, column) + def _get_counter_internal(row, column) family, qualifier = parse_column_name(column.to_s) # Format get request get = org.apache.hadoop.hbase.client.Get.new(row.to_s.to_java_bytes) @@ -205,8 +292,8 @@ module Hbase end #---------------------------------------------------------------------------------------------- - # Scans whole table or a range of keys and returns rows matching specific criterias - def scan(args = {}) + # Scans whole table or a range of keys and returns rows matching specific criteria + def _scan_internal(args = {}) unless args.kind_of?(Hash) raise ArgumentError, "Arguments should be a hash. Failed to parse #{args.inspect}, #{args.class}" end @@ -298,8 +385,55 @@ module Hbase return ((block_given?) ? count : res) end + #---------------------------- + # Add general administration utilities to the shell + # each of the names below adds this method name to the table + # by callling the corresponding method in the shell + # Add single method utilities to the current class + # Generally used for admin functions which just have one name and take the table name + def self.add_admin_utils(*args) + args.each do |method| + define_method method do + @shell.command(method, @name) + end + end + end + + #Add the following admin utilities to the table + add_admin_utils :enable, :disable, :flush, :drop, :describe + + #---------------------------- + #give the general help for the table + # or the named command + def help (command = nil) + #if there is a command, get the per-command help from the shell + if command + begin + return @shell.help_command(command) + rescue NoMethodError + puts "Command \'#{command}\' does not exist. Please see general table help." + return nil + end + end + return @shell.help('table_help') + end + + # Table to string + def to_s + cl = self.class() + return "#{cl} - #{@name}" + end + + # Standard ruby call to get the return value for an object + # overriden here so we get sane semantics for printing a table on return + def inspect + to_s + end + #---------------------------------------------------------------------------------------- # Helper methods + #everthing below here is 'private' - can only be called from within the class context + private # Returns a list of column names in the table def get_all_columns @@ -345,6 +479,5 @@ module Hbase end (maxlength != -1) ? val[0, maxlength] : val end - end end diff --git src/main/ruby/shell.rb src/main/ruby/shell.rb index 53f3de8..3b14cd6 100644 --- src/main/ruby/shell.rb +++ src/main/ruby/shell.rb @@ -80,7 +80,7 @@ module Shell end def hbase_table(name) - hbase.table(name, formatter) + hbase.table(name, self) end def hbase_replication_admin @@ -93,10 +93,15 @@ module Shell def export_commands(where) ::Shell.commands.keys.each do |cmd| + # here where is the IRB namespace + # this method just adds the call to the specified command + # which just references back to 'this' shell object + # a decently extensible way to add commands where.send :instance_eval, <<-EOF def #{cmd}(*args) - @shell.command('#{cmd}', *args) + ret = @shell.command('#{cmd}', *args) puts + return ret end EOF end @@ -106,8 +111,17 @@ module Shell ::Shell.commands[command.to_s].new(self) end + #call the method 'command' on the specified command def command(command, *args) - command_instance(command).command_safe(self.debug, *args) + internal_command(command, :command, *args) + end + + #call a specific internal method in the command instance + # command - name of the command to call + # method_name - name of the method on the command to call. Defaults to just 'command' + # args - to be passed to the named method + def internal_command(command, method_name= :command, *args) + command_instance(command).command_safe(self.debug,method_name, *args) end def print_banner @@ -211,6 +225,7 @@ Shell.load_command_group( :commands => %w[ status version + table_help ] ) @@ -234,6 +249,7 @@ Shell.load_command_group( show_filters alter_status alter_async + get_table ] ) diff --git src/main/ruby/shell/commands.rb src/main/ruby/shell/commands.rb index af6df33..c9004fa 100644 --- src/main/ruby/shell/commands.rb +++ src/main/ruby/shell/commands.rb @@ -21,14 +21,18 @@ module Shell module Commands class Command - attr_accessor :shell def initialize(shell) - self.shell = shell + @shell = shell end - def command_safe(debug, *args) - translate_hbase_exceptions(*args) { command(*args) } + #wrap an execution of cmd to catch hbase exceptions + # cmd - command name to execture + # args - arguments to pass to the command + def command_safe(debug, cmd = :command, *args) + # send is internal ruby method to call 'cmd' with *args + #(everything is a message, so this is just the formal semantics to support that idiom) + translate_hbase_exceptions(*args) { send(cmd,*args) } rescue => e puts puts "ERROR: #{e}" @@ -37,30 +41,28 @@ module Shell puts "Here is some help for this command:" puts help puts - ensure - return nil end def admin - shell.hbase_admin + @shell.hbase_admin end def table(name) - shell.hbase_table(name) + @shell.hbase_table(name) end def replication_admin - shell.hbase_replication_admin + @shell.hbase_replication_admin end def security_admin - shell.hbase_security_admin + @shell.hbase_security_admin end #---------------------------------------------------------------------- def formatter - shell.formatter + @shell.formatter end def format_simple_command @@ -70,6 +72,14 @@ module Shell formatter.footer(now) end + def format_and_return_simple_command + now = Time.now + ret = yield + formatter.header + formatter.footer(now) + return ret + end + def translate_hbase_exceptions(*args) yield rescue org.apache.hadoop.hbase.TableNotFoundException diff --git src/main/ruby/shell/commands/count.rb src/main/ruby/shell/commands/count.rb index 6596441..66ee700 100644 --- src/main/ruby/shell/commands/count.rb +++ src/main/ruby/shell/commands/count.rb @@ -35,10 +35,22 @@ parameter. Examples: hbase> count 't1', INTERVAL => 100000 hbase> count 't1', CACHE => 1000 hbase> count 't1', INTERVAL => 10, CACHE => 1000 + +The same commands also can be run on a table reference. Suppose you had a reference +t to table 't1', the corresponding commands would be: + + hbase> t.count + hbase> t.count INTERVAL => 100000 + hbase> t.count CACHE => 1000 + hbase> t.count INTERVAL => 10, CACHE => 1000 EOF end def command(table, params = {}) + count(table(table), params) + end + + def count(table, params = {}) # If the second parameter is an integer, then it is the old command syntax params = { 'INTERVAL' => params } if params.kind_of?(Fixnum) @@ -51,7 +63,7 @@ EOF # Call the counter method now = Time.now formatter.header - count = table(table).count(params['INTERVAL'].to_i, params['CACHE'].to_i) do |cnt, row| + count = table._count_internal(params['INTERVAL'].to_i, params['CACHE'].to_i) do |cnt, row| formatter.row([ "Current count: #{cnt}, row: #{row}" ]) end formatter.footer(now, count) @@ -59,3 +71,6 @@ EOF end end end + +#Add the method table.count that calls count.count +::Hbase::Table.add_shell_command("count") diff --git src/main/ruby/shell/commands/create.rb src/main/ruby/shell/commands/create.rb index 14c1b0f..0845ada 100644 --- src/main/ruby/shell/commands/create.rb +++ src/main/ruby/shell/commands/create.rb @@ -38,13 +38,22 @@ Examples: hbase> # Optionally pre-split the table into NUMREGIONS, using hbase> # SPLITALGO ("HexStringSplit", "UniformSplit" or classname) hbase> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'} + + You can also keep around a reference to the created table: + + hbase> t1 = create 't1', 'f1' + + Which gives you a reference to the table named 't1', on which you can then + call methods. EOF end def command(table, *args) format_simple_command do - admin.create(table, *args) + ret = admin.create(table, *args) end + #and then return the table you just created + table(table) end end end diff --git src/main/ruby/shell/commands/delete.rb src/main/ruby/shell/commands/delete.rb index 12bc405..67bbdc5 100644 --- src/main/ruby/shell/commands/delete.rb +++ src/main/ruby/shell/commands/delete.rb @@ -30,14 +30,26 @@ versions. To delete a cell from 't1' at row 'r1' under column 'c1' marked with the time 'ts1', do: hbase> delete 't1', 'r1', 'c1', ts1 + +The same command can also be run on a table reference. Suppose you had a reference +t to table 't1', the corresponding command would be: + + hbase> t.delete 'r1', 'c1', ts1 EOF end def command(table, row, column, timestamp = org.apache.hadoop.hbase.HConstants::LATEST_TIMESTAMP) + delete(table(table), row, column, timestamp) + end + + def delete(table, row, column, timestamp = org.apache.hadoop.hbase.HConstants::LATEST_TIMESTAMP) format_simple_command do - table(table).delete(row, column, timestamp) + table._delete_internal(row, column, timestamp) end end end end end + +#Add the method table.delete that calls delete.delete +::Hbase::Table.add_shell_command("delete") \ No newline at end of file diff --git src/main/ruby/shell/commands/deleteall.rb src/main/ruby/shell/commands/deleteall.rb index 5731b60..612d37c 100644 --- src/main/ruby/shell/commands/deleteall.rb +++ src/main/ruby/shell/commands/deleteall.rb @@ -29,14 +29,28 @@ a column and timestamp. Examples: hbase> deleteall 't1', 'r1' hbase> deleteall 't1', 'r1', 'c1' hbase> deleteall 't1', 'r1', 'c1', ts1 + +The same commands also can be run on a table reference. Suppose you had a reference +t to table 't1', the corresponding command would be: + + hbase> t.deleteall 'r1' + hbase> t.deleteall 'r1', 'c1' + hbase> t.deleteall 'r1', 'c1', ts1 EOF end - def command(table, row, column = nil, timestamp = org.apache.hadoop.hbase.HConstants::LATEST_TIMESTAMP) + def command(table, row, column, timestamp = org.apache.hadoop.hbase.HConstants::LATEST_TIMESTAMP) + deleteall(table(table), row, column, timestamp) + end + + def deleteall(table, row, column = nil, timestamp = org.apache.hadoop.hbase.HConstants::LATEST_TIMESTAMP) format_simple_command do - table(table).deleteall(row, column, timestamp) + table.deleteall_internal(row, column, timestamp) end end end end end + +#Add the method table.deleteall that calls deleteall.deleteall +::Hbase::Table.add_shell_command("deleteall") \ No newline at end of file diff --git src/main/ruby/shell/commands/get.rb src/main/ruby/shell/commands/get.rb index 754c3d6..a2f2141 100644 --- src/main/ruby/shell/commands/get.rb +++ src/main/ruby/shell/commands/get.rb @@ -36,14 +36,32 @@ a dictionary of column(s), timestamp, timerange and versions. Examples: hbase> get 't1', 'r1', 'c1' hbase> get 't1', 'r1', 'c1', 'c2' hbase> get 't1', 'r1', ['c1', 'c2'] + +The same commands also can be run on a reference to a table (obtained via get_table or + create_table). Suppose you had a reference t to table 't1', the corresponding commands would be: + + hbase> t.get 'r1' + hbase> t.get 'r1', {TIMERANGE => [ts1, ts2]} + hbase> t.get 'r1', {COLUMN => 'c1'} + hbase> t.get 'r1', {COLUMN => ['c1', 'c2', 'c3']} + hbase> t.get 'r1', {COLUMN => 'c1', TIMESTAMP => ts1} + hbase> t.get 'r1', {COLUMN => 'c1', TIMERANGE => [ts1, ts2], VERSIONS => 4} + hbase> t.get 'r1', {COLUMN => 'c1', TIMESTAMP => ts1, VERSIONS => 4} + hbase> t.get 'r1', 'c1' + hbase> t.get 'r1', 'c1', 'c2' + hbase> t.get 'r1', ['c1', 'c2'] EOF end def command(table, row, *args) + get(table(table), row, *args) + end + + def get(table, row, *args) now = Time.now formatter.header(["COLUMN", "CELL"]) - table(table).get(row, *args) do |column, value| + table._get_internal(row, *args) do |column, value| formatter.row([ column, value ]) end @@ -52,3 +70,6 @@ EOF end end end + +#add get command to table +::Hbase::Table.add_shell_command('get') diff --git src/main/ruby/shell/commands/get_counter.rb src/main/ruby/shell/commands/get_counter.rb index 3cbe226..defa3c2 100644 --- src/main/ruby/shell/commands/get_counter.rb +++ src/main/ruby/shell/commands/get_counter.rb @@ -28,11 +28,19 @@ A cell cell should be managed with atomic increment function oh HBase and the data should be binary encoded. Example: hbase> get_counter 't1', 'r1', 'c1' + +The same commands also can be run on a table reference. Suppose you had a reference +t to table 't1', the corresponding command would be: + + hbase> t.get_counter 'r1', 'c1' EOF end + def command(table, row, column, value) + get_counter(table(table), row, column, value) + end def command(table, row, column, value = nil) - if cnt = table(table).get_counter(row, column) + if cnt = table._get_counter_internal(row, column) puts "COUNTER VALUE = #{cnt}" else puts "No counter found at specified coordinates" @@ -41,3 +49,5 @@ EOF end end end + +::Hbase::Table.add_shell_command('get_counter') diff --git src/main/ruby/shell/commands/get_table.rb src/main/ruby/shell/commands/get_table.rb new file mode 100644 index 0000000..76f4b95 --- /dev/null +++ src/main/ruby/shell/commands/get_table.rb @@ -0,0 +1,46 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module Shell + module Commands + class GetTable < Command + def help + return <<-EOF +Get the given table name and return it as an actual object to +be manipulated by the user. See table.help for more information +on how to use the table. +Eg. + + hbase> t1 = get_table 't1' + +returns the table named 't1' as a table object. You can then do + + hbase> t1.help + +which will then print the help for that table. +EOF + end + + def command(table, *args) + format_and_return_simple_command do + table(table) + end + end + end + end +end diff --git src/main/ruby/shell/commands/incr.rb src/main/ruby/shell/commands/incr.rb index 38a2fc5..a10a249 100644 --- src/main/ruby/shell/commands/incr.rb +++ src/main/ruby/shell/commands/incr.rb @@ -30,13 +30,27 @@ To increment a cell value in table 't1' at row 'r1' under column hbase> incr 't1', 'r1', 'c1' hbase> incr 't1', 'r1', 'c1', 1 hbase> incr 't1', 'r1', 'c1', 10 + +The same commands also can be run on a table reference. Suppose you had a reference +t to table 't1', the corresponding command would be: + + hbase> t.incr 'r1', 'c1' + hbase> t.incr 'r1', 'c1', 1 + hbase> t.incr 'r1', 'c1', 10 EOF end - def command(table, row, column, value = nil) - cnt = table(table).incr(row, column, value) + def command(table, row, column, value) + incr(table(table), row, column, value) + end + + def incr(table, row, column, value = nil) + cnt = table._incr_internal(row, column, value) puts "COUNTER VALUE = #{cnt}" end end end end + +#add incr comamnd to Table +::Hbase::Table.add_shell_command("incr") \ No newline at end of file diff --git src/main/ruby/shell/commands/put.rb src/main/ruby/shell/commands/put.rb index dde0433..82df23a 100644 --- src/main/ruby/shell/commands/put.rb +++ src/main/ruby/shell/commands/put.rb @@ -28,14 +28,26 @@ timestamp coordinates. To put a cell value into table 't1' at row 'r1' under column 'c1' marked with the time 'ts1', do: hbase> put 't1', 'r1', 'c1', 'value', ts1 + +The same commands also can be run on a table reference. Suppose you had a reference +t to table 't1', the corresponding command would be: + + hbase> t.put 'r1', 'c1', 'value', ts1 EOF end def command(table, row, column, value, timestamp = nil) + put table(table), row, column, value, timestamp + end + + def put(table, row, column, value, timestamp = nil) format_simple_command do - table(table).put(row, column, value, timestamp) + table._put_internal(row, column, value, timestamp) end end end end end + +#Add the method table.put that calls Put.put +::Hbase::Table.add_shell_command("put") \ No newline at end of file diff --git src/main/ruby/shell/commands/scan.rb src/main/ruby/shell/commands/scan.rb index e58aaac..869d274 100644 --- src/main/ruby/shell/commands/scan.rb +++ src/main/ruby/shell/commands/scan.rb @@ -58,14 +58,28 @@ cells). This option cannot be combined with requesting specific COLUMNS. Disabled by default. Example: hbase> scan 't1', {RAW => true, VERSIONS => 10} + +Scan can also be used directly from a table, by first getting a reference to a table, like such: + + hbase> t = get_table 't' + hbase> t.scan + +Note in the above situation, you can still provide all the filtering, columns, options, etc as +described above. EOF end def command(table, args = {}) + scan(table(table), args) + end + + #internal command that actually does the scanning + def scan(table, args = {}) now = Time.now formatter.header(["ROW", "COLUMN+CELL"]) - count = table(table).scan(args) do |row, cells| + #actually do the scanning + count = table._scan_internal(args) do |row, cells| formatter.row([ row, cells ]) end @@ -74,3 +88,6 @@ EOF end end end + +#Add the method table.scan that calls Scan.scan +::Hbase::Table.add_shell_command("scan") diff --git src/main/ruby/shell/commands/table_help.rb src/main/ruby/shell/commands/table_help.rb new file mode 100644 index 0000000..e5a1858 --- /dev/null +++ src/main/ruby/shell/commands/table_help.rb @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module Shell + module Commands + class TableHelp < Command + def help + return Hbase::Table.help + end + + #just print the help + def command + # call the shell to get the nice formatting there + @shell.help_command 'table_help' + end + end + end +end diff --git src/test/ruby/hbase/admin_test.rb src/test/ruby/hbase/admin_test.rb index 0c2672b..ec4c01e 100644 --- src/test/ruby/hbase/admin_test.rb +++ src/test/ruby/hbase/admin_test.rb @@ -310,5 +310,13 @@ module Hbase assert_no_match(eval("/" + key1 + "\\$(\\d+)/"), admin.describe(@test_name)) assert_no_match(eval("/" + key2 + "/"), admin.describe(@test_name)) end + + define_test "get_table should get a real table" do + drop_test_table(@test_name) + create_test_table(@test_name) + + table = table(@test_name) + assert_not_equal(nil, table) + end end end