ad-framework 0.1.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 (64) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +12 -0
  3. data/README.markdown +36 -0
  4. data/Rakefile +19 -0
  5. data/ad-framework.gemspec +25 -0
  6. data/doc/open_ldap_server.markdown +17 -0
  7. data/extras/adtest.schema +304 -0
  8. data/extras/slapd.conf +10 -0
  9. data/lib/ad-framework.rb +53 -0
  10. data/lib/ad-framework/attribute.rb +35 -0
  11. data/lib/ad-framework/attribute_type.rb +133 -0
  12. data/lib/ad-framework/auxiliary_class.rb +24 -0
  13. data/lib/ad-framework/config.rb +72 -0
  14. data/lib/ad-framework/config/attribute_definition.rb +18 -0
  15. data/lib/ad-framework/config/mapping.rb +26 -0
  16. data/lib/ad-framework/exceptions.rb +17 -0
  17. data/lib/ad-framework/fields.rb +44 -0
  18. data/lib/ad-framework/patterns/callbacks.rb +47 -0
  19. data/lib/ad-framework/patterns/has_schema.rb +127 -0
  20. data/lib/ad-framework/patterns/persistence.rb +67 -0
  21. data/lib/ad-framework/patterns/searchable.rb +117 -0
  22. data/lib/ad-framework/patterns/validations.rb +50 -0
  23. data/lib/ad-framework/schema.rb +118 -0
  24. data/lib/ad-framework/structural_class.rb +61 -0
  25. data/lib/ad-framework/utilities/entry_builder.rb +77 -0
  26. data/lib/ad-framework/utilities/transaction.rb +32 -0
  27. data/lib/ad-framework/utilities/validator.rb +26 -0
  28. data/lib/ad-framework/version.rb +5 -0
  29. data/test/helper.rb +71 -0
  30. data/test/integration/defined_array_test.rb +49 -0
  31. data/test/integration/defined_integer_test.rb +48 -0
  32. data/test/integration/defined_string_test.rb +48 -0
  33. data/test/integration/defined_top_test.rb +101 -0
  34. data/test/integration/defined_user_test.rb +140 -0
  35. data/test/irb.rb +2 -0
  36. data/test/support/factory.rb +67 -0
  37. data/test/support/ldap.yml +6 -0
  38. data/test/support/schema/attribute_types.rb +67 -0
  39. data/test/support/schema/attributes.rb +10 -0
  40. data/test/support/schema/auxiliary_classes.rb +12 -0
  41. data/test/support/schema/structural_classes.rb +46 -0
  42. data/test/support/seed.rb +28 -0
  43. data/test/support/state.rb +29 -0
  44. data/test/unit/ad-framework/attribute_test.rb +84 -0
  45. data/test/unit/ad-framework/attribute_type/class_methods_test.rb +146 -0
  46. data/test/unit/ad-framework/attribute_type_test.rb +114 -0
  47. data/test/unit/ad-framework/auxiliary_class_test.rb +39 -0
  48. data/test/unit/ad-framework/config/attribute_definition_test.rb +26 -0
  49. data/test/unit/ad-framework/config/mapping_test.rb +41 -0
  50. data/test/unit/ad-framework/config_test.rb +121 -0
  51. data/test/unit/ad-framework/fields_test.rb +44 -0
  52. data/test/unit/ad-framework/patterns/callbacks_test.rb +90 -0
  53. data/test/unit/ad-framework/patterns/has_schema/class_methods_test.rb +214 -0
  54. data/test/unit/ad-framework/patterns/has_schema_test.rb +96 -0
  55. data/test/unit/ad-framework/patterns/persistence_test.rb +126 -0
  56. data/test/unit/ad-framework/patterns/searchable_test.rb +201 -0
  57. data/test/unit/ad-framework/patterns/validations_test.rb +113 -0
  58. data/test/unit/ad-framework/schema_test.rb +268 -0
  59. data/test/unit/ad-framework/structural_class_test.rb +64 -0
  60. data/test/unit/ad-framework/utilities/entry_builder_test.rb +107 -0
  61. data/test/unit/ad-framework/utilities/transaction_test.rb +50 -0
  62. data/test/unit/ad-framework/utilities/validator_test.rb +46 -0
  63. data/test/unit/ad-framework_test.rb +116 -0
  64. metadata +225 -0
