ruby-activeldap 0.8.3 → 0.8.3.1

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 (177) hide show
  1. data/CHANGES +431 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +58 -0
  4. data/README +104 -0
  5. data/Rakefile +165 -0
  6. data/TODO +22 -0
  7. data/benchmark/bench-al.rb +202 -0
  8. data/benchmark/config.yaml.sample +5 -0
  9. data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
  10. data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
  11. data/examples/al-admin/README +182 -0
  12. data/examples/al-admin/Rakefile +10 -0
  13. data/examples/al-admin/app/controllers/account_controller.rb +50 -0
  14. data/examples/al-admin/app/controllers/application.rb +15 -0
  15. data/examples/al-admin/app/controllers/directory_controller.rb +22 -0
  16. data/examples/al-admin/app/controllers/users_controller.rb +38 -0
  17. data/examples/al-admin/app/controllers/welcome_controller.rb +4 -0
  18. data/examples/al-admin/app/helpers/account_helper.rb +2 -0
  19. data/examples/al-admin/app/helpers/application_helper.rb +6 -0
  20. data/examples/al-admin/app/helpers/directory_helper.rb +2 -0
  21. data/examples/al-admin/app/helpers/users_helper.rb +13 -0
  22. data/examples/al-admin/app/helpers/welcome_helper.rb +2 -0
  23. data/examples/al-admin/app/models/entry.rb +19 -0
  24. data/examples/al-admin/app/models/ldap_user.rb +49 -0
  25. data/examples/al-admin/app/models/user.rb +91 -0
  26. data/examples/al-admin/app/views/account/login.rhtml +12 -0
  27. data/examples/al-admin/app/views/account/sign_up.rhtml +22 -0
  28. data/examples/al-admin/app/views/directory/index.rhtml +5 -0
  29. data/examples/al-admin/app/views/directory/populate.rhtml +2 -0
  30. data/examples/al-admin/app/views/layouts/application.rhtml +41 -0
  31. data/examples/al-admin/app/views/users/_attribute_information.rhtml +22 -0
  32. data/examples/al-admin/app/views/users/_entry.rhtml +12 -0
  33. data/examples/al-admin/app/views/users/_form.rhtml +29 -0
  34. data/examples/al-admin/app/views/users/_object_class_information.rhtml +23 -0
  35. data/examples/al-admin/app/views/users/edit.rhtml +10 -0
  36. data/examples/al-admin/app/views/users/index.rhtml +9 -0
  37. data/examples/al-admin/app/views/users/show.rhtml +3 -0
  38. data/examples/al-admin/app/views/welcome/index.rhtml +16 -0
  39. data/examples/al-admin/config/boot.rb +45 -0
  40. data/examples/al-admin/config/database.yml.example +19 -0
  41. data/examples/al-admin/config/environment.rb +68 -0
  42. data/examples/al-admin/config/environments/development.rb +21 -0
  43. data/examples/al-admin/config/environments/production.rb +18 -0
  44. data/examples/al-admin/config/environments/test.rb +19 -0
  45. data/examples/al-admin/config/ldap.yml.example +21 -0
  46. data/examples/al-admin/config/routes.rb +26 -0
  47. data/examples/al-admin/db/migrate/001_create_users.rb +16 -0
  48. data/examples/al-admin/lib/accept_http_rails_relative_url_root.rb +9 -0
  49. data/examples/al-admin/lib/authenticated_system.rb +131 -0
  50. data/examples/al-admin/lib/authenticated_test_helper.rb +113 -0
  51. data/examples/al-admin/lib/tasks/gettext.rake +35 -0
  52. data/examples/al-admin/po/en/al-admin.po +190 -0
  53. data/examples/al-admin/po/ja/al-admin.po +190 -0
  54. data/examples/al-admin/po/nl/al-admin.po +202 -0
  55. data/examples/al-admin/public/.htaccess +40 -0
  56. data/examples/al-admin/public/404.html +30 -0
  57. data/examples/al-admin/public/500.html +30 -0
  58. data/examples/al-admin/public/dispatch.cgi +10 -0
  59. data/examples/al-admin/public/dispatch.fcgi +24 -0
  60. data/examples/al-admin/public/dispatch.rb +10 -0
  61. data/examples/al-admin/public/favicon.ico +0 -0
  62. data/examples/al-admin/public/images/rails.png +0 -0
  63. data/examples/al-admin/public/javascripts/application.js +2 -0
  64. data/examples/al-admin/public/javascripts/controls.js +833 -0
  65. data/examples/al-admin/public/javascripts/dragdrop.js +942 -0
  66. data/examples/al-admin/public/javascripts/effects.js +1088 -0
  67. data/examples/al-admin/public/javascripts/prototype.js +2515 -0
  68. data/examples/al-admin/public/robots.txt +1 -0
  69. data/examples/al-admin/public/stylesheets/rails.css +35 -0
  70. data/examples/al-admin/public/stylesheets/screen.css +52 -0
  71. data/examples/al-admin/script/about +3 -0
  72. data/examples/al-admin/script/breakpointer +3 -0
  73. data/examples/al-admin/script/console +3 -0
  74. data/examples/al-admin/script/destroy +3 -0
  75. data/examples/al-admin/script/generate +3 -0
  76. data/examples/al-admin/script/performance/benchmarker +3 -0
  77. data/examples/al-admin/script/performance/profiler +3 -0
  78. data/examples/al-admin/script/plugin +3 -0
  79. data/examples/al-admin/script/process/inspector +3 -0
  80. data/examples/al-admin/script/process/reaper +3 -0
  81. data/examples/al-admin/script/process/spawner +3 -0
  82. data/examples/al-admin/script/runner +3 -0
  83. data/examples/al-admin/script/server +3 -0
  84. data/examples/al-admin/test/fixtures/users.yml +9 -0
  85. data/examples/al-admin/test/functional/account_controller_test.rb +24 -0
  86. data/examples/al-admin/test/functional/directory_controller_test.rb +18 -0
  87. data/examples/al-admin/test/functional/users_controller_test.rb +18 -0
  88. data/examples/al-admin/test/functional/welcome_controller_test.rb +18 -0
  89. data/examples/al-admin/test/run-test.sh +3 -0
  90. data/examples/al-admin/test/test_helper.rb +28 -0
  91. data/examples/al-admin/test/unit/user_test.rb +13 -0
  92. data/examples/al-admin/vendor/plugins/exception_notification/README +111 -0
  93. data/examples/al-admin/vendor/plugins/exception_notification/init.rb +1 -0
  94. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifiable.rb +99 -0
  95. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier.rb +67 -0
  96. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier_helper.rb +77 -0
  97. data/examples/al-admin/vendor/plugins/exception_notification/test/exception_notifier_helper_test.rb +61 -0
  98. data/examples/al-admin/vendor/plugins/exception_notification/test/test_helper.rb +7 -0
  99. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_backtrace.rhtml +1 -0
  100. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_environment.rhtml +7 -0
  101. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_inspect_model.rhtml +16 -0
  102. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_request.rhtml +3 -0
  103. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_session.rhtml +2 -0
  104. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_title.rhtml +3 -0
  105. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/exception_notification.rhtml +6 -0
  106. data/examples/config.yaml.example +5 -0
  107. data/examples/example.der +0 -0
  108. data/examples/example.jpg +0 -0
  109. data/examples/groupadd +41 -0
  110. data/examples/groupdel +35 -0
  111. data/examples/groupls +49 -0
  112. data/examples/groupmod +42 -0
  113. data/examples/lpasswd +55 -0
  114. data/examples/objects/group.rb +13 -0
  115. data/examples/objects/ou.rb +4 -0
  116. data/examples/objects/user.rb +20 -0
  117. data/examples/ouadd +38 -0
  118. data/examples/useradd +45 -0
  119. data/examples/useradd-binary +50 -0
  120. data/examples/userdel +34 -0
  121. data/examples/userls +50 -0
  122. data/examples/usermod +42 -0
  123. data/examples/usermod-binary-add +47 -0
  124. data/examples/usermod-binary-add-time +51 -0
  125. data/examples/usermod-binary-del +48 -0
  126. data/examples/usermod-lang-add +43 -0
  127. data/lib/active_ldap.rb +978 -0
  128. data/lib/active_ldap/adapter/base.rb +512 -0
  129. data/lib/active_ldap/adapter/ldap.rb +233 -0
  130. data/lib/active_ldap/adapter/ldap_ext.rb +69 -0
  131. data/lib/active_ldap/adapter/net_ldap.rb +290 -0
  132. data/lib/active_ldap/adapter/net_ldap_ext.rb +29 -0
  133. data/lib/active_ldap/association/belongs_to.rb +47 -0
  134. data/lib/active_ldap/association/belongs_to_many.rb +42 -0
  135. data/lib/active_ldap/association/collection.rb +83 -0
  136. data/lib/active_ldap/association/has_many.rb +31 -0
  137. data/lib/active_ldap/association/has_many_utils.rb +35 -0
  138. data/lib/active_ldap/association/has_many_wrap.rb +46 -0
  139. data/lib/active_ldap/association/proxy.rb +102 -0
  140. data/lib/active_ldap/associations.rb +172 -0
  141. data/lib/active_ldap/attributes.rb +211 -0
  142. data/lib/active_ldap/base.rb +1256 -0
  143. data/lib/active_ldap/callbacks.rb +19 -0
  144. data/lib/active_ldap/command.rb +48 -0
  145. data/lib/active_ldap/configuration.rb +114 -0
  146. data/lib/active_ldap/connection.rb +234 -0
  147. data/lib/active_ldap/distinguished_name.rb +250 -0
  148. data/lib/active_ldap/escape.rb +12 -0
  149. data/lib/active_ldap/get_text/parser.rb +142 -0
  150. data/lib/active_ldap/get_text_fallback.rb +53 -0
  151. data/lib/active_ldap/get_text_support.rb +12 -0
  152. data/lib/active_ldap/helper.rb +23 -0
  153. data/lib/active_ldap/ldap_error.rb +74 -0
  154. data/lib/active_ldap/object_class.rb +93 -0
  155. data/lib/active_ldap/operations.rb +419 -0
  156. data/lib/active_ldap/populate.rb +44 -0
  157. data/lib/active_ldap/schema.rb +427 -0
  158. data/lib/active_ldap/timeout.rb +75 -0
  159. data/lib/active_ldap/timeout_stub.rb +17 -0
  160. data/lib/active_ldap/user_password.rb +93 -0
  161. data/lib/active_ldap/validations.rb +112 -0
  162. data/po/en/active-ldap.po +3011 -0
  163. data/po/ja/active-ldap.po +3044 -0
  164. data/rails/plugin/active_ldap/README +54 -0
  165. data/rails/plugin/active_ldap/generators/scaffold_al/scaffold_al_generator.rb +7 -0
  166. data/rails/plugin/active_ldap/generators/scaffold_al/templates/ldap.yml +21 -0
  167. data/rails/plugin/active_ldap/init.rb +19 -0
  168. data/test/al-test-utils.rb +362 -0
  169. data/test/command.rb +62 -0
  170. data/test/config.yaml.sample +6 -0
  171. data/test/run-test.rb +31 -0
  172. data/test/test-unit-ext.rb +4 -0
  173. data/test/test-unit-ext/always-show-result.rb +28 -0
  174. data/test/test-unit-ext/backtrace-filter.rb +17 -0
  175. data/test/test-unit-ext/long-display-for-emacs.rb +25 -0
  176. data/test/test-unit-ext/priority.rb +163 -0
  177. metadata +211 -4
