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.
- data/.travis.yml +3 -0
- data/Gemfile.lock +13 -15
- data/History.txt +19 -0
- data/README.rdoc +2 -0
- data/Rakefile +2 -1
- data/lib/arel/visitors/derby.rb +9 -2
- data/lib/arel/visitors/sql_server.rb +2 -0
- data/lib/arjdbc/db2/adapter.rb +3 -1
- data/lib/arjdbc/derby/adapter.rb +10 -3
- data/lib/arjdbc/jdbc/adapter.rb +5 -2
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/base_ext.rb +15 -0
- data/lib/arjdbc/jdbc/connection.rb +5 -1
- data/lib/arjdbc/jdbc/missing_functionality_helper.rb +1 -0
- data/lib/arjdbc/mssql/adapter.rb +31 -28
- data/lib/arjdbc/mssql/lock_helpers.rb +72 -0
- data/lib/arjdbc/mysql/adapter.rb +110 -45
- data/lib/arjdbc/oracle/adapter.rb +7 -0
- data/lib/arjdbc/postgresql/adapter.rb +327 -153
- data/lib/arjdbc/sqlite3/adapter.rb +9 -4
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/db.rake +17 -5
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +14 -4
- data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +25 -0
- data/test/db/jdbc.rb +4 -3
- data/test/db2_reset_column_information_test.rb +8 -0
- data/test/derby_reset_column_information_test.rb +8 -0
- data/test/derby_row_locking_test.rb +9 -0
- data/test/derby_simple_test.rb +40 -0
- data/test/h2_simple_test.rb +2 -2
- data/test/helper.rb +15 -2
- data/test/jdbc_common.rb +1 -0
- data/test/jndi_callbacks_test.rb +5 -9
- data/test/manualTestDatabase.rb +31 -31
- data/test/models/validates_uniqueness_of_string.rb +1 -1
- data/test/mssql_ignore_system_views_test.rb +27 -0
- data/test/mssql_null_test.rb +14 -0
- data/test/mssql_reset_column_information_test.rb +8 -0
- data/test/mssql_row_locking_sql_test.rb +159 -0
- data/test/mssql_row_locking_test.rb +9 -0
- data/test/mysql_reset_column_information_test.rb +8 -0
- data/test/mysql_simple_test.rb +69 -5
- data/test/oracle_reset_column_information_test.rb +8 -0
- data/test/oracle_specific_test.rb +1 -1
- data/test/postgres_nonseq_pkey_test.rb +1 -1
- data/test/postgres_reset_column_information_test.rb +8 -0
- data/test/postgres_simple_test.rb +72 -1
- data/test/row_locking.rb +90 -0
- data/test/simple.rb +82 -2
- data/test/sqlite3_reset_column_information_test.rb +8 -0
- data/test/sqlite3_simple_test.rb +47 -0
- data/test/sybase_reset_column_information_test.rb +8 -0
- 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
|
-
|
212
|
-
|
213
|
-
|
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
|
data/lib/arjdbc/version.rb
CHANGED
data/rakelib/db.rake
CHANGED
@@ -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
|
-
|
6
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
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 (
|
1006
|
-
|
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);
|
data/test/db/jdbc.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'jdbc/mysql'
|
2
2
|
|
3
3
|
config = {
|
4
|
-
|
5
|
-
:
|
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/
|
9
|
+
:url => 'jdbc:mysql://localhost:3306/arjdbc_test'
|
9
10
|
}
|
10
11
|
|
11
12
|
ActiveRecord::Base.establish_connection(config)
|
data/test/derby_simple_test.rb
CHANGED
@@ -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
|
data/test/h2_simple_test.rb
CHANGED
@@ -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.
|
19
|
-
User.
|
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
|
data/test/helper.rb
CHANGED
@@ -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
|
-
|
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
|
|
data/test/jdbc_common.rb
CHANGED
data/test/jndi_callbacks_test.rb
CHANGED
@@ -6,32 +6,28 @@ begin
|
|
6
6
|
|
7
7
|
class JndiConnectionPoolCallbacksTest < Test::Unit::TestCase
|
8
8
|
def setup
|
9
|
-
@
|
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
|
|
data/test/manualTestDatabase.rb
CHANGED
@@ -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
|
-
|
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
|