@@ -0,0 +1,35 @@
1
+ require 'ad-framework/exceptions'
2
+
3
+ module AD
4
+ module Framework
5
+
6
+ class Attribute
7
+ attr_accessor :name, :ldap_name, :attribute_type
8
+
9
+ def initialize(name)
10
+ self.name = name
11
+ definition = AD::Framework.defined_attributes.find(self.name)
12
+ if !definition
13
+ raise(AD::Framework::AttributeNotDefined, "There is no attribute defintion for #{name.inspect}.")
14
+ end
15
+ self.ldap_name = definition.ldap_name
16
+ self.attribute_type = AD::Framework.defined_attribute_types.find(definition.type)
17
+ if !self.attribute_type
18
+ raise(*[
19
+ AD::Framework::AttributeTypeNotDefined,
20
+ "There is no attribute type defined for #{definition.type.inspect}"
21
+ ])
22
+ end
23
+ end
24
+
25
+ def define_reader(klass)
26
+ self.attribute_type.define_reader(self, klass)
27
+ end
28
+ def define_writer(klass)
29
+ self.attribute_type.define_writer(self, klass)
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,133 @@
1
+ module AD
2
+ module Framework
3
+
4
+ class AttributeType
5
+ attr_accessor :object, :attr_ldap_name, :value, :ldap_value
6
+
7
+ def initialize(object, attr_ldap_name, value = nil)
8
+ self.object = object
9
+ self.attr_ldap_name = attr_ldap_name
10
+ self.value = value.nil? ? self.value_from_field : value
11
+ end
12
+
13
+ def value_from_field
14
+ (self.object.fields[self.attr_ldap_name] || []).first
15
+ end
16
+
17
+ def value=(new_value)
18
+ @value = new_value
19
+ self.ldap_value = @value
20
+ @value
21
+ end
22
+
23
+ def ldap_value=(new_ldap_value)
24
+ self.object.fields[self.attr_ldap_name] = if new_ldap_value
25
+ [*new_ldap_value].collect(&:to_s)
26
+ else
27
+ []
28
+ end
29
+ @ldap_value = new_ldap_value
30
+ end
31
+
32
+ def reset
33
+ self.value = self.value_from_field
34
+ end
35
+
36
+ def is_set?
37
+ !self.value.nil?
38
+ end
39
+
40
+ def inspect
41
+ attr_display = [ :value, :ldap_value, :attr_ldap_name ].collect do |attr|
42
+ "#{attr}: #{self.instance_variable_get("@#{attr}").inspect}"
43
+ end
44
+ attr_display.push("object: #{object.class} - #{object.dn.inspect}")
45
+ [ "#<#{self.class} ", attr_display.sort.join(", "), ">" ].join
46
+ end
47
+
48
+ class << self
49
+
50
+ def key(new_value = nil)
51
+ if new_value
52
+ @key = new_value
53
+ end
54
+ @key
55
+ end
56
+
57
+ def define_attribute_type(attribute, klass)
58
+ method_name = "#{attribute.name}_attribute_type"
59
+ if !klass.instance_methods.collect(&:to_s).include?(method_name)
60
+ attribute_type_method = self.attribute_type_method(attribute, method_name)
61
+ if attribute_type_method.kind_of?(::Proc)
62
+ klass.class_eval(&attribute_type_method)
63
+ else
64
+ klass.class_eval(attribute_type_method.to_s)
65
+ end
66
+ end
67
+ end
68
+
69
+ def attribute_type_method(attribute, method_name)
70
+ <<-DEFINE_ATTRIBUTE_TYPE
71
+
72
+ def #{method_name}
73
+ unless @#{method_name}
74
+ type = #{attribute.attribute_type}.new(self, "#{attribute.ldap_name}")
75
+ @#{method_name} = type
76
+ end
77
+ @#{method_name}
78
+ end
79
+
80
+ DEFINE_ATTRIBUTE_TYPE
81
+ end
82
+
83
+ def define_reader(attribute, klass)
84
+ self.define_attribute_type(attribute, klass)
85
+ reader_method = self.reader_method(attribute)
86
+ if reader_method.kind_of?(::Proc)
87
+ klass.class_eval(&reader_method)
88
+ else
89
+ klass.class_eval(reader_method.to_s)
90
+ end
91
+ end
92
+
93
+ def reader_method(attribute)
94
+ <<-DEFINE_READER
95
+
96
+ def #{attribute.name}
97
+ self.#{attribute.name}_attribute_type.value
98
+ end
99
+
100
+ DEFINE_READER
101
+ end
102
+
103
+ def define_writer(attribute, klass)
104
+ self.define_attribute_type(attribute, klass)
105
+ writer_method = self.writer_method(attribute)
106
+ if writer_method.kind_of?(::Proc)
107
+ klass.class_eval(&writer_method)
108
+ else
109
+ klass.class_eval(writer_method.to_s)
110
+ end
111
+ end
112
+
113
+ def writer_method(attribute)
114
+ <<-DEFINE_WRITER
115
+
116
+ def #{attribute.name}=(new_value)
117
+ self.#{attribute.name}_attribute_type.value = new_value
118
+ if self.respond_to?("#{attribute.name}")
119
+ self.#{attribute.name}
120
+ else
121
+ new_value
122
+ end
123
+ end
124
+
125
+ DEFINE_WRITER
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,24 @@
1
+ require 'ad-framework/patterns/has_schema'
2
+
3
+ module AD
4
+ module Framework
5
+
6
+ module AuxiliaryClass
7
+ class << self
8
+
9
+ def included(klass)
10
+ klass.class_eval do
11
+ include AD::Framework::Patterns::HasSchema
12
+
13
+ def self.included(klass)
14
+ super
15
+ klass.schema.add_auxiliary_class(self)
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,72 @@
1
+ require 'ad-ldap'
2
+
3
+ require 'ad-framework/config/mapping'
4
+ require 'ad-framework/config/attribute_definition'
5
+
6
+ module AD
7
+ module Framework
8
+
9
+ class Config
10
+ attr_accessor :attributes, :attribute_types, :object_classes, :ldap_prefix
11
+
12
+ def initialize
13
+ self.mappings = AD::Framework::Config::Mapping.new
14
+ self.mappings.add(:dn, "distinguishedname")
15
+
16
+ self.attributes = AD::Framework::Config::Mapping.new
17
+ self.attribute_types = AD::Framework::Config::Mapping.new
18
+ self.object_classes = AD::Framework::Config::Mapping.new
19
+ end
20
+
21
+ def ldap_prefix=(new_value)
22
+ @ldap_prefix = new_value
23
+ self.object_classes.dup.each do |key, value|
24
+ self.object_classes.delete(key)
25
+ self.object_classes.add("#{@ldap_prefix}#{key}", value)
26
+ end
27
+ end
28
+
29
+ def ldap(&block)
30
+ if block
31
+ AD::LDAP.configure(&block)
32
+ end
33
+ AD::LDAP
34
+ end
35
+ alias :adapter :ldap
36
+
37
+ def logger
38
+ self.adapter.logger
39
+ end
40
+ def logger=(new_logger)
41
+ self.adapter.config.logger = new_logger
42
+ end
43
+
44
+ [ :search_size_supported, :mappings, :run_commands, :treebase ].each do |method|
45
+
46
+ define_method(method) do
47
+ self.adapter.config.send(method)
48
+ end
49
+
50
+ writer = "#{method}="
51
+ define_method(writer) do |new_value|
52
+ self.adapter.config.send(writer, new_value)
53
+ end
54
+
55
+ end
56
+
57
+ def add_attribute(attribute)
58
+ definition = AD::Framework::Config::AttributeDefinition.new(attribute)
59
+ self.mappings.add(definition.name, definition.ldap_name)
60
+ self.attributes.add(definition.name, definition)
61
+ end
62
+ def add_attribute_type(attribute_type)
63
+ self.attribute_types.add(attribute_type.key, attribute_type)
64
+ end
65
+ def add_object_class(object_class)
66
+ self.object_classes.add(object_class.ldap_name, object_class)
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,18 @@
1
+ module AD
2
+ module Framework
3
+ class Config
4
+
5
+ class AttributeDefinition
6
+ attr_accessor :name, :ldap_name, :type
7
+
8
+ def initialize(attributes)
9
+ attributes.each do |key, value|
10
+ self.send("#{key}=", value)
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ module AD
2
+ module Framework
3
+ class Config
4
+
5
+ class Mapping < Hash
6
+
7
+ def [](lookup)
8
+ super(lookup.to_sym)
9
+ end
10
+ def find(lookup)
11
+ self[lookup]
12
+ end
13
+
14
+ def []=(lookup, object)
15
+ super(lookup.to_sym, object)
16
+ end
17
+
18
+ def add(lookup, object)
19
+ self[lookup] = object
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ module AD
2
+ module Framework
3
+
4
+ class AttributeNotDefined < StandardError
5
+ end
6
+
7
+ class AttributeTypeNotDefined < StandardError
8
+ end
9
+
10
+ class ObjectClassNotDefined < StandardError
11
+ end
12
+
13
+ class EntryNotFound < StandardError
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,44 @@
1
+ module AD
2
+ module Framework
3
+
4
+ class Fields < Hash
5
+
6
+ def initialize(ldap_entry)
7
+ super()
8
+ @changed_keys = Set.new
9
+ (ldap_entry || {}).each do |ldap_name, value|
10
+ self[ldap_name.to_s] = value
11
+ end
12
+ end
13
+
14
+ def [](lookup)
15
+ super(lookup.to_s)
16
+ end
17
+ def []=(lookup, value)
18
+ if self[lookup] != value
19
+ @changed_keys << lookup.to_s
20
+ end
21
+ super(lookup.to_s, value)
22
+ end
23
+
24
+ def changes
25
+ @changed_keys.inject({}){|h, k| h.merge({ k => self[k] }) }
26
+ end
27
+
28
+ def to_hash
29
+ self.inject({}){|h, (k,v)| h.merge({ k => v }) }
30
+ end
31
+
32
+ def inspect
33
+ max_key_length = (self.keys.collect(&:size).max || 0) + 1
34
+ display = self.collect do |(key, value)|
35
+ key_label = "#{key}:".rjust(max_key_length, ' ')
36
+ [ key_label, value.inspect ].join(" ")
37
+ end
38
+ "\n#{display.join("\n")}"
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ require 'ad-framework/utilities/transaction'
2
+
3
+ module AD
4
+ module Framework
5
+ module Patterns
6
+
7
+ module Callbacks
8
+ class << self
9
+
10
+ def included(klass)
11
+ klass.class_eval do
12
+ include AD::Framework::Patterns::Callbacks::InstanceMethods
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ module InstanceMethods
19
+
20
+ def create
21
+ transaction = AD::Framework::Utilities::Transaction.new(:create, self) do
22
+ super
23
+ end
24
+ transaction.run
25
+ end
26
+
27
+ def update
28
+ transaction = AD::Framework::Utilities::Transaction.new(:update, self) do
29
+ super
30
+ end
31
+ transaction.run
32
+ end
33
+
34
+ def destroy
35
+ transaction = AD::Framework::Utilities::Transaction.new(:destroy, self) do
36
+ super
37
+ end
38
+ transaction.run
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,127 @@
1
+ require 'ad-framework/schema'
2
+
3
+ module AD
4
+ module Framework
5
+ module Patterns
6
+
7
+ module HasSchema
8
+ class << self
9
+
10
+ def included(klass)
11
+ klass.class_eval do
12
+ extend AD::Framework::Patterns::HasSchema::ClassMethods
13
+ include AD::Framework::Patterns::HasSchema::InstanceMethods
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ module InstanceMethods
20
+
21
+ def schema
22
+ @schema ||= self.class.schema.dup
23
+ end
24
+
25
+ def dn
26
+ dn = self.fields[:distinguishedname] || self.fields[:dn]
27
+ dn ||= if self.respond_to?(self.schema.rdn)
28
+ [ "CN=#{self.send(self.schema.rdn)}", self.schema.treebase ].join(", ")
29
+ end
30
+ end
31
+
32
+ def attributes
33
+ self.schema.attributes.inject({}) do |attrs, name|
34
+ attrs.merge({ name.to_sym => self.send(name.to_sym) })
35
+ end
36
+ end
37
+ def attributes=(new_attributes)
38
+ new_attributes.each do |name, value|
39
+ if self.schema.attributes.include?(name)
40
+ self.send("#{name}=", value)
41
+ end
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ module ClassMethods
48
+
49
+ def schema
50
+ @schema ||= AD::Framework::Schema.new(self)
51
+ end
52
+
53
+ def ldap_name(name = nil)
54
+ (self.schema.ldap_name = name) if name
55
+ self.schema.ldap_name
56
+ end
57
+
58
+ def treebase(value = nil)
59
+ (self.schema.treebase = value) if value
60
+ self.schema.treebase
61
+ end
62
+
63
+ def rdn(name = nil)
64
+ (self.schema.rdn = name) if name
65
+ self.schema.rdn
66
+ end
67
+
68
+ # Attributes methods
69
+ def attributes(*attribute_names)
70
+ self.schema.add_attributes(attribute_names)
71
+ end
72
+
73
+ def read_attributes(*attribute_names)
74
+ self.schema.add_read_attributes(attribute_names)
75
+ end
76
+
77
+ def write_attributes(*attribute_names)
78
+ self.schema.add_write_attributes(attribute_names)
79
+ end
80
+
81
+ # Validation methods
82
+ def must_set(*attribute_names)
83
+ self.schema.add_mandatory(attribute_names)
84
+ end
85
+
86
+ # Callbacks methods
87
+ def before_create(*method_names)
88
+ self.schema.add_callback(:before, :create, method_names)
89
+ end
90
+
91
+ def before_update(*method_names)
92
+ self.schema.add_callback(:before, :update, method_names)
93
+ end
94
+
95
+ def before_save(*method_names)
96
+ self.before_create(*method_names)
97
+ self.before_update(*method_names)
98
+ end
99
+
100
+ def before_destroy(*method_names)
101
+ self.schema.add_callback(:before, :destroy, method_names)
102
+ end
103
+
104
+ def after_create(*method_names)
105
+ self.schema.add_callback(:after, :create, method_names)
106
+ end
107
+
108
+ def after_update(*method_names)
109
+ self.schema.add_callback(:after, :update, method_names)
110
+ end
111
+
112
+ def after_save(*method_names)
113
+ self.after_create(*method_names)
114
+ self.after_update(*method_names)
115
+ end
116
+
117
+ def after_destroy(*method_names)
118
+ self.schema.add_callback(:after, :destroy, method_names)
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+ end
127
+ end