activerecord-jdbc-adapter 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|