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.
- data/.gitignore +6 -0
- data/Gemfile +12 -0
- data/README.markdown +36 -0
- data/Rakefile +19 -0
- data/ad-framework.gemspec +25 -0
- data/doc/open_ldap_server.markdown +17 -0
- data/extras/adtest.schema +304 -0
- data/extras/slapd.conf +10 -0
- data/lib/ad-framework.rb +53 -0
- data/lib/ad-framework/attribute.rb +35 -0
- data/lib/ad-framework/attribute_type.rb +133 -0
- data/lib/ad-framework/auxiliary_class.rb +24 -0
- data/lib/ad-framework/config.rb +72 -0
- data/lib/ad-framework/config/attribute_definition.rb +18 -0
- data/lib/ad-framework/config/mapping.rb +26 -0
- data/lib/ad-framework/exceptions.rb +17 -0
- data/lib/ad-framework/fields.rb +44 -0
- data/lib/ad-framework/patterns/callbacks.rb +47 -0
- data/lib/ad-framework/patterns/has_schema.rb +127 -0
- data/lib/ad-framework/patterns/persistence.rb +67 -0
- data/lib/ad-framework/patterns/searchable.rb +117 -0
- data/lib/ad-framework/patterns/validations.rb +50 -0
- data/lib/ad-framework/schema.rb +118 -0
- data/lib/ad-framework/structural_class.rb +61 -0
- data/lib/ad-framework/utilities/entry_builder.rb +77 -0
- data/lib/ad-framework/utilities/transaction.rb +32 -0
- data/lib/ad-framework/utilities/validator.rb +26 -0
- data/lib/ad-framework/version.rb +5 -0
- data/test/helper.rb +71 -0
- data/test/integration/defined_array_test.rb +49 -0
- data/test/integration/defined_integer_test.rb +48 -0
- data/test/integration/defined_string_test.rb +48 -0
- data/test/integration/defined_top_test.rb +101 -0
- data/test/integration/defined_user_test.rb +140 -0
- data/test/irb.rb +2 -0
- data/test/support/factory.rb +67 -0
- data/test/support/ldap.yml +6 -0
- data/test/support/schema/attribute_types.rb +67 -0
- data/test/support/schema/attributes.rb +10 -0
- data/test/support/schema/auxiliary_classes.rb +12 -0
- data/test/support/schema/structural_classes.rb +46 -0
- data/test/support/seed.rb +28 -0
- data/test/support/state.rb +29 -0
- data/test/unit/ad-framework/attribute_test.rb +84 -0
- data/test/unit/ad-framework/attribute_type/class_methods_test.rb +146 -0
- data/test/unit/ad-framework/attribute_type_test.rb +114 -0
- data/test/unit/ad-framework/auxiliary_class_test.rb +39 -0
- data/test/unit/ad-framework/config/attribute_definition_test.rb +26 -0
- data/test/unit/ad-framework/config/mapping_test.rb +41 -0
- data/test/unit/ad-framework/config_test.rb +121 -0
- data/test/unit/ad-framework/fields_test.rb +44 -0
- data/test/unit/ad-framework/patterns/callbacks_test.rb +90 -0
- data/test/unit/ad-framework/patterns/has_schema/class_methods_test.rb +214 -0
- data/test/unit/ad-framework/patterns/has_schema_test.rb +96 -0
- data/test/unit/ad-framework/patterns/persistence_test.rb +126 -0
- data/test/unit/ad-framework/patterns/searchable_test.rb +201 -0
- data/test/unit/ad-framework/patterns/validations_test.rb +113 -0
- data/test/unit/ad-framework/schema_test.rb +268 -0
- data/test/unit/ad-framework/structural_class_test.rb +64 -0
- data/test/unit/ad-framework/utilities/entry_builder_test.rb +107 -0
- data/test/unit/ad-framework/utilities/transaction_test.rb +50 -0
- data/test/unit/ad-framework/utilities/validator_test.rb +46 -0
- data/test/unit/ad-framework_test.rb +116 -0
- 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
|