activerecord_authorails 1.0.0

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.
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