activerecord-jdbc-adapter 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.travis.yml +3 -0
  2. data/Gemfile.lock +13 -15
  3. data/History.txt +19 -0
  4. data/README.rdoc +2 -0
  5. data/Rakefile +2 -1
  6. data/lib/arel/visitors/derby.rb +9 -2
  7. data/lib/arel/visitors/sql_server.rb +2 -0
  8. data/lib/arjdbc/db2/adapter.rb +3 -1
  9. data/lib/arjdbc/derby/adapter.rb +10 -3
  10. data/lib/arjdbc/jdbc/adapter.rb +5 -2
  11. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  12. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  13. data/lib/arjdbc/jdbc/connection.rb +5 -1
  14. data/lib/arjdbc/jdbc/missing_functionality_helper.rb +1 -0
  15. data/lib/arjdbc/mssql/adapter.rb +31 -28
  16. data/lib/arjdbc/mssql/lock_helpers.rb +72 -0
  17. data/lib/arjdbc/mysql/adapter.rb +110 -45
  18. data/lib/arjdbc/oracle/adapter.rb +7 -0
  19. data/lib/arjdbc/postgresql/adapter.rb +327 -153
  20. data/lib/arjdbc/sqlite3/adapter.rb +9 -4
  21. data/lib/arjdbc/version.rb +1 -1
  22. data/rakelib/db.rake +17 -5
  23. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +14 -4
  24. data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +25 -0
  25. data/test/db/jdbc.rb +4 -3
  26. data/test/db2_reset_column_information_test.rb +8 -0
  27. data/test/derby_reset_column_information_test.rb +8 -0
  28. data/test/derby_row_locking_test.rb +9 -0
  29. data/test/derby_simple_test.rb +40 -0
  30. data/test/h2_simple_test.rb +2 -2
  31. data/test/helper.rb +15 -2
  32. data/test/jdbc_common.rb +1 -0
  33. data/test/jndi_callbacks_test.rb +5 -9
  34. data/test/manualTestDatabase.rb +31 -31
  35. data/test/models/validates_uniqueness_of_string.rb +1 -1
  36. data/test/mssql_ignore_system_views_test.rb +27 -0
  37. data/test/mssql_null_test.rb +14 -0
  38. data/test/mssql_reset_column_information_test.rb +8 -0
  39. data/test/mssql_row_locking_sql_test.rb +159 -0
  40. data/test/mssql_row_locking_test.rb +9 -0
  41. data/test/mysql_reset_column_information_test.rb +8 -0
  42. data/test/mysql_simple_test.rb +69 -5
  43. data/test/oracle_reset_column_information_test.rb +8 -0
  44. data/test/oracle_specific_test.rb +1 -1
  45. data/test/postgres_nonseq_pkey_test.rb +1 -1
  46. data/test/postgres_reset_column_information_test.rb +8 -0
  47. data/test/postgres_simple_test.rb +72 -1
  48. data/test/row_locking.rb +90 -0
  49. data/test/simple.rb +82 -2
  50. data/test/sqlite3_reset_column_information_test.rb +8 -0
  51. data/test/sqlite3_simple_test.rb +47 -0
  52. data/test/sybase_reset_column_information_test.rb +8 -0
  53. metadata +33 -3
@@ -147,7 +147,7 @@ module ::ArJdbc
147
147
 
148
148
  def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) #:nodoc:
149
149
  sql = substitute_binds(sql, binds)
150
- @connection.execute_update(sql)
150
+ log(sql, name) { @connection.execute_update(sql) }
151
151
  id_value || last_insert_id
152
152
  end
153
153
 
@@ -208,9 +208,12 @@ module ::ArJdbc
208
208
  end
209
209
 
210
210
  def table_structure(table_name)
211
- structure = @connection.execute_query("PRAGMA table_info(#{quote_table_name(table_name)})")
212
- raise ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'" if structure.empty?
213
- structure
211
+ sql = "PRAGMA table_info(#{quote_table_name(table_name)})"
212
+ log(sql, 'SCHEMA') { @connection.execute_query(sql) }
213
+ rescue ActiveRecord::JDBCError => error
214
+ e = ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'")
215
+ e.set_backtrace error.backtrace
216
+ raise e
214
217
  end
215
218
 
216
219
  def jdbc_columns(table_name, name = nil) #:nodoc:
@@ -283,6 +286,8 @@ module ::ArJdbc
283
286
  self.limit = options[:limit] if options.include?(:limit)
284
287
  self.default = options[:default] if include_default
