activerecord_authorails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. data/CHANGELOG +3043 -0
  2. data/README +360 -0
  3. data/RUNNING_UNIT_TESTS +64 -0
  4. data/Rakefile +226 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/associations.rb +87 -0
  7. data/examples/shared_setup.rb +15 -0
  8. data/examples/validation.rb +85 -0
  9. data/install.rb +30 -0
  10. data/lib/active_record.rb +85 -0
  11. data/lib/active_record/acts/list.rb +244 -0
  12. data/lib/active_record/acts/nested_set.rb +211 -0
  13. data/lib/active_record/acts/tree.rb +89 -0
  14. data/lib/active_record/aggregations.rb +191 -0
  15. data/lib/active_record/associations.rb +1637 -0
  16. data/lib/active_record/associations/association_collection.rb +190 -0
  17. data/lib/active_record/associations/association_proxy.rb +158 -0
  18. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  19. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  20. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +169 -0
  21. data/lib/active_record/associations/has_many_association.rb +210 -0
  22. data/lib/active_record/associations/has_many_through_association.rb +247 -0
  23. data/lib/active_record/associations/has_one_association.rb +80 -0
  24. data/lib/active_record/attribute_methods.rb +75 -0
  25. data/lib/active_record/base.rb +2164 -0
  26. data/lib/active_record/calculations.rb +270 -0
  27. data/lib/active_record/callbacks.rb +367 -0
  28. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +279 -0
  29. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +58 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +343 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +161 -0
  34. data/lib/active_record/connection_adapters/db2_adapter.rb +228 -0
  35. data/lib/active_record/connection_adapters/firebird_adapter.rb +728 -0
  36. data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
  37. data/lib/active_record/connection_adapters/mysql_adapter.rb +414 -0
  38. data/lib/active_record/connection_adapters/openbase_adapter.rb +350 -0
  39. data/lib/active_record/connection_adapters/oracle_adapter.rb +689 -0
  40. data/lib/active_record/connection_adapters/postgresql_adapter.rb +584 -0
  41. data/lib/active_record/connection_adapters/sqlite_adapter.rb +407 -0
  42. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +591 -0
  43. data/lib/active_record/connection_adapters/sybase_adapter.rb +662 -0
  44. data/lib/active_record/deprecated_associations.rb +104 -0
  45. data/lib/active_record/deprecated_finders.rb +44 -0
  46. data/lib/active_record/fixtures.rb +628 -0
  47. data/lib/active_record/locking/optimistic.rb +106 -0
  48. data/lib/active_record/locking/pessimistic.rb +77 -0
  49. data/lib/active_record/migration.rb +394 -0
  50. data/lib/active_record/observer.rb +178 -0
  51. data/lib/active_record/query_cache.rb +64 -0
  52. data/lib/active_record/reflection.rb +222 -0
  53. data/lib/active_record/schema.rb +58 -0
  54. data/lib/active_record/schema_dumper.rb +149 -0
  55. data/lib/active_record/timestamp.rb +51 -0
  56. data/lib/active_record/transactions.rb +136 -0
  57. data/lib/active_record/validations.rb +843 -0
  58. data/lib/active_record/vendor/db2.rb +362 -0
  59. data/lib/active_record/vendor/mysql.rb +1214 -0
  60. data/lib/active_record/vendor/simple.rb +693 -0
  61. data/lib/active_record/version.rb +9 -0
  62. data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
  63. data/lib/active_record/wrappings.rb +58 -0
  64. data/lib/active_record/xml_serialization.rb +308 -0
  65. data/test/aaa_create_tables_test.rb +59 -0
  66. data/test/abstract_unit.rb +77 -0
  67. data/test/active_schema_test_mysql.rb +31 -0
  68. data/test/adapter_test.rb +87 -0
  69. data/test/adapter_test_sqlserver.rb +81 -0
  70. data/test/aggregations_test.rb +95 -0
  71. data/test/all.sh +8 -0
  72. data/test/ar_schema_test.rb +33 -0
  73. data/test/association_inheritance_reload.rb +14 -0
  74. data/test/associations/callbacks_test.rb +126 -0
  75. data/test/associations/cascaded_eager_loading_test.rb +138 -0
  76. data/test/associations/eager_test.rb +393 -0
  77. data/test/associations/extension_test.rb +42 -0
  78. data/test/associations/join_model_test.rb +497 -0
  79. data/test/associations_test.rb +1809 -0
  80. data/test/attribute_methods_test.rb +49 -0
  81. data/test/base_test.rb +1586 -0
  82. data/test/binary_test.rb +37 -0
  83. data/test/calculations_test.rb +219 -0
  84. data/test/callbacks_test.rb +377 -0
  85. data/test/class_inheritable_attributes_test.rb +32 -0
  86. data/test/column_alias_test.rb +17 -0
  87. data/test/connection_test_firebird.rb +8 -0
  88. data/test/connections/native_db2/connection.rb +25 -0
  89. data/test/connections/native_firebird/connection.rb +26 -0
  90. data/test/connections/native_frontbase/connection.rb +27 -0
  91. data/test/connections/native_mysql/connection.rb +24 -0
  92. data/test/connections/native_openbase/connection.rb +21 -0
  93. data/test/connections/native_oracle/connection.rb +27 -0
  94. data/test/connections/native_postgresql/connection.rb +23 -0
  95. data/test/connections/native_sqlite/connection.rb +34 -0
  96. data/test/connections/native_sqlite3/connection.rb +34 -0
  97. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  98. data/test/connections/native_sqlserver/connection.rb +23 -0
  99. data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
  100. data/test/connections/native_sybase/connection.rb +23 -0
  101. data/test/copy_table_sqlite.rb +64 -0
  102. data/test/datatype_test_postgresql.rb +52 -0
  103. data/test/default_test_firebird.rb +16 -0
  104. data/test/defaults_test.rb +60 -0
  105. data/test/deprecated_associations_test.rb +396 -0
  106. data/test/deprecated_finder_test.rb +151 -0
  107. data/test/empty_date_time_test.rb +25 -0
  108. data/test/finder_test.rb +504 -0
  109. data/test/fixtures/accounts.yml +28 -0
  110. data/test/fixtures/author.rb +99 -0
  111. data/test/fixtures/author_favorites.yml +4 -0
  112. data/test/fixtures/authors.yml +7 -0
  113. data/test/fixtures/auto_id.rb +4 -0
  114. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  115. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  116. data/test/fixtures/bad_fixtures/blank_line +3 -0
  117. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  118. data/test/fixtures/bad_fixtures/missing_value +1 -0
  119. data/test/fixtures/binary.rb +2 -0
  120. data/test/fixtures/categories.yml +14 -0
  121. data/test/fixtures/categories/special_categories.yml +9 -0
  122. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  123. data/test/fixtures/categories_ordered.yml +7 -0
  124. data/test/fixtures/categories_posts.yml +23 -0
  125. data/test/fixtures/categorization.rb +5 -0
  126. data/test/fixtures/categorizations.yml +17 -0
  127. data/test/fixtures/category.rb +20 -0
  128. data/test/fixtures/column_name.rb +3 -0
  129. data/test/fixtures/comment.rb +23 -0
  130. data/test/fixtures/comments.yml +59 -0
  131. data/test/fixtures/companies.yml +55 -0
  132. data/test/fixtures/company.rb +107 -0
  133. data/test/fixtures/company_in_module.rb +59 -0
  134. data/test/fixtures/computer.rb +3 -0
  135. data/test/fixtures/computers.yml +4 -0
  136. data/test/fixtures/course.rb +3 -0
  137. data/test/fixtures/courses.yml +7 -0
  138. data/test/fixtures/customer.rb +55 -0
  139. data/test/fixtures/customers.yml +17 -0
  140. data/test/fixtures/db_definitions/db2.drop.sql +32 -0
  141. data/test/fixtures/db_definitions/db2.sql +231 -0
  142. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  143. data/test/fixtures/db_definitions/db22.sql +5 -0
  144. data/test/fixtures/db_definitions/firebird.drop.sql +63 -0
  145. data/test/fixtures/db_definitions/firebird.sql +304 -0
  146. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  148. data/test/fixtures/db_definitions/frontbase.drop.sql +32 -0
  149. data/test/fixtures/db_definitions/frontbase.sql +268 -0
  150. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  151. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  152. data/test/fixtures/db_definitions/mysql.drop.sql +32 -0
  153. data/test/fixtures/db_definitions/mysql.sql +234 -0
  154. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  156. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  157. data/test/fixtures/db_definitions/openbase.sql +302 -0
  158. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  159. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  160. data/test/fixtures/db_definitions/oracle.drop.sql +65 -0
  161. data/test/fixtures/db_definitions/oracle.sql +325 -0
  162. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  163. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  164. data/test/fixtures/db_definitions/postgresql.drop.sql +37 -0
  165. data/test/fixtures/db_definitions/postgresql.sql +263 -0
  166. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  167. data/test/fixtures/db_definitions/postgresql2.sql +5 -0
  168. data/test/fixtures/db_definitions/schema.rb +60 -0
  169. data/test/fixtures/db_definitions/sqlite.drop.sql +32 -0
  170. data/test/fixtures/db_definitions/sqlite.sql +215 -0
  171. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  172. data/test/fixtures/db_definitions/sqlite2.sql +5 -0
  173. data/test/fixtures/db_definitions/sqlserver.drop.sql +34 -0
  174. data/test/fixtures/db_definitions/sqlserver.sql +243 -0
  175. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  176. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  177. data/test/fixtures/db_definitions/sybase.drop.sql +34 -0
  178. data/test/fixtures/db_definitions/sybase.sql +218 -0
  179. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  180. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  181. data/test/fixtures/default.rb +2 -0
  182. data/test/fixtures/developer.rb +52 -0
  183. data/test/fixtures/developers.yml +21 -0
  184. data/test/fixtures/developers_projects.yml +17 -0
  185. data/test/fixtures/developers_projects/david_action_controller +3 -0
  186. data/test/fixtures/developers_projects/david_active_record +3 -0
  187. data/test/fixtures/developers_projects/jamis_active_record +2 -0
  188. data/test/fixtures/edge.rb +5 -0
  189. data/test/fixtures/edges.yml +6 -0
  190. data/test/fixtures/entrant.rb +3 -0
  191. data/test/fixtures/entrants.yml +14 -0
  192. data/test/fixtures/fk_test_has_fk.yml +3 -0
  193. data/test/fixtures/fk_test_has_pk.yml +2 -0
  194. data/test/fixtures/flowers.jpg +0 -0
  195. data/test/fixtures/funny_jokes.yml +10 -0
  196. data/test/fixtures/joke.rb +6 -0
  197. data/test/fixtures/keyboard.rb +3 -0
  198. data/test/fixtures/legacy_thing.rb +3 -0
  199. data/test/fixtures/legacy_things.yml +3 -0
  200. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  201. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  202. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  203. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  204. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  205. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  206. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  207. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  208. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  209. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  210. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  211. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  212. data/test/fixtures/mixed_case_monkey.rb +3 -0
  213. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  214. data/test/fixtures/mixin.rb +63 -0
  215. data/test/fixtures/mixins.yml +127 -0
  216. data/test/fixtures/movie.rb +5 -0
  217. data/test/fixtures/movies.yml +7 -0
  218. data/test/fixtures/naked/csv/accounts.csv +1 -0
  219. data/test/fixtures/naked/yml/accounts.yml +1 -0
  220. data/test/fixtures/naked/yml/companies.yml +1 -0
  221. data/test/fixtures/naked/yml/courses.yml +1 -0
  222. data/test/fixtures/order.rb +4 -0
  223. data/test/fixtures/people.yml +3 -0
  224. data/test/fixtures/person.rb +4 -0
  225. data/test/fixtures/post.rb +58 -0
  226. data/test/fixtures/posts.yml +48 -0
  227. data/test/fixtures/project.rb +27 -0
  228. data/test/fixtures/projects.yml +7 -0
  229. data/test/fixtures/reader.rb +4 -0
  230. data/test/fixtures/readers.yml +4 -0
  231. data/test/fixtures/reply.rb +37 -0
  232. data/test/fixtures/subject.rb +4 -0
  233. data/test/fixtures/subscriber.rb +6 -0
  234. data/test/fixtures/subscribers/first +2 -0
  235. data/test/fixtures/subscribers/second +2 -0
  236. data/test/fixtures/tag.rb +7 -0
  237. data/test/fixtures/tagging.rb +6 -0
  238. data/test/fixtures/taggings.yml +18 -0
  239. data/test/fixtures/tags.yml +7 -0
  240. data/test/fixtures/task.rb +3 -0
  241. data/test/fixtures/tasks.yml +7 -0
  242. data/test/fixtures/topic.rb +25 -0
  243. data/test/fixtures/topics.yml +22 -0
  244. data/test/fixtures/vertex.rb +9 -0
  245. data/test/fixtures/vertices.yml +4 -0
  246. data/test/fixtures_test.rb +401 -0
  247. data/test/inheritance_test.rb +205 -0
  248. data/test/lifecycle_test.rb +137 -0
  249. data/test/locking_test.rb +190 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +768 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_nested_set_test.rb +196 -0
  254. data/test/mixin_test.rb +550 -0
  255. data/test/modules_test.rb +34 -0
  256. data/test/multiple_db_test.rb +60 -0
  257. data/test/pk_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +159 -0
  260. data/test/schema_authorization_test_postgresql.rb +75 -0
  261. data/test/schema_dumper_test.rb +96 -0
  262. data/test/schema_test_postgresql.rb +64 -0
  263. data/test/synonym_test_oracle.rb +17 -0
  264. data/test/table_name_test_sqlserver.rb +23 -0
  265. data/test/threaded_connections_test.rb +48 -0
  266. data/test/transactions_test.rb +230 -0
  267. data/test/unconnected_test.rb +32 -0
  268. data/test/validations_test.rb +1097 -0
  269. data/test/xml_serialization_test.rb +125 -0
  270. metadata +365 -0
