arborist 0.0.1.pre20160128152542 → 0.0.1.pre20160606141735
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/ChangeLog +426 -1
- data/Manifest.txt +17 -2
- data/Nodes.md +70 -0
- data/Protocol.md +68 -9
- data/README.md +3 -5
- data/Rakefile +4 -1
- data/TODO.md +52 -20
- data/lib/arborist.rb +19 -6
- data/lib/arborist/cli.rb +39 -25
- data/lib/arborist/client.rb +97 -4
- data/lib/arborist/command/client.rb +2 -1
- data/lib/arborist/command/start.rb +51 -5
- data/lib/arborist/dependency.rb +286 -0
- data/lib/arborist/event.rb +7 -2
- data/lib/arborist/event/{node_matching.rb → node.rb} +11 -5
- data/lib/arborist/event/node_acked.rb +5 -7
- data/lib/arborist/event/node_delta.rb +30 -3
- data/lib/arborist/event/node_disabled.rb +16 -0
- data/lib/arborist/event/node_down.rb +10 -0
- data/lib/arborist/event/node_quieted.rb +11 -0
- data/lib/arborist/event/node_unknown.rb +10 -0
- data/lib/arborist/event/node_up.rb +10 -0
- data/lib/arborist/event/node_update.rb +2 -11
- data/lib/arborist/event/sys_node_added.rb +10 -0
- data/lib/arborist/event/sys_node_removed.rb +10 -0
- data/lib/arborist/exceptions.rb +4 -0
- data/lib/arborist/manager.rb +188 -18
- data/lib/arborist/manager/event_publisher.rb +1 -1
- data/lib/arborist/manager/tree_api.rb +92 -13
- data/lib/arborist/mixins.rb +17 -0
- data/lib/arborist/monitor.rb +10 -1
- data/lib/arborist/monitor/socket.rb +123 -2
- data/lib/arborist/monitor_runner.rb +6 -5
- data/lib/arborist/node.rb +420 -94
- data/lib/arborist/node/ack.rb +72 -0
- data/lib/arborist/node/host.rb +43 -8
- data/lib/arborist/node/resource.rb +73 -0
- data/lib/arborist/node/root.rb +6 -0
- data/lib/arborist/node/service.rb +89 -22
- data/lib/arborist/observer.rb +1 -1
- data/lib/arborist/subscription.rb +11 -6
- data/spec/arborist/client_spec.rb +93 -5
- data/spec/arborist/dependency_spec.rb +375 -0
- data/spec/arborist/event/node_delta_spec.rb +66 -0
- data/spec/arborist/event/node_down_spec.rb +84 -0
- data/spec/arborist/event/node_spec.rb +59 -0
- data/spec/arborist/event/node_update_spec.rb +14 -3
- data/spec/arborist/event_spec.rb +3 -3
- data/spec/arborist/manager/tree_api_spec.rb +295 -3
- data/spec/arborist/manager_spec.rb +240 -57
- data/spec/arborist/monitor_spec.rb +26 -3
- data/spec/arborist/node/ack_spec.rb +74 -0
- data/spec/arborist/node/host_spec.rb +79 -0
- data/spec/arborist/node/resource_spec.rb +56 -0
- data/spec/arborist/node/service_spec.rb +68 -2
- data/spec/arborist/node_spec.rb +288 -11
- data/spec/arborist/subscription_spec.rb +23 -14
- data/spec/arborist_spec.rb +0 -4
- data/spec/data/observers/webservices.rb +10 -2
- data/spec/spec_helper.rb +8 -0
- metadata +58 -15
- metadata.gz.sig +0 -0
- data/LICENSE +0 -29
data/lib/arborist/client.rb
CHANGED
@@ -58,10 +58,11 @@ class Arborist::Client
|
|
58
58
|
|
59
59
|
|
60
60
|
### Return the manager's current node tree.
|
61
|
-
def make_list_request( from: nil )
|
61
|
+
def make_list_request( from: nil, depth: nil )
|
62
62
|
header = {}
|
63
63
|
self.log.debug "From is: %p" % [ from ]
|
64
64
|
header[:from] = from if from
|
65
|
+
header[:depth] = depth if depth
|
65
66
|
|
66
67
|
return self.pack_message( :list, header )
|
67
68
|
end
|
@@ -75,19 +76,54 @@ class Arborist::Client
|
|
75
76
|
|
76
77
|
|
77
78
|
### Return the manager's current node tree.
|
78
|
-
def make_fetch_request( criteria, include_down: false, properties: :all )
|
79
|
+
def make_fetch_request( criteria, include_down: false, properties: :all, exclude: {} )
|
79
80
|
header = {}
|
80
81
|
header[ :include_down ] = true if include_down
|
81
82
|
header[ :return ] = properties if properties != :all
|
82
83
|
|
83
|
-
return self.pack_message( :fetch, header, criteria )
|
84
|
+
return self.pack_message( :fetch, header, [ criteria, exclude ] )
|
84
85
|
end
|
85
86
|
|
86
87
|
|
88
|
+
### Mark a node as 'acknowledged' if it's down, or 'disabled' if
|
89
|
+
### it's up. (A pre-emptive acknowledgement.) Requires the node
|
90
|
+
### +identifier+, an acknowledgement +message+, and +sender+. You
|
91
|
+
### can optionally include a +via+ (source), and override the default
|
92
|
+
### +time+ of now.
|
93
|
+
def acknowledge( node, message, sender, via=nil, time=Time.now )
|
94
|
+
data = {
|
95
|
+
node => {
|
96
|
+
ack: {
|
97
|
+
message: message,
|
98
|
+
sender: sender,
|
99
|
+
via: via,
|
100
|
+
time: time.to_s
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
request = self.make_update_request( data )
|
106
|
+
self.send_tree_api_request( request )
|
107
|
+
return true
|
108
|
+
end
|
109
|
+
alias_method :ack, :acknowledge
|
110
|
+
|
111
|
+
|
112
|
+
### Clear an acknowledged/disabled +node+.
|
113
|
+
def clear_acknowledgement( node )
|
114
|
+
data = { node => { ack: nil } }
|
115
|
+
request = self.make_update_request( data )
|
116
|
+
self.send_tree_api_request( request )
|
117
|
+
return true
|
118
|
+
end
|
119
|
+
alias_method :clear_ack, :clear_acknowledgement
|
120
|
+
|
121
|
+
|
87
122
|
### Update the identified nodes in the manager with the specified data.
|
88
123
|
def update( *args )
|
89
124
|
request = self.make_update_request( *args )
|
90
|
-
|
125
|
+
self.send_tree_api_request( request )
|
126
|
+
return true
|
91
127
|
end
|
92
128
|
|
93
129
|
|
@@ -133,6 +169,63 @@ class Arborist::Client
|
|
133
169
|
end
|
134
170
|
|
135
171
|
|
172
|
+
### Remove a node
|
173
|
+
def prune( *args )
|
174
|
+
request = self.make_prune_request( *args )
|
175
|
+
response = self.send_tree_api_request( request )
|
176
|
+
return response
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
### Remove the node with the specified +identfier+.
|
181
|
+
def make_prune_request( identifier )
|
182
|
+
self.log.debug "Making prune request for identifier: %s" % [ identifier ]
|
183
|
+
|
184
|
+
return self.pack_message( :prune, identifier: identifier )
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
### Add a new node to the tree.
|
189
|
+
def graft( *args )
|
190
|
+
request = self.make_graft_request( *args )
|
191
|
+
response = self.send_tree_api_request( request )
|
192
|
+
return response
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
### Add a node with the specified +identifier+ and +arguments+.
|
197
|
+
def make_graft_request( identifier, attributes={} )
|
198
|
+
self.log.debug "Making graft request for identifer: %s" % [ identifier ]
|
199
|
+
|
200
|
+
parent = attributes.delete( :parent )
|
201
|
+
type = attributes.delete( :type )
|
202
|
+
|
203
|
+
header = {
|
204
|
+
identifier: identifier,
|
205
|
+
parent: parent,
|
206
|
+
type: type
|
207
|
+
}
|
208
|
+
|
209
|
+
return self.pack_message( :graft, header, attributes )
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
### Modify operational attributes of a node.
|
214
|
+
def modify( *args )
|
215
|
+
request = self.make_modify_request( *args )
|
216
|
+
response = self.send_tree_api_request( request )
|
217
|
+
return true
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
### Modify the operations +attributes+ of the node with the specified +identifier+.
|
222
|
+
def make_modify_request( identifier, attributes={} )
|
223
|
+
self.log.debug "Making modify request for identifer: %s" % [ identifier ]
|
224
|
+
|
225
|
+
return self.pack_message( :modify, {identifier: identifier}, attributes )
|
226
|
+
end
|
227
|
+
|
228
|
+
|
136
229
|
### Send the packed +request+ via the Tree API socket, raise an error on
|
137
230
|
### unsuccessful response, and return the response body.
|
138
231
|
def send_tree_api_request( request )
|
@@ -22,11 +22,15 @@ module Arborist::CLI::Start
|
|
22
22
|
cmd.flag :loader, desc: "Specify a loader type to use.",
|
23
23
|
default_value: 'file'
|
24
24
|
|
25
|
+
cmd.desc "Run under the profiler in the given MODE (one of wall, cpu, or object; defaults to wall)."
|
26
|
+
cmd.arg_name :MODE
|
27
|
+
cmd.flag [:p, 'profiler'], must_match: ['wall', 'cpu', 'object']
|
28
|
+
|
25
29
|
cmd.action do |globals, options, args|
|
26
30
|
appname = args.shift
|
27
31
|
source = args.shift
|
28
32
|
|
29
|
-
loader = Arborist::Loader.create( options
|
33
|
+
loader = Arborist::Loader.create( options[:loader], source )
|
30
34
|
runner = case appname
|
31
35
|
when 'manager'
|
32
36
|
Arborist.manager_for( loader )
|
@@ -39,7 +43,7 @@ module Arborist::CLI::Start
|
|
39
43
|
end
|
40
44
|
|
41
45
|
unless_dryrun( "starting #{appname}" ) do
|
42
|
-
start( runner )
|
46
|
+
start( runner, options[:p] )
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
@@ -51,10 +55,52 @@ module Arborist::CLI::Start
|
|
51
55
|
|
52
56
|
### Start the specified +runner+ instance after setting up the environment for
|
53
57
|
### it.
|
54
|
-
def start( runner )
|
55
|
-
|
56
|
-
|
58
|
+
def start( runner, profile_mode=nil )
|
59
|
+
Process.setproctitle( runner.class.name )
|
60
|
+
|
61
|
+
if profile_mode
|
62
|
+
self.with_profiling_enabled( profile_mode, runner ) do
|
63
|
+
runner.run
|
64
|
+
end
|
65
|
+
else
|
66
|
+
runner.run
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
### Wrap the profiler around the specified +callable+.
|
72
|
+
def self::with_profiling_enabled( profile_arg, runner, &block )
|
73
|
+
require 'stackprof'
|
74
|
+
mode, outfile = self.parse_profile_args( profile_arg, runner )
|
75
|
+
|
76
|
+
self.log.info "Profiling in %s mode, outputting to %s" % [ mode, outfile ]
|
77
|
+
StackProf.run( mode: mode.to_sym, out: outfile, &block )
|
78
|
+
rescue LoadError => err
|
79
|
+
self.log.debug "%p while loading the StackProf profiler: %s"
|
80
|
+
exit_now!( "Couldn't load the profiler; you probably need to `gem install stackprof`", 254 )
|
57
81
|
end
|
58
82
|
|
83
|
+
|
84
|
+
### Set up the StackProf profiler to run in the given +mode+.
|
85
|
+
def self::parse_profile_args( arg, runner )
|
86
|
+
profile_mode, profile_filename = arg.split( ':', 2 )
|
87
|
+
profile_filename ||= self.default_profile_filename( profile_mode, runner )
|
88
|
+
|
89
|
+
return profile_mode, profile_filename
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
### Return a filename for a StackProf profile run over the given +runner+.
|
94
|
+
def self::default_profile_filename( mode, runner )
|
95
|
+
basename = runner.class.name.gsub( /.*::/, '' )
|
96
|
+
return "%s-%s-%s.%d.dump" % [
|
97
|
+
basename,
|
98
|
+
mode,
|
99
|
+
Time.now.strftime('%Y%m%d%H%M%S'),
|
100
|
+
Process.pid,
|
101
|
+
]
|
102
|
+
end
|
103
|
+
|
104
|
+
|
59
105
|
end # module Arborist::CLI::Start
|
60
106
|
|
@@ -0,0 +1,286 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'set'
|
5
|
+
require 'time'
|
6
|
+
require 'loggability'
|
7
|
+
|
8
|
+
require 'arborist' unless defined?( Arborist )
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
# A inter-node dependency that is outside of the implicit ones expressed by the
|
13
|
+
# tree.
|
14
|
+
class Arborist::Dependency
|
15
|
+
extend Loggability
|
16
|
+
|
17
|
+
|
18
|
+
# Loggability API -- log to the Arborist logger
|
19
|
+
log_to :arborist
|
20
|
+
|
21
|
+
|
22
|
+
### Construct a new Dependency for the specified +behavior+ on the given +identifiers+
|
23
|
+
### with +prefixes+.
|
24
|
+
def self::on( behavior, *identifiers, prefixes: nil )
|
25
|
+
deps, identifiers = identifiers.flatten.uniq.partition {|obj| obj.is_a?(self.class) }
|
26
|
+
prefixes = Array( prefixes ).uniq
|
27
|
+
identifiers = prefixes.product( identifiers ).map {|pair| pair.join('-') } unless
|
28
|
+
prefixes.empty?
|
29
|
+
|
30
|
+
return self.new( behavior, identifiers + deps )
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
### Construct a new instance using the specified +hash+, which should be in the same form
|
35
|
+
### as that generated by #to_h:
|
36
|
+
###
|
37
|
+
### {
|
38
|
+
### behavior: <string>,
|
39
|
+
### identifiers: [<identifier_1>, <identifier_n>],
|
40
|
+
### subdeps: [<dephash_1>, <dephash_n>],
|
41
|
+
### }
|
42
|
+
###
|
43
|
+
def self::from_hash( hash )
|
44
|
+
self.log.debug "Creating a new %p from a hash: %p" % [ self, hash ]
|
45
|
+
|
46
|
+
hash[:subdeps] ||= []
|
47
|
+
subdeps = hash[:subdeps].map {|subhash| self.from_hash(subhash) }
|
48
|
+
|
49
|
+
return self.new( hash[:behavior], hash[:identifiers] + subdeps )
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
### Create a new Dependency on the specified +nodes_or_subdeps+ with the given +behavior+
|
54
|
+
### (one of :any or :all)
|
55
|
+
def initialize( behavior, *nodes_or_subdeps )
|
56
|
+
@behavior = behavior
|
57
|
+
@subdeps, identifiers = nodes_or_subdeps.flatten.
|
58
|
+
partition {|obj| obj.is_a?(self.class) }
|
59
|
+
@identifier_states = identifiers.product([ nil ]).to_h
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
### Dup constructor -- dup internal datastructures without ephemeral state on #dup.
|
64
|
+
def initialize_dup( original ) # :nodoc:
|
65
|
+
@subdeps = @subdeps.map( &:dup )
|
66
|
+
@identifier_states = @identifier_states.keys.product([ nil ]).to_h
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
### Clone constructor -- clone internal datastructures without ephemeral state on #clone.
|
71
|
+
def initialize_clone( original ) # :nodoc:
|
72
|
+
@subdeps = @subdeps.map( &:clone )
|
73
|
+
@identifier_states = @identifier_states.keys.product([ nil ]).to_h
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
######
|
78
|
+
public
|
79
|
+
######
|
80
|
+
|
81
|
+
##
|
82
|
+
# The behavior that determines if the dependency is met by any or all of the
|
83
|
+
# nodes.
|
84
|
+
attr_reader :behavior
|
85
|
+
|
86
|
+
##
|
87
|
+
# The Hash of identifier states
|
88
|
+
attr_reader :identifier_states
|
89
|
+
|
90
|
+
##
|
91
|
+
# The Array of sub-dependencies (instances of Dependency).
|
92
|
+
attr_reader :subdeps
|
93
|
+
|
94
|
+
|
95
|
+
### Return a Set of identifiers belonging to this dependency.
|
96
|
+
def identifiers
|
97
|
+
return Set.new( self.identifier_states.keys )
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
### Return a Set of identifiers which have been marked down in this dependency.
|
102
|
+
def down_identifiers
|
103
|
+
return Set.new( self.identifier_states.select {|_, mark| mark }.map(&:first) )
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
### Return a Set of identifiers which have not been marked down in this dependency.
|
108
|
+
def up_identifiers
|
109
|
+
return Set.new( self.identifier_states.reject {|_, mark| mark }.map(&:first) )
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
### Return a Set of identifiers for all of this Dependency's sub-dependencies.
|
114
|
+
def subdep_identifiers
|
115
|
+
return self.subdeps.map( &:all_identifiers ).reduce( :+ ) || Set.new
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
### Return the Set of this Dependency's identifiers as well as those of all of its
|
120
|
+
### sub-dependencies.
|
121
|
+
def all_identifiers
|
122
|
+
return self.identifiers + self.subdep_identifiers
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
### Return any of this dependency's sub-dependencies that are down.
|
127
|
+
def down_subdeps
|
128
|
+
return self.subdeps.select( &:down? )
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
### Return any of this dependency's sub-dependencies that are up.
|
133
|
+
def up_subdeps
|
134
|
+
return self.subdeps.select( &:up? )
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
### Yield each unique identifier and Time of downed nodes from both direct and
|
139
|
+
### sub-dependencies.
|
140
|
+
def each_downed
|
141
|
+
return enum_for( __method__ ) unless block_given?
|
142
|
+
|
143
|
+
yielded = Set.new
|
144
|
+
self.identifier_states.each do |ident, time|
|
145
|
+
if time
|
146
|
+
yield( ident, time ) unless yielded.include?( ident )
|
147
|
+
yielded.add( ident )
|
148
|
+
end
|
149
|
+
end
|
150
|
+
self.subdeps.each do |subdep|
|
151
|
+
subdep.each_downed do |ident, time|
|
152
|
+
if time
|
153
|
+
yield( ident, time ) unless yielded.include?( ident )
|
154
|
+
yielded.add( ident )
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
### Returns +true+ if the receiver includes all of the given +identifiers+.
|
162
|
+
def include?( *identifiers )
|
163
|
+
return self.all_identifiers.include?( *identifiers )
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
### Returns +true+ if this dependency doesn't contain any identifiers or
|
168
|
+
### sub-dependencies.
|
169
|
+
def empty?
|
170
|
+
return self.all_identifiers.empty?
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
### Mark the specified +identifier+ as being down and propagate it to any subdependencies.
|
175
|
+
def mark_down( identifier, time=Time.now )
|
176
|
+
self.identifier_states[ identifier ] = time if self.identifier_states.key?( identifier )
|
177
|
+
self.subdeps.each do |dep|
|
178
|
+
dep.mark_down( identifier, time )
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
### Mark the specified +identifier+ as being up and propagate it to any subdependencies.
|
184
|
+
def mark_up( identifier )
|
185
|
+
self.subdeps.each do |dep|
|
186
|
+
dep.mark_up( identifier )
|
187
|
+
end
|
188
|
+
self.identifier_states[ identifier ] = nil if self.identifier_states.key?( identifier )
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
### Returns +true+ if this dependency cannot be met.
|
193
|
+
def down?
|
194
|
+
case self.behavior
|
195
|
+
when :all
|
196
|
+
self.identifier_states.values.any? || self.subdeps.any?( &:down? )
|
197
|
+
when :any
|
198
|
+
self.identifier_states.values.all? && self.subdeps.all?( &:down? )
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
### Returns +true+ if this dependency is met.
|
204
|
+
def up?
|
205
|
+
return !self.down?
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
### Returns the earliest Time a node was marked down.
|
210
|
+
def earliest_down_time
|
211
|
+
return self.identifier_states.values.compact.min
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
### Returns the latest Time a node was marked down.
|
216
|
+
def latest_down_time
|
217
|
+
return self.identifier_states.values.compact.max
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
### Return an English description of why this dependency is not met. If it is
|
222
|
+
### met, returns +nil+.
|
223
|
+
def down_reason
|
224
|
+
ids = self.down_identifiers
|
225
|
+
subdeps = self.down_subdeps
|
226
|
+
|
227
|
+
return nil if ids.empty? && subdeps.empty?
|
228
|
+
|
229
|
+
msg = nil
|
230
|
+
case self.behavior
|
231
|
+
when :all
|
232
|
+
msg = ids.first.dup
|
233
|
+
if ids.size == 1
|
234
|
+
msg << " is down"
|
235
|
+
else
|
236
|
+
msg << " (and %d other%s) are down" % [ ids.size - 1, ids.size == 2 ? '' : 's' ]
|
237
|
+
end
|
238
|
+
|
239
|
+
msg << " as of %s" % [ self.earliest_down_time ]
|
240
|
+
|
241
|
+
when :any
|
242
|
+
msg = "%s are all down" % [ ids.to_a.join(', ') ]
|
243
|
+
msg << " as of %s" % [ self.latest_down_time ]
|
244
|
+
|
245
|
+
else
|
246
|
+
raise "Don't know how to build a description of down behavior for %p" % [ self.behavior ]
|
247
|
+
end
|
248
|
+
|
249
|
+
return msg
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
### Return the entire dependency tree as a nested Hash.
|
254
|
+
def to_h
|
255
|
+
return {
|
256
|
+
behavior: self.behavior,
|
257
|
+
identifiers: self.identifier_states.keys,
|
258
|
+
subdeps: self.subdeps.map( &:to_h )
|
259
|
+
}
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
### Returns true if +other+ is the same object or if they both have the same
|
264
|
+
### identifiers, sub-dependencies, and identifier states.
|
265
|
+
def eql?( other )
|
266
|
+
self.log.debug "Comparing %p to %p (with states)" % [ self, other ]
|
267
|
+
return true if other.equal?( self )
|
268
|
+
return self == other &&
|
269
|
+
self.identifier_states.eql?( other.identifier_states ) &&
|
270
|
+
self.subdeps.eql?( other.subdeps )
|
271
|
+
end
|
272
|
+
|
273
|
+
|
274
|
+
### Equality comparison operator -- return true if the +other+ dependency has the same
|
275
|
+
### behavior, identifiers, and sub-dependencies. Does not consider identifier states.
|
276
|
+
def ==( other )
|
277
|
+
return true if other.equal?( self )
|
278
|
+
return false unless other.is_a?( self.class )
|
279
|
+
|
280
|
+
return self.behavior == other.behavior &&
|
281
|
+
self.identifiers == other.identifiers &&
|
282
|
+
self.subdeps == other.subdeps
|
283
|
+
end
|
284
|
+
|
285
|
+
end # class Arborist::Dependency
|
286
|
+
|