@@ -0,0 +1,29 @@
1
+ require_library_or_gem 'net/ldap'
2
+
3
+ module Net
4
+ class LDAP
5
+ class Entry
6
+ alias initialize_without_original_attribute_names initialize
7
+ def initialize(*args)
8
+ @original_attribute_names = []
9
+ initialize_without_original_attribute_names(*args)
10
+ end
11
+
12
+ alias aset_without_original_attribute_names []=
13
+ def []=(name, value)
14
+ @original_attribute_names << name
15
+ aset_without_original_attribute_names(name, value)
16
+ end
17
+
18
+ def original_attribute_names
19
+ @original_attribute_names.compact.uniq
20
+ end
21
+
22
+ def each_attribute
23
+ attribute_names.sort_by {|name| name.to_s}.each do |name|
24
+ yield name, self[name]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ require 'active_ldap/association/proxy'
2
+
3
+ module ActiveLdap
4
+ module Association
5
+ class BelongsTo < Proxy
6
+ def replace(entry)
7
+ if entry.nil?
8
+ @target = @owner[@options[:foreign_key_name]] = nil
9
+ else
10
+ @target = (Proxy === entry ? entry.target : entry)
11
+ infect_connection(@target)
12
+ unless entry.new_entry?
13
+ @owner[@options[:foreign_key_name]] = entry[primary_key]
14
+ end
15
+ @updated = true
16
+ end
17
+
18
+ loaded
19
+ entry
20
+ end
21
+
22
+ def updated?
23
+ @updated
24
+ end
25
+
26
+ private
27
+ def have_foreign_key?
28
+ not @owner[@options[:foreign_key_name]].nil?
29
+ end
30
+
31
+ def find_target
32
+ value = @owner[@options[:foreign_key_name]]
33
+ raise EntryNotFound if value.nil?
34
+ key = primary_key
35
+ if key == "dn"
36
+ result = foreign_class.find(value, find_options)
37
+ else
38
+ filter = {key => value}
39
+ options = find_options(:filter => filter, :limit => 1)
40
+ result = foreign_class.find(:all, options).first
41
+ end
42
+ raise EntryNotFound if result.nil?
43
+ result
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,42 @@
1
+ require 'active_ldap/association/collection'
2
+
3
+ module ActiveLdap
4
+ module Association
5
+ class BelongsToMany < Collection
6
+ private
7
+ def insert_entry(entry)
8
+ old_value = entry[@options[:many], true]
9
+ new_value = old_value + @owner[@options[:foreign_key_name], true]
10
+ new_value = new_value.uniq.sort
11
+ if old_value != new_value
12
+ entry[@options[:many]] = new_value
13
+ entry.save
14
+ end
15
+ end
16
+
17
+ def delete_entries(entries)
18
+ entries.each do |entry|
19
+ old_value = entry[@options[:many], true]
20
+ new_value = old_value - @owner[@options[:foreign_key_name], true]
21
+ new_value = new_value.uniq.sort
22
+ if old_value != new_value
23
+ entry[@options[:many]] = new_value
24
+ entry.save
25
+ end
26
+ end
27
+ end
28
+
29
+ def find_target
30
+ values = @owner[@options[:foreign_key_name], true].compact
31
+ return [] if values.empty?
32
+
33
+ key = @options[:many]
34
+ components = values.collect do |value|
35
+ [key, value]
36
+ end
37
+ options = find_options(:filter => [:or, *components])
38
+ foreign_class.find(:all, options)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,83 @@
1
+ require 'active_ldap/association/proxy'
2
+
3
+ module ActiveLdap
4
+ module Association
5
+ class Collection < Proxy
6
+ include Enumerable
7
+
8
+ def to_ary
9
+ load_target
10
+ @target.to_ary
11
+ end
12
+
13
+ def reset
14
+ @target = []
15
+ @loaded = false
16
+ end
17
+
18
+ def <<(*entries)
19
+ add_entries(*entries)
20
+ end
21
+ alias_method(:push, :<<)
22
+ alias_method(:concat, :<<)
23
+
24
+ def each(&block)
25
+ to_ary.each(&block)
26
+ end
27
+
28
+ def delete(*entries)
29
+ entries = flatten_deeper(entries).reject do |entry|
30
+ @target.delete(entry) if entry.new_entry?
31
+ entry.new_entry?
32
+ end
33
+ return if entries.empty?
34
+
35
+ delete_entries(entries)
36
+ entries.each do |entry|
37
+ @target.delete(entry)
38
+ end
39
+ end
40
+
41
+ def replace(others)
42
+ load_target
43
+ deleted_entries = @target - others
44
+ added_entries = others - @target
45
+
46
+ delete(deleted_entries)
47
+ concat(added_entries)
48
+ end
49
+
50
+ def exists?
51
+ load_target
52
+ not @target.empty?
53
+ end
54
+
55
+ private
56
+ def flatten_deeper(array)
57
+ array.collect do |element|
58
+ element.respond_to?(:flatten) ? element.flatten : element
59
+ end.flatten
60
+ end
61
+
62
+ def insert_entry(entry)
63
+ entry[@options[:foreign_key_name]] = @owner[@options[:local_key_name]]
64
+ entry.save
65
+ end
66
+
67
+ def add_entries(*entries)
68
+ result = true
69
+ load_target
70
+
71
+ flatten_deeper(entries).each do |entry|
72
+ unless @owner.new_entry?
73
+ infect_connection(entry)
74
+ result &&= insert_entry(entry)
75
+ end
76
+ @target << entry
77
+ end
78
+
79
+ result && self
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,31 @@
1
+ require 'active_ldap/association/collection'
2
+ require 'active_ldap/association/has_many_utils'
3
+
4
+ module ActiveLdap
5
+ module Association
6
+ class HasMany < Collection
7
+ include HasManyUtils
8
+
9
+ private
10
+ def insert_entry(entry)
11
+ entry[primary_key] = @owner[@options[:foreign_key_name]]
12
+ entry.save
13
+ end
14
+
15
+ def find_target
16
+ collect_targets(:foreign_key_name)
17
+ end
18
+
19
+ def delete_entries(entries)
20
+ key = primary_key
21
+ components = @owner[@options[:foreign_key_name], true].reject do |value|
22
+ value.nil?
23
+ end
24
+ filter = [:and,
25
+ [:and, {key => components}],
26
+ [:or, {foreign_class.dn_attribute => entries.collect(&:id)}]]
27
+ foreign_class.update_all({key => []}, filter)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ module ActiveLdap
2
+ module Association
3
+ module HasManyUtils
4
+ private
5
+ def collect_targets(requested_target_key, need_requested_targets=false)
6
+ foreign_base_key = primary_key
7
+ return [] if foreign_base_key.nil?
8
+
9
+ requested_targets = @owner[@options[requested_target_key], true]
10
+
11
+ components = requested_targets.reject(&:nil?)
12
+ unless foreign_base_key == "dn"
13
+ components = components.collect do |value|
14
+ [foreign_base_key, value]
15
+ end
16
+ end
17
+
18
+ if components.empty?
19
+ targets = []
20
+ elsif foreign_base_key == "dn"
21
+ targets = foreign_class.find(components, find_options)
22
+ else
23
+ options = find_options(:filter => [:or, *components])
24
+ targets = foreign_class.find(:all, options)
25
+ end
26
+
27
+ if need_requested_targets
28
+ [targets, requested_targets]
29
+ else
30
+ targets
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ require 'active_ldap/association/collection'
2
+ require 'active_ldap/association/has_many_utils'
3
+
4
+ module ActiveLdap
5
+ module Association
6
+ class HasManyWrap < Collection
7
+ include HasManyUtils
8
+
9
+ private
10
+ def insert_entry(entry)
11
+ old_value = @owner[@options[:wrap], true]
12
+ new_value = (old_value + entry[primary_key, true]).uniq.sort
13
+ if old_value != new_value
14
+ @owner[@options[:wrap]] = new_value
15
+ @owner.save
16
+ end
17
+ end
18
+
19
+ def delete_entries(entries)
20
+ old_value = @owner[@options[:wrap], true]
21
+ new_value = old_value - entries.collect {|entry| entry[primary_key]}
22
+ new_value = new_value.uniq.sort
23
+ if old_value != new_value
24
+ @owner[@options[:wrap]] = new_value
25
+ @owner.save
26
+ end
27
+ end
28
+
29
+ def find_target
30
+ targets, requested_targets = collect_targets(:wrap, true)
31
+ return [] if targets.nil?
32
+
33
+ found_targets = {}
34
+ foreign_base_key = primary_key
35
+ targets.each do |target|
36
+ found_targets[target.send(foreign_base_key)] ||= target
37
+ end
38
+
39
+ klass = foreign_class
40
+ requested_targets.collect do |name|
41
+ found_targets[name] || klass.new(name)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,102 @@
1
+ module ActiveLdap
2
+ module Association
3
+ class Proxy
4
+ alias_method :proxy_respond_to?, :respond_to?
5
+ alias_method :proxy_extend, :extend
6
+
7
+ def initialize(owner, options)
8
+ @owner = owner
9
+ @options = options
10
+ extend(options[:extend]) if options[:extend]
11
+ reset
12
+ end
13
+
14
+ def respond_to?(symbol, include_priv=false)
15
+ proxy_respond_to?(symbol, include_priv) or
16
+ (load_target && @target.respond_to?(symbol, include_priv))
17
+ end
18
+
19
+ def ===(other)
20
+ load_target and other === @target
21
+ end
22
+
23
+ def reset
24
+ @target = nil
25
+ @loaded = false
26
+ end
27
+
28
+ def reload
29
+ reset
30
+ load_target
31
+ end
32
+
33
+ def loaded?
34
+ @loaded
35
+ end
36
+
37
+ def loaded
38
+ @loaded = true
39
+ end
40
+
41
+ def target
42
+ @target
43
+ end
44
+
45
+ def target=(target)
46
+ @target = target
47
+ loaded
48
+ end
49
+
50
+ def exists?
51
+ load_target
52
+ not @target.nil?
53
+ end
54
+
55
+ private
56
+ def method_missing(method, *args, &block)
57
+ load_target
58
+ @target.send(method, *args, &block)
59
+ end
60
+
61
+ def foreign_class
62
+ klass = @owner.class.associated_class(@options[:association_id])
63
+ klass = @owner.class.module_eval(klass) if klass.is_a?(String)
64
+ klass
65
+ end
66
+
67
+ def have_foreign_key?
68
+ false
69
+ end
70
+
71
+ def primary_key
72
+ @options[:primary_key_name] || foreign_class.dn_attribute
73
+ end
74
+
75
+ def load_target
76
+ if !@owner.new_entry? or have_foreign_key?
77
+ begin
78
+ @target = find_target unless loaded?
79
+ rescue EntryNotFound
80
+ reset
81
+ end
82
+ end
83
+
84
+ loaded if target
85
+ target
86
+ end
87
+
88
+ def find_options(options={})
89
+ if @owner.connection != @owner.class.connection
90
+ {:connection => @owner.connection}.merge(options)
91
+ else
92
+ options
93
+ end
94
+ end
95
+
96
+ def infect_connection(target)
97
+ conn = @owner.instance_variable_get("@connection")
98
+ target.connection = conn if conn
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,172 @@
1
+ require 'active_ldap/association/belongs_to'
2
+ require 'active_ldap/association/belongs_to_many'
3
+ require 'active_ldap/association/has_many'
4
+ require 'active_ldap/association/has_many_wrap'
5
+
6
+ module ActiveLdap
7
+ # Associations
8
+ #
9
+ # Associations provides the class methods needed for
10
+ # the extension classes to create methods using
11
+ # belongs_to and has_many
12
+ module Associations
13
+ def self.append_features(base)
14
+ super
15
+ base.extend(ClassMethods)
16
+ base.class_inheritable_array(:associations)
17
+ base.associations = []
18
+ end
19
+
20
+ module ClassMethods
21
+ def set_associated_class(name, klass)
22
+ @associated_classes ||= {}
23
+ @associated_classes[name.to_s] = klass
24
+ end
25
+
26
+ def associated_class(name)
27
+ @associated_classes[name.to_s]
28
+ end
29
+
30
+ # belongs_to
31
+ #
32
+ # This defines a method for an extension class map its DN key
33
+ # attribute value on to multiple items which reference it by
34
+ # |:foreign_key| in the other LDAP entry covered by class |:class_name|.
35
+ #
36
+ # Example:
37
+ # belongs_to :groups, :class_name => "Group",
38
+ # :many => "memberUid" # Group#memberUid
39
+ # # :foreign_key => "uid" # User#uid
40
+ # # dn attribute value is used by default
41
+ # belongs_to :primary_group, :class_name => "Group",
42
+ # :foreign_key => "gidNumber", # User#gidNumber
43
+ # :primary_key => "gidNumber" # Group#gidNumber
44
+ #
45
+ def belongs_to(association_id, options={})
46
+ validate_belongs_to_options(options)
47
+ klass = options[:class] || Inflector.classify(association_id)
48
+ foreign_key = options[:foreign_key]
49
+ primary_key = options[:primary_key]
50
+ many = options[:many]
51
+ set_associated_class(association_id, klass)
52
+
53
+ opts = {
54
+ :association_id => association_id,
55
+ :foreign_key_name => foreign_key,
56
+ :primary_key_name => primary_key,
57
+ :many => many,
58
+ :extend => options[:extend],
59
+ }
60
+ if opts[:many]
61
+ association_class = Association::BelongsToMany
62
+ opts[:foreign_key_name] ||= dn_attribute
63
+ else
64
+ association_class = Association::BelongsTo
65
+ opts[:foreign_key_name] ||= "#{association_id}_id"
66
+
67
+ before_save(<<-EOC)
68
+ if defined?(@#{association_id})
69
+ association = @#{association_id}
70
+ if association and association.updated?
71
+ self[association.__send__(:primary_key)] =
72
+ association[#{opts[:foreign_key_name].dump}]
73
+ end
74
+ end
75
+ EOC
76
+ end
77
+
78
+ association_accessor(association_id) do |target|
79
+ association_class.new(target, opts)
80
+ end
81
+ end
82
+
83
+
84
+ # has_many
85
+ #
86
+ # This defines a method for an extension class expand an
87
+ # existing multi-element attribute into ActiveLdap objects.
88
+ # This discards any calls which result in entries that
89
+ # don't exist in LDAP!
90
+ #
91
+ # Example:
92
+ # has_many :primary_members, :class_name => "User",
93
+ # :primary_key => "gidNumber", # User#gidNumber
94
+ # :foreign_key => "gidNumber" # Group#gidNumber
95
+ # has_many :members, :class_name => "User",
96
+ # :wrap => "memberUid" # Group#memberUid
97
+ def has_many(association_id, options = {})
98
+ validate_has_many_options(options)
99
+ klass = options[:class] || Inflector.classify(association_id)
100
+ foreign_key = options[:foreign_key] || association_id.to_s + "_id"
101
+ primary_key = options[:primary_key]
102
+ set_associated_class(association_id, klass)
103
+
104
+ opts = {
105
+ :association_id => association_id,
106
+ :foreign_key_name => foreign_key,
107
+ :primary_key_name => primary_key,
108
+ :wrap => options[:wrap],
109
+ :extend => options[:extend],
110
+ }
111
+ if opts[:wrap]
112
+ association_class = Association::HasManyWrap
113
+ else
114
+ association_class = Association::HasMany
115
+ end
116
+
117
+ association_accessor(association_id) do |target|
118
+ association_class.new(target, opts)
119
+ end
120
+ end
121
+
122
+ private
123
+ def association_accessor(name, &make_association)
124
+ define_method("__make_#{name}") do
125
+ make_association.call(self)
126
+ end
127
+ associations << name
128
+ association_reader(name, &make_association)
129
+ association_writer(name, &make_association)
130
+ end
131
+
132
+ def association_reader(name, &make_association)
133
+ class_eval(<<-EOM, __FILE__, __LINE__ + 1)
134
+ def #{name}
135
+ @#{name} ||= __make_#{name}
136
+ end
137
+ EOM
138
+ end
139
+
140
+ def association_writer(name, &make_association)
141
+ class_eval(<<-EOM, __FILE__, __LINE__ + 1)
142
+ def #{name}=(new_value)
143
+ association = defined?(@#{name}) ? @#{name} : nil
144
+ association ||= __make_#{name}
145
+ association.replace(new_value)
146
+ @#{name} = new_value.nil? ? nil : association
147
+ @#{name}
148
+ end
149
+ EOM
150
+ end
151
+
152
+ VALID_BELONGS_TO_OPTIONS = [:class, :foreign_key, :primary_key, :many,
153
+ :extend]
154
+ def validate_belongs_to_options(options)
155
+ options.assert_valid_keys(VALID_BELONGS_TO_OPTIONS)
156
+ end
157
+
158
+ VALID_HAS_MANY_OPTIONS = [:class, :foreign_key, :primary_key, :wrap,
159
+ :extend]
160
+ def validate_has_many_options(options)
161
+ options.assert_valid_keys(VALID_HAS_MANY_OPTIONS)
162
+ end
163
+ end
164
+
165
+ def clear_association_cache
166
+ return if new_record?
167
+ (self.class.associations || []).each do |association|
168
+ instance_variable_set("@#{association}", nil)
169
+ end
170
+ end
171
+ end
172
+ end