Binary file
@@ -0,0 +1,87 @@
1
+ require File.dirname(__FILE__) + '/shared_setup'
2
+
3
+ logger = Logger.new(STDOUT)
4
+
5
+ # Database setup ---------------
6
+
7
+ logger.info "\nCreate tables"
8
+
9
+ [ "DROP TABLE companies", "DROP TABLE people", "DROP TABLE people_companies",
10
+ "CREATE TABLE companies (id int(11) auto_increment, client_of int(11), name varchar(255), type varchar(100), PRIMARY KEY (id))",
11
+ "CREATE TABLE people (id int(11) auto_increment, name varchar(100), PRIMARY KEY (id))",
12
+ "CREATE TABLE people_companies (person_id int(11), company_id int(11), PRIMARY KEY (person_id, company_id))",
13
+ ].each { |statement|
14
+ # Tables doesn't necessarily already exist
15
+ begin; ActiveRecord::Base.connection.execute(statement); rescue ActiveRecord::StatementInvalid; end
16
+ }
17
+
18
+
19
+ # Class setup ---------------
20
+
21
+ class Company < ActiveRecord::Base
22
+ has_and_belongs_to_many :people, :class_name => "Person", :join_table => "people_companies", :table_name => "people"
23
+ end
24
+
25
+ class Firm < Company
26
+ has_many :clients, :foreign_key => "client_of"
27
+
28
+ def people_with_all_clients
29
+ clients.inject([]) { |people, client| people + client.people }
30
+ end
31
+ end
32
+
33
+ class Client < Company
34
+ belongs_to :firm, :foreign_key => "client_of"
35
+ end
36
+
37
+ class Person < ActiveRecord::Base
38
+ has_and_belongs_to_many :companies, :join_table => "people_companies"
39
+ def self.table_name() "people" end
40
+ end
41
+
42
+
43
+ # Usage ---------------
44
+
45
+ logger.info "\nCreate fixtures"
46
+
47
+ Firm.new("name" => "Next Angle").save
48
+ Client.new("name" => "37signals", "client_of" => 1).save
49
+ Person.new("name" => "David").save
50
+
51
+
52
+ logger.info "\nUsing Finders"
53
+
54
+ next_angle = Company.find(1)
55
+ next_angle = Firm.find(1)
56
+ next_angle = Company.find_first "name = 'Next Angle'"
57
+ next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first
58
+
59
+ Firm === next_angle
60
+
61
+
62
+ logger.info "\nUsing has_many association"
63
+
64
+ next_angle.has_clients?
65
+ next_angle.clients_count
66
+ all_clients = next_angle.clients
67
+
68
+ thirty_seven_signals = next_angle.find_in_clients(2)
69
+
70
+
71
+ logger.info "\nUsing belongs_to association"
72
+
73
+ thirty_seven_signals.has_firm?
74
+ thirty_seven_signals.firm?(next_angle)
75
+
76
+
77
+ logger.info "\nUsing has_and_belongs_to_many association"
78
+
79
+ david = Person.find(1)
80
+ david.add_companies(thirty_seven_signals, next_angle)
81
+ david.companies.include?(next_angle)
82
+ david.companies_count == 2
83
+
84
+ david.remove_companies(next_angle)
85
+ david.companies_count == 1
86
+
87
+ thirty_seven_signals.people.include?(david)
@@ -0,0 +1,15 @@
1
+ # Be sure to change the mysql_connection details and create a database for the example
2
+
3
+ $: << File.dirname(__FILE__) + '/../lib'
4
+
5
+ require 'active_record'
6
+ require 'logger'; class Logger; def format_message(severity, timestamp, msg, progname) "#{msg}\n" end; end
7
+
8
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
9
+ ActiveRecord::Base.establish_connection(
10
+ :adapter => "mysql",
11
+ :host => "localhost",
12
+ :username => "root",
13
+ :password => "",
14
+ :database => "activerecord_examples"
15
+ )
@@ -0,0 +1,85 @@
1
+ require File.dirname(__FILE__) + '/shared_setup'
2
+
3
+ logger = Logger.new(STDOUT)
4
+
5
+ # Database setup ---------------
6
+
7
+ logger.info "\nCreate tables"
8
+
9
+ [ "DROP TABLE people",
10
+ "CREATE TABLE people (id int(11) auto_increment, name varchar(100), pass varchar(100), email varchar(100), PRIMARY KEY (id))"
11
+ ].each { |statement|
12
+ begin; ActiveRecord::Base.connection.execute(statement); rescue ActiveRecord::StatementInvalid; end # Tables doesn't necessarily already exist
13
+ }
14
+
15
+
16
+ # Class setup ---------------
17
+
18
+ class Person < ActiveRecord::Base
19
+ # Using
20
+ def self.authenticate(name, pass)
21
+ # find_first "name = '#{name}' AND pass = '#{pass}'" would be open to sql-injection (in a web-app scenario)
22
+ find_first [ "name = '%s' AND pass = '%s'", name, pass ]
23
+ end
24
+
25
+ def self.name_exists?(name, id = nil)
26
+ if id.nil?
27
+ condition = [ "name = '%s'", name ]
28
+ else
29
+ # Check if anyone else than the person identified by person_id has that user_name
30
+ condition = [ "name = '%s' AND id <> %d", name, id ]
31
+ end
32
+
33
+ !find_first(condition).nil?
34
+ end
35
+
36
+ def email_address_with_name
37
+ "\"#{name}\" <#{email}>"
38
+ end
39
+
40
+ protected
41
+ def validate
42
+ errors.add_on_empty(%w(name pass email))
43
+ errors.add("email", "must be valid") unless email_address_valid?
44
+ end
45
+
46
+ def validate_on_create
47
+ if attribute_present?("name") && Person.name_exists?(name)
48
+ errors.add("name", "is already taken by another person")
49
+ end
50
+ end
51
+
52
+ def validate_on_update
53
+ if attribute_present?("name") && Person.name_exists?(name, id)
54
+ errors.add("name", "is already taken by another person")
55
+ end
56
+ end
57
+
58
+ private
59
+ def email_address_valid?() email =~ /\w[-.\w]*\@[-\w]+[-.\w]*\.\w+/ end
60
+ end
61
+
62
+ # Usage ---------------
63
+
64
+ logger.info "\nCreate fixtures"
65
+ david = Person.new("name" => "David Heinemeier Hansson", "pass" => "", "email" => "")
66
+ unless david.save
67
+ puts "There was #{david.errors.count} error(s)"
68
+ david.errors.each_full { |error| puts error }
69
+ end
70
+
71
+ david.pass = "something"
72
+ david.email = "invalid_address"
73
+ unless david.save
74
+ puts "There was #{david.errors.count} error(s)"
75
+ puts "It was email with: " + david.errors.on("email")
76
+ end
77
+
78
+ david.email = "david@loudthinking.com"
79
+ if david.save then puts "David finally made it!" end
80
+
81
+
82
+ another_david = Person.new("name" => "David Heinemeier Hansson", "pass" => "xc", "email" => "david@loudthinking")
83
+ unless another_david.save
84
+ puts "Error on name: " + another_david.errors.on("name")
85
+ end
@@ -0,0 +1,30 @@
1
+ require 'rbconfig'
2
+ require 'find'
3
+ require 'ftools'
4
+
5
+ include Config
6
+
7
+ # this was adapted from rdoc's install.rb by ways of Log4r
8
+
9
+ $sitedir = CONFIG["sitelibdir"]
10
+ unless $sitedir
11
+ version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
12
+ $libdir = File.join(CONFIG["libdir"], "ruby", version)
13
+ $sitedir = $:.find {|x| x =~ /site_ruby/ }
14
+ if !$sitedir
15
+ $sitedir = File.join($libdir, "site_ruby")
16
+ elsif $sitedir !~ Regexp.quote(version)
17
+ $sitedir = File.join($sitedir, version)
18
+ end
19
+ end
20
+
21
+ # the acual gruntwork
22
+ Dir.chdir("lib")
23
+
24
+ Find.find("active_record", "active_record.rb") { |f|
25
+ if f[-3..-1] == ".rb"
26
+ File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
27
+ else
28
+ File::makedirs(File.join($sitedir, *f.split(/\//)))
29
+ end
30
+ }
@@ -0,0 +1,85 @@
1
+ #--
2
+ # Copyright (c) 2004-2006 David Heinemeier Hansson
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ $:.unshift(File.dirname(__FILE__)) unless
25
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
26
+
27
+ unless defined?(ActiveSupport)
28
+ begin
29
+ $:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")
30
+ require 'active_support'
31
+ rescue LoadError
32
+ require 'rubygems'
33
+ gem 'activesupport'
34
+ end
35
+ end
36
+
37
+ require 'active_record/base'
38
+ require 'active_record/observer'
39
+ require 'active_record/validations'
40
+ require 'active_record/callbacks'
41
+ require 'active_record/reflection'
42
+ require 'active_record/associations'
43
+ require 'active_record/aggregations'
44
+ require 'active_record/transactions'
45
+ require 'active_record/timestamp'
46
+ require 'active_record/acts/list'
47
+ require 'active_record/acts/tree'
48
+ require 'active_record/acts/nested_set'
49
+ require 'active_record/locking/optimistic'
50
+ require 'active_record/locking/pessimistic'
51
+ require 'active_record/migration'
52
+ require 'active_record/schema'
53
+ require 'active_record/calculations'
54
+ require 'active_record/xml_serialization'
55
+ require 'active_record/attribute_methods'
56
+
57
+ ActiveRecord::Base.class_eval do
58
+ include ActiveRecord::Validations
59
+ include ActiveRecord::Locking::Optimistic
60
+ include ActiveRecord::Locking::Pessimistic
61
+ include ActiveRecord::Callbacks
62
+ include ActiveRecord::Observing
63
+ include ActiveRecord::Timestamp
64
+ include ActiveRecord::Associations
65
+ include ActiveRecord::Aggregations
66
+ include ActiveRecord::Transactions
67
+ include ActiveRecord::Reflection
68
+ include ActiveRecord::Acts::Tree
69
+ include ActiveRecord::Acts::List
70
+ include ActiveRecord::Acts::NestedSet
71
+ include ActiveRecord::Calculations
72
+ include ActiveRecord::XmlSerialization
73
+ include ActiveRecord::AttributeMethods
74
+ end
75
+
76
+ unless defined?(RAILS_CONNECTION_ADAPTERS)
77
+ RAILS_CONNECTION_ADAPTERS = %w( mysql postgresql sqlite firebird sqlserver db2 oracle sybase openbase frontbase )
78
+ end
79
+
80
+ RAILS_CONNECTION_ADAPTERS.each do |adapter|
81
+ require "active_record/connection_adapters/" + adapter + "_adapter"
82
+ end
83
+
84
+ require 'active_record/query_cache'
85
+ require 'active_record/schema_dumper'
@@ -0,0 +1,244 @@
1
+ module ActiveRecord
2
+ module Acts #:nodoc:
3
+ module List #:nodoc:
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ # This act provides the capabilities for sorting and reordering a number of objects in a list.
9
+ # The class that has this specified needs to have a "position" column defined as an integer on
10
+ # the mapped database table.
11
+ #
12
+ # Todo list example:
13
+ #
14
+ # class TodoList < ActiveRecord::Base
15
+ # has_many :todo_items, :order => "position"
16
+ # end
17
+ #
18
+ # class TodoItem < ActiveRecord::Base
19
+ # belongs_to :todo_list
20
+ # acts_as_list :scope => :todo_list
21
+ # end
22
+ #
23
+ # todo_list.first.move_to_bottom
24
+ # todo_list.last.move_higher
25
+ module ClassMethods
26
+ # Configuration options are:
27
+ #
28
+ # * +column+ - specifies the column name to use for keeping the position integer (default: position)
29
+ # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach "_id"
30
+ # (if that hasn't been already) and use that as the foreign key restriction. It's also possible
31
+ # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
32
+ # Example: <tt>acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
33
+ def acts_as_list(options = {})
34
+ configuration = { :column => "position", :scope => "1 = 1" }
35
+ configuration.update(options) if options.is_a?(Hash)
36
+
37
+ configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
38
+
39
+ if configuration[:scope].is_a?(Symbol)
40
+ scope_condition_method = %(
41
+ def scope_condition
42
+ if #{configuration[:scope].to_s}.nil?
43
+ "#{configuration[:scope].to_s} IS NULL"
44
+ else
45
+ "#{configuration[:scope].to_s} = \#{#{configuration[:scope].to_s}}"
46
+ end
47
+ end
48
+ )
49
+ else
50
+ scope_condition_method = "def scope_condition() \"#{configuration[:scope]}\" end"
51
+ end
52
+
53
+ class_eval <<-EOV
54
+ include ActiveRecord::Acts::List::InstanceMethods
55
+
56
+ def acts_as_list_class
57
+ ::#{self.name}
58
+ end
59
+
60
+ def position_column
61
+ '#{configuration[:column]}'
62
+ end
63
+
64
+ #{scope_condition_method}
65
+
66
+ after_destroy :remove_from_list
67
+ before_create :add_to_list_bottom
68
+ EOV
69
+ end
70
+ end
71
+
72
+ # All the methods available to a record that has had <tt>acts_as_list</tt> specified. Each method works
73
+ # by assuming the object to be the item in the list, so <tt>chapter.move_lower</tt> would move that chapter
74
+ # lower in the list of all chapters. Likewise, <tt>chapter.first?</tt> would return true if that chapter is
75
+ # the first in the list of all chapters.
76
+ module InstanceMethods
77
+ def insert_at(position = 1)
78
+ insert_at_position(position)
79
+ end
80
+
81
+ # Swap positions with the next lower item, if one exists.
82
+ def move_lower
83
+ return unless lower_item
84
+
85
+ acts_as_list_class.transaction do
86
+ lower_item.decrement_position
87
+ increment_position
88
+ end
89
+ end
90
+
91
+ # Swap positions with the next higher item, if one exists.
92
+ def move_higher
93
+ return unless higher_item
94
+
95
+ acts_as_list_class.transaction do
96
+ higher_item.increment_position
97
+ decrement_position
98
+ end
99
+ end
100
+
101
+ # Move to the bottom of the list. If the item is already in the list, the items below it have their
102
+ # position adjusted accordingly.
103
+ def move_to_bottom
104
+ return unless in_list?
105
+ acts_as_list_class.transaction do
106
+ decrement_positions_on_lower_items
107
+ assume_bottom_position
108
+ end
109
+ end
110
+
111
+ # Move to the top of the list. If the item is already in the list, the items above it have their
112
+ # position adjusted accordingly.
113
+ def move_to_top
114
+ return unless in_list?
115
+ acts_as_list_class.transaction do
116
+ increment_positions_on_higher_items
117
+ assume_top_position
118
+ end
119
+ end
120
+
121
+ def remove_from_list
122
+ decrement_positions_on_lower_items if in_list?
123
+ end
124
+
125
+ # Increase the position of this item without adjusting the rest of the list.
126
+ def increment_position
127
+ return unless in_list?
128
+ update_attribute position_column, self.send(position_column).to_i + 1
129
+ end
130
+
131
+ # Decrease the position of this item without adjusting the rest of the list.
132
+ def decrement_position
133
+ return unless in_list?
134
+ update_attribute position_column, self.send(position_column).to_i - 1
135
+ end
136
+
137
+ # Return true if this object is the first in the list.
138
+ def first?
139
+ return false unless in_list?
140
+ self.send(position_column) == 1
141
+ end
142
+
143
+ # Return true if this object is the last in the list.
144
+ def last?
145
+ return false unless in_list?
146
+ self.send(position_column) == bottom_position_in_list
147
+ end
148
+
149
+ # Return the next higher item in the list.
150
+ def higher_item
151
+ return nil unless in_list?
152
+ acts_as_list_class.find(:first, :conditions =>
153
+ "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
154
+ )
155
+ end
156
+
157
+ # Return the next lower item in the list.
158
+ def lower_item
159
+ return nil unless in_list?
160
+ acts_as_list_class.find(:first, :conditions =>
161
+ "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
162
+ )
163
+ end
164
+
165
+ def in_list?
166
+ !send(position_column).nil?
167
+ end
168
+
169
+ private
170
+ def add_to_list_top
171
+ increment_positions_on_all_items
172
+ end
173
+
174
+ def add_to_list_bottom
175
+ self[position_column] = bottom_position_in_list.to_i + 1
176
+ end
177
+
178
+ # Overwrite this method to define the scope of the list changes
179
+ def scope_condition() "1" end
180
+
181
+ def bottom_position_in_list(except = nil)
182
+ item = bottom_item(except)
183
+ item ? item.send(position_column) : 0
184
+ end
185
+
186
+ def bottom_item(except = nil)
187
+ conditions = scope_condition
188
+ conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
189
+ acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
190
+ end
191
+
192
+ def assume_bottom_position
193
+ update_attribute(position_column, bottom_position_in_list(self).to_i + 1)
194
+ end
195
+
196
+ def assume_top_position
197
+ update_attribute(position_column, 1)
198
+ end
199
+
200
+ # This has the effect of moving all the higher items up one.
201
+ def decrement_positions_on_higher_items(position)
202
+ acts_as_list_class.update_all(
203
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
204
+ )
205
+ end
206
+
207
+ # This has the effect of moving all the lower items up one.
208
+ def decrement_positions_on_lower_items
209
+ return unless in_list?
210
+ acts_as_list_class.update_all(
211
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
212
+ )
213
+ end
214
+
215
+ # This has the effect of moving all the higher items down one.
216
+ def increment_positions_on_higher_items
217
+ return unless in_list?
218
+ acts_as_list_class.update_all(
219
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
220
+ )
221
+ end
222
+
223
+ # This has the effect of moving all the lower items down one.
224
+ def increment_positions_on_lower_items(position)
225
+ acts_as_list_class.update_all(
226
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
227
+ )
228
+ end
229
+
230
+ def increment_positions_on_all_items
231
+ acts_as_list_class.update_all(
232
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
233
+ )
234
+ end
235
+
236
+ def insert_at_position(position)
237
+ remove_from_list
238
+ increment_positions_on_lower_items(position)
239
+ self.update_attribute(position_column, position)
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end