285
288
  self.null = options[:null] if options.include?(:null)
289
+ self.precision = options[:precision] if options.include?(:precision)
290
+ self.scale = options[:scale] if options.include?(:scale)
286
291
  end
287
292
  end
288
293
  end
@@ -1,6 +1,6 @@
1
1
  module ArJdbc
2
2
  module Version
3
- VERSION = "1.2.1"
3
+ VERSION = "1.2.2"
4
4
  end
5
5
  end
6
6
  # Compatibility with older versions of ar-jdbc for other extensions out there
@@ -2,26 +2,38 @@ namespace :db do
2
2
  desc "Creates the test database for MySQL."
3
3
  task :mysql do
4
4
  load 'test/db/mysql.rb' rescue nil
5
- IO.popen("mysql -u root", "w") do |io|
6
- io.puts <<-SQL
5
+ t = Tempfile.new("mysql")
6
+ t.puts <<-SQL
7
7
  DROP DATABASE IF EXISTS `#{MYSQL_CONFIG[:database]}`;
8
8
  CREATE DATABASE `#{MYSQL_CONFIG[:database]}` DEFAULT CHARACTER SET `utf8`;
9
9
  GRANT ALL PRIVILEGES ON `#{MYSQL_CONFIG[:database]}`.* TO #{MYSQL_CONFIG[:username]}@localhost;
10
+ GRANT ALL PRIVILEGES ON `test\_%`.* TO #{MYSQL_CONFIG[:username]}@localhost;
10
11
  SET PASSWORD FOR #{MYSQL_CONFIG[:username]}@localhost = PASSWORD('#{MYSQL_CONFIG[:password]}');
11
12
  SQL
13
+ t.close
14
+ at_exit { t.unlink }
15
+ password = ""
16
+ if ENV['DATABASE_YML']
17
+ require 'yaml'
18
+ password = YAML.load(File.new(ENV['DATABASE_YML']))["production"]["password"]
19
+ password_arg = " --password=#{password}"
12
20
  end
21
+ sh "cat #{t.path} | mysql -u root#{password_arg}", :verbose => false # so password is not echoed
13
22
  end
14
23
 
15
24
  desc "Creates the test database for PostgreSQL."
16
25
  task :postgres do
26
+ fail unless have_postgres?
17
27
  load 'test/db/postgres.rb' rescue nil
18
- IO.popen("psql", "w") do |io|
19
- io.puts <<-SQL
28
+ t = Tempfile.new("psql")
29
+ t.puts <<-SQL
20
30
  DROP DATABASE IF EXISTS #{POSTGRES_CONFIG[:database]};
21
31
  DROP USER IF EXISTS #{POSTGRES_CONFIG[:username]};
22
32
  CREATE USER #{POSTGRES_CONFIG[:username]} CREATEDB SUPERUSER LOGIN PASSWORD '#{POSTGRES_CONFIG[:password]}';
23
33
  CREATE DATABASE #{POSTGRES_CONFIG[:database]} OWNER #{POSTGRES_CONFIG[:username]};
24
34
  SQL
25
- end
35
+ t.close
36
+ at_exit { t.unlink }
37
+ sh "cat #{t.path} | psql -U postgres"
26
38
  end
27
39
  end
@@ -970,11 +970,13 @@ public class RubyJdbcConnection extends RubyObject {
970
970
  return RubyString.newUnicodeString(runtime, string);
971
971
  }
972
972
 
973
- private static final int TABLE_NAME = 3;
974
973
 
975
974
  protected SQLBlock tableLookupBlock(final Ruby runtime,
976
975
  final String catalog, final String schemapat,
977
976
  final String tablepat, final String[] types, final boolean downCase) {
977
+ final int TABLE_SCHEM = 2;
978
+ final int TABLE_NAME = 3;
979
+ final int TABLE_TYPE = 4;
978
980
  return new SQLBlock() {
979
981
  public Object call(Connection c) throws SQLException {
980
982
  ResultSet rs = null;
@@ -982,7 +984,8 @@ public class RubyJdbcConnection extends RubyObject {
982
984
  DatabaseMetaData metadata = c.getMetaData();
983
985
  String clzName = metadata.getClass().getName().toLowerCase();
984
986
  boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
985
- boolean isDerby = clzName.indexOf("derby") != 1;
987
+ boolean isDerby = clzName.indexOf("derby") != -1;
988
+ boolean isMssql = clzName.indexOf("sqlserver") != -1 || clzName.indexOf("tds") != -1;
986
989
 
987
990
  String realschema = schemapat;
988
991
  String realtablepat = tablepat;
@@ -995,6 +998,7 @@ public class RubyJdbcConnection extends RubyObject {
995
998
  List arr = new ArrayList();
996
999
  while (rs.next()) {
997
1000
  String name;
1001
+ String schema = rs.getString(TABLE_SCHEM) != null ? rs.getString(TABLE_SCHEM).toLowerCase() : null;
998
1002
 
999
1003
  if (downCase) {
1000
1004
  name = rs.getString(TABLE_NAME).toLowerCase();
@@ -1002,9 +1006,15 @@ public class RubyJdbcConnection extends RubyObject {
1002
1006
  name = caseConvertIdentifierForRails(metadata, rs.getString(TABLE_NAME));
1003
1007
  }
1004
1008
  // Handle stupid Oracle 10g RecycleBin feature
1005
- if (!isOracle || !name.startsWith("bin$")) {
1006
- arr.add(RubyString.newUnicodeString(runtime, name));
1009
+ if (isOracle && name.startsWith("bin$")) {
1010
+ continue;
1007
1011
  }
1012
+ // Under mssql, don't return system tables/views unless they're explicitly asked for.
1013
+ if (isMssql && realschema==null &&
1014
+ ("sys".equals(schema) || "information_schema".equals(schema))) {
1015
+ continue;
1016
+ }
1017
+ arr.add(RubyString.newUnicodeString(runtime, name));
1008
1018
  }
1009
1019
  return runtime.newArray(arr);
1010
1020
  } finally {
@@ -27,6 +27,10 @@ package arjdbc.postgresql;
27
27
 
28
28
  import arjdbc.jdbc.RubyJdbcConnection;
29
29
 
30
+ import java.sql.ResultSet;
31
+ import java.sql.SQLException;
32
+ import java.sql.Types;
33
+
30
34
  import org.jruby.Ruby;
31
35
  import org.jruby.RubyClass;
32
36
  import org.jruby.runtime.ObjectAllocator;
@@ -49,6 +53,27 @@ public class PostgresqlRubyJdbcConnection extends RubyJdbcConnection {
49
53
  return clazz;
50
54
  }
51
55
 
56
+ /**
57
+ * Override jdbcToRuby type conversions to handle infinite timestamps.
58
+ * Handing timestamp off to ruby as string so adapter can perform type
59
+ * conversion to timestamp
60
+ */
61
+ @Override
62
+ protected IRubyObject jdbcToRuby(Ruby runtime, int column, int type,
63
+ ResultSet resultSet)
64
+ throws SQLException {
65
+ if(type == Types.TIMESTAMP) {
66
+ try {
67
+ return stringToRuby(runtime, resultSet,
68
+ resultSet.getString(column));
69
+ } catch(java.io.IOException ioe) {
70
+ SQLException ex = new SQLException(ioe.getMessage());
71
+ throw (SQLException) ex.initCause(ioe);
72
+ }
73
+ }
74
+ return super.jdbcToRuby(runtime, column, type, resultSet);
75
+ }
76
+
52
77
  private static ObjectAllocator POSTGRESQL_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
53
78
  public IRubyObject allocate(Ruby runtime, RubyClass klass) {
54
79
  return new PostgresqlRubyJdbcConnection(runtime, klass);
@@ -1,11 +1,12 @@
1
1
  require 'jdbc/mysql'
2
2
 
3
3
  config = {
4
- :username => 'blog',
5
- :password => '',
4
+ # see db/mysql.rb
5
+ :username => 'arjdbc',
6
+ :password => 'arjdbc',
6
7
  :adapter => 'jdbc',
7
8
  :driver => 'com.mysql.jdbc.Driver',
8
- :url => 'jdbc:mysql://localhost:3306/weblog_development'
9
+ :url => 'jdbc:mysql://localhost:3306/arjdbc_test'
9
10
  }
10
11
 
11
12
  ActiveRecord::Base.establish_connection(config)
@@ -0,0 +1,8 @@
1
+ #! /usr/bin/env jruby
2
+
3
+ require 'jdbc_common'
4
+ require 'db/db2'
5
+
6
+ class DB2ResetColumnInformationTest < Test::Unit::TestCase
7
+ include ResetColumnInformationTestMethods
8
+ end
@@ -0,0 +1,8 @@
1
+ #! /usr/bin/env jruby
2
+
3
+ require 'jdbc_common'
4
+ require 'db/derby'
5
+
6
+ class DerbyResetColumnInformationTest < Test::Unit::TestCase
7
+ include ResetColumnInformationTestMethods
8
+ end
@@ -0,0 +1,9 @@
1
+ #! /usr/bin/env jruby
2
+
3
+ require 'jdbc_common'
4
+ require 'db/derby'
5
+
6
+ class DerbyRowLockingTest < Test::Unit::TestCase
7
+ include MigrationSetup
8
+ include RowLockingTestMethods
9
+ end
@@ -96,4 +96,44 @@ class DerbySimpleTest < Test::Unit::TestCase
96
96
  end
97
97
  end
98
98
  end
99
+
100
+ def test_data_types
101
+ # From test/models/data_types.rb, with the modifications as noted in the comments.
102
+ expected_types = [
103
+ ["id", :integer, { }],
104
+ ["sample_timestamp", :datetime, { }], # :timestamp is just an alias for :datetime in Derby
105
+ ["sample_datetime", :datetime, { }],
106
+ ["sample_date", :date, { }],
107
+ ["sample_time", :time, { }],
108
+ ["sample_decimal", :integer, { :precision => 15, :scale => 0 }], # it's an :integer because the :scale is 0 (...right?)
109
+ ["sample_small_decimal", :decimal, { :precision => 3, :scale => 2 }],
110
+ ["sample_default_decimal", :integer, { }], # decimal and integer are the same type in Derby
111
+ ["sample_float", :float, { }],
112
+ ["sample_binary", :binary, { }],
113
+ ["sample_boolean", :boolean, { }],
114
+ ["sample_string", :string, { :default => '' }],
115
+ ["sample_integer", :integer, { }], # don't care about the limit
116
+ ["sample_integer_with_limit_2", :integer, { }], # don't care about the limit
117
+ ["sample_integer_with_limit_8", :integer, { }], # don't care about the limit
118
+ ["sample_integer_no_limit", :integer, { }],
119
+ ["sample_integer_neg_default", :integer, { :default => -1 }],
120
+ ["sample_text", :text, { }],
121
+ ].sort{|a,b| a[0] <=> b[0]}
122
+
123
+ column_names = (expected_types.map{|et| et[0]} + DbType.column_names).sort.uniq
124
+ result = []
125
+ column_names.each do |column_name|
126
+ et = expected_types.detect{|t| t[0] == column_name }
127
+ col = DbType.columns_hash[column_name]
128
+ if col
129
+ attrs = et && Hash[et[2].keys.map{|k| [k, col.send(k)]}]
130
+ result << [col.name, col.type, attrs]
131
+ else
132
+ result << [column_name, nil, nil]
133
+ end
134
+ end
135
+ result.sort!{|a,b| a[0] <=> b[0]}
136
+
137
+ assert_equal expected_types, result
138
+ end
99
139
  end
@@ -15,8 +15,8 @@ class H2SchemaTest < Test::Unit::TestCase
15
15
  @connection.execute("set schema s2");
16
16
  CreateUsers.up
17
17
  @connection.execute("set schema public");
18
- Entry.set_table_name 's1.entries'
19
- User.set_table_name 's2.users'
18
+ Entry.table_name = 's1.entries'
19
+ User.table_name = 's2.users'
20
20
  user = User.create! :login => "something"
21
21
  Entry.create! :title => "title", :content => "content", :rating => 123.45, :user => user
22
22
  end
@@ -2,16 +2,29 @@ module Kernel
2
2
  def find_executable?(name)
3
3
  ENV['PATH'].split(File::PATH_SEPARATOR).detect {|p| File.executable?(File.join(p, name))}
4
4
  end
5
+
6
+ def have_postgres?
7
+ if find_executable?("psql")
8
+ if `psql -c '\\l' -U postgres 2>&1` && $?.exitstatus == 0
9
+ true
10
+ else
11
+ warn "No \"postgres\" role? You might need to execute `createuser postgres -drs' first."
12
+ false
13
+ end
14
+ end
15
+ end
5
16
  end
6
17
 
7
18
  # assert_queries and SQLCounter taken from rails active_record tests
8
19
  require 'test/unit'
9
20
  class Test::Unit::TestCase
10
- def assert_queries(num = 1)
21
+ def assert_queries(num = 1, matching = nil)
11
22
  ActiveRecord::SQLCounter.log = []
12
23
  yield
13
24
  ensure
14
- assert_equal num, ActiveRecord::SQLCounter.log.size, "#{ActiveRecord::SQLCounter.log.size} instead of #{num} queries were executed.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
25
+ queries = nil
26
+ ActiveRecord::SQLCounter.log.tap {|log| queries = (matching ? log.select {|s| s =~ matching } : log) }
27
+ assert_equal num, queries.size, "#{queries.size} instead of #{num} queries were executed.#{queries.size == 0 ? '' : "\nQueries:\n#{queries.join("\n")}"}"
15
28
  end
16
29
  end
17
30
 
@@ -16,6 +16,7 @@ require 'models/thing'
16
16
  require 'simple'
17
17
  require 'has_many_through'
18
18
  require 'helper'
19
+ require 'row_locking'
19
20
  require 'test/unit'
20
21
 
21
22
  # Comment/uncomment to enable logging to be loaded for any of the database adapters
@@ -6,32 +6,28 @@ begin
6
6
 
7
7
  class JndiConnectionPoolCallbacksTest < Test::Unit::TestCase
8
8
  def setup
9
- @connection = mock "JdbcConnection"
10
- @connection.stubs(:jndi_connection?).returns(true)
11
- @connection.stubs(:adapter=)
12
- @logger = mock "logger"
9
+ @logger = stub_everything "logger"
13
10
  @config = JNDI_CONFIG
11
+ @connection = ActiveRecord::ConnectionAdapters::JdbcConnection.new @config
14
12
  Entry.connection_pool.disconnect!
15
13
  assert !Entry.connection_pool.connected?
16
14
  class << Entry.connection_pool; public :instance_variable_set; end
17
15
  end
18
16
 
19
17
  def teardown
20
- @connection.stubs(:disconnect!)
21
18
  Entry.connection_pool.disconnect!
22
19
  end
23
20
 
24
21
  def test_should_call_hooks_on_checkout_and_checkin
25
- @connection.stubs(:active?).returns(true)
26
- @connection.expects(:disconnect!)
27
22
  @adapter = ActiveRecord::ConnectionAdapters::JdbcAdapter.new @connection, @logger, @config
28
23
  Entry.connection_pool.instance_variable_set "@connections", [@adapter]
24
+ assert !@connection.active?
29
25
 
30
- @connection.expects(:reconnect!)
31
26
  Entry.connection_pool.checkout
27
+ assert @connection.active?
32
28
 
33
- @connection.expects(:disconnect!)
34
29
  Entry.connection_pool.checkin @adapter
30
+ assert !@connection.active?
35
31
  end
36
32
  end
37
33
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env jruby
2
2
 
3
- if ARGV.length < 2
3
+ if ARGV.length < 2
4
4
  $stderr.puts "syntax: #{__FILE__} [filename] [configuration-name]"
5
5
  $stderr.puts " where filename points to a YAML database configuration file"
6
6
  $stderr.puts " and the configuration name is in this file"
@@ -21,7 +21,7 @@ ActiveRecord::Base.establish_connection(cfg)
21
21
  ActiveRecord::Schema.define do
22
22
  drop_table :authors rescue nil
23
23
  drop_table :author rescue nil
24
-
24
+
25
25
  create_table :author, :force => true do |t|
26
26
  t.column :name, :string, :null => false
27
27
  end
@@ -42,13 +42,13 @@ ActiveRecord::Schema.define do
42
42
  change_column_default :author, :female, false if /db2|derby|mssql|firebird/ !~ ARGV[1]
43
43
  remove_column :author, :died if /db2|derby/ !~ ARGV[1]
44
44
  rename_column :author, :wakeup_time, :waking_time if /db2|derby|mimer/ !~ ARGV[1]
45
-
45
+
46
46
  add_index :author, :name, :unique if /db2/ !~ ARGV[1]
47
47
  add_index :author, [:age,:female], :name => :is_age_female if /db2/ !~ ARGV[1]
48
-
48
+
49
49
  remove_index :author, :name if /db2/ !~ ARGV[1]
50
50
  remove_index :author, :name => :is_age_female if /db2/ !~ ARGV[1]
51
-
51
+
52
52
  rename_table :author, :authors if /db2|firebird|mimer/ !~ ARGV[1]
53
53
 
54
54
 
@@ -73,7 +73,7 @@ ActiveRecord::Schema.define do
73
73
  end
74
74
 
75
75
  class Author < ActiveRecord::Base;
76
- set_table_name "author" if /db2|firebird|mimer/ =~ ARGV[1]
76
+ self.table_name = "author" if /db2|firebird|mimer/ =~ ARGV[1]
77
77
  end
78
78
 
79
79
  class Order < ActiveRecord::Base
@@ -83,7 +83,7 @@ end
83
83
  class Product < ActiveRecord::Base
84
84
  has_many :orders, :through => :line_items
85
85
  has_many :line_items
86
-
86
+
87
87
  def self.find_products_for_sale
88
88
  find(:all, :order => "title")
89
89
  end
@@ -95,51 +95,51 @@ class LineItem < ActiveRecord::Base
95
95
  end
96
96
 
97
97
  Product.create(:title => 'Pragmatic Project Automation',
98
- :description =>
98
+ :description =>
99
99
  %{<p>
100
- <em>Pragmatic Project Automation</em> shows you how to improve the
101
- consistency and repeatability of your project's procedures using
100
+ <em>Pragmatic Project Automation</em> shows you how to improve the
101
+ consistency and repeatability of your project's procedures using
102
102
  automation to reduce risk and errors.
103
103
  </p>
104
104
  <p>
105
- Simply put, we're going to put this thing called a computer to work
106
- for you doing the mundane (but important) project stuff. That means
107
- you'll have more time and energy to do the really
105
+ Simply put, we're going to put this thing called a computer to work
106
+ for you doing the mundane (but important) project stuff. That means
107
+ you'll have more time and energy to do the really
108
108
  exciting---and difficult---stuff, like writing quality code.
109
109
  </p>},
110
- :image_url => '/images/auto.jpg',
110
+ :image_url => '/images/auto.jpg',
111
111
  :price => 29.95)
112
112
 
113
113
 
114
114
  Product.create(:title => 'Pragmatic Version Control',
115
115
  :description =>
116
116
  %{<p>
117
- This book is a recipe-based approach to using Subversion that will
118
- get you up and
119
- running quickly---and correctly. All projects need version control:
120
- it's a foundational piece of any project's infrastructure. Yet half
121
- of all project teams in the U.S. don't use any version control at all.
117
+ This book is a recipe-based approach to using Subversion that will
118
+ get you up and
119
+ running quickly---and correctly. All projects need version control:
120
+ it's a foundational piece of any project's infrastructure. Yet half
121
+ of all project teams in the U.S. don't use any version control at all.
122
122
  Many others don't use it well, and end up experiencing time-consuming problems.
123
123
  </p>},
124
124
  :image_url => '/images/svn.jpg',
125
125
  :price => 28.50)
126
-
126
+
127
127
  # . . .
128
128
 
129
129
 
130
130
  Product.create(:title => 'Pragmatic Unit Testing (C#)',
131
- :description =>
131
+ :description =>
132
132
  %{<p>
133
- Pragmatic programmers use feedback to drive their development and
134
- personal processes. The most valuable feedback you can get while
133
+ Pragmatic programmers use feedback to drive their development and
134
+ personal processes. The most valuable feedback you can get while
135
135
  coding comes from unit testing.
136
136
  </p>
137
137
  <p>
138
- Without good tests in place, coding can become a frustrating game of
139
- "whack-a-mole." That's the carnival game where the player strikes at a
140
- mechanical mole; it retreats and another mole pops up on the opposite side
141
- of the field. The moles pop up and down so fast that you end up flailing
142
- your mallet helplessly as the moles continue to pop up where you least
138
+ Without good tests in place, coding can become a frustrating game of
139
+ "whack-a-mole." That's the carnival game where the player strikes at a
140
+ mechanical mole; it retreats and another mole pops up on the opposite side
141
+ of the field. The moles pop up and down so fast that you end up flailing
142
+ your mallet helplessly as the moles continue to pop up where you least
143
143
  expect them.
144
144
  </p>},
145
145
  :image_url => '/images/utc.jpg',
@@ -148,7 +148,7 @@ end
148
148
 
149
149
 
150
150
 
151
- 1.times do
151
+ 1.times do
152
152
  $stderr.print '.'
153
153
  Author.destroy_all
154
154
  Author.create(:name => "Arne Svensson", :age => 30)
@@ -181,11 +181,11 @@ end
181
181
  puts "order: #{order.line_items.inspect}, with id: #{order.id} and name: #{order.name}"
182
182
  end
183
183
 
184
- ActiveRecord::Schema.define do
184
+ ActiveRecord::Schema.define do
185
185
  drop_table :line_items
186
186
  drop_table :orders
187
187
  drop_table :products
188
188
 
189
-
189
+
190
190
  drop_table((/db2|firebird|mimer/=~ARGV[1]? :author : :authors ))
191
191
  end