conjur-api 4.14.0 → 4.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +4 -0
- data/lib/conjur-api/version.rb +1 -1
- data/lib/conjur/acts_as_asset.rb +44 -3
- data/lib/conjur/acts_as_resource.rb +53 -4
- data/lib/conjur/acts_as_user.rb +17 -7
- data/lib/conjur/annotations.rb +49 -3
- data/lib/conjur/api.rb +30 -3
- data/lib/conjur/api/deputies.rb +25 -1
- data/lib/conjur/api/resources.rb +109 -5
- data/lib/conjur/api/roles.rb +103 -11
- data/lib/conjur/api/secrets.rb +16 -1
- data/lib/conjur/api/users.rb +65 -1
- data/lib/conjur/api/variables.rb +65 -1
- data/lib/conjur/audit-api.rb +3 -0
- data/lib/conjur/authn-api.rb +4 -0
- data/lib/conjur/authz-api.rb +4 -0
- data/lib/conjur/base.rb +31 -30
- data/lib/conjur/build_from_response.rb +11 -0
- data/lib/conjur/cast.rb +5 -1
- data/lib/conjur/core-api.rb +22 -2
- data/lib/conjur/deputy.rb +19 -2
- data/lib/conjur/env.rb +18 -3
- data/lib/conjur/escape.rb +65 -4
- data/lib/conjur/event_source.rb +15 -2
- data/lib/conjur/graph.rb +103 -12
- data/lib/conjur/has_id.rb +13 -1
- data/lib/conjur/has_identifier.rb +9 -6
- data/lib/conjur/has_owner.rb +21 -7
- data/lib/conjur/host.rb +8 -0
- data/lib/conjur/layer-api.rb +4 -0
- data/lib/conjur/layer.rb +50 -3
- data/lib/conjur/log.rb +22 -2
- data/lib/conjur/log_source.rb +27 -0
- data/lib/conjur/path_based.rb +47 -2
- data/lib/conjur/pubkeys-api.rb +12 -0
- data/lib/conjur/role.rb +220 -9
- data/lib/conjur/role_grant.rb +50 -2
- data/lib/conjur/secret.rb +9 -1
- data/lib/conjur/standard_methods.rb +31 -3
- data/lib/conjur/user.rb +55 -3
- data/spec/lib/role_spec.rb +1 -2
- metadata +2 -2
@@ -19,7 +19,18 @@
|
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
21
|
module Conjur
|
22
|
+
# @api private
|
23
|
+
# This module is included by classes that can be built from JSON responses.
|
22
24
|
module BuildFromResponse
|
25
|
+
# @api private
|
26
|
+
#
|
27
|
+
# Build a Conjur asset from a REST response.
|
28
|
+
#
|
29
|
+
# @param [RestCliet::Response] response the response to build the object from
|
30
|
+
# @param [Hash] credentials options as {Conjur::API#credentials} used to perform requests in methods on
|
31
|
+
# the created asset.
|
32
|
+
#
|
33
|
+
# @return [Object] an object of this type
|
23
34
|
def build_from_response(response, credentials)
|
24
35
|
new(response.headers[:location], credentials).tap do |obj|
|
25
36
|
obj.attributes = JSON.parse(response.body)
|
data/lib/conjur/cast.rb
CHANGED
@@ -21,7 +21,11 @@
|
|
21
21
|
module Conjur
|
22
22
|
module Cast
|
23
23
|
protected
|
24
|
-
|
24
|
+
|
25
|
+
# Convert a value to a role or resource identifier.
|
26
|
+
#
|
27
|
+
# @param [String, Array, #roleid, #resourceid] obj the value to cast
|
28
|
+
# @param [Symbol] kind must be either `:roleid` or `:resourceid`
|
25
29
|
def cast(obj, kind)
|
26
30
|
case kind
|
27
31
|
when :roleid, :resourceid
|
data/lib/conjur/core-api.rb
CHANGED
@@ -21,6 +21,11 @@
|
|
21
21
|
module Conjur
|
22
22
|
class API
|
23
23
|
class << self
|
24
|
+
# @api private
|
25
|
+
#
|
26
|
+
# Host for the core service. We don't really use this anymore.
|
27
|
+
#
|
28
|
+
# @return [String] the core asset host
|
24
29
|
def core_asset_host
|
25
30
|
::Conjur::Core::API.host
|
26
31
|
end
|
@@ -30,14 +35,29 @@ module Conjur
|
|
30
35
|
module Core
|
31
36
|
class API < Conjur::API
|
32
37
|
class << self
|
38
|
+
# @api private
|
39
|
+
# @deprecated
|
40
|
+
# The host for the Conjur directory service
|
41
|
+
# @return [String] the host.
|
33
42
|
def host
|
34
43
|
Conjur.configuration.core_url
|
35
44
|
end
|
36
|
-
|
45
|
+
|
46
|
+
# Returns the account as determined by the conjur server.
|
47
|
+
#
|
48
|
+
# You should generally provide the account with {Conjur::Configuration#account}, but this method
|
49
|
+
# can determine it by asking the server.
|
50
|
+
#
|
51
|
+
# You do not need any credentials to call this method.
|
37
52
|
def conjur_account
|
38
53
|
info['account'] or raise "No account field in #{info.inspect}"
|
39
54
|
end
|
40
|
-
|
55
|
+
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
# Used to fetch an `info` hash from the server.
|
59
|
+
#
|
60
|
+
# @return [Hash] a hash containing an `'account'` field that specifies the current Conjur account.
|
41
61
|
def info
|
42
62
|
@info ||= JSON.parse RestClient::Resource.new(Conjur::Core::API.host)['info'].get
|
43
63
|
end
|
data/lib/conjur/deputy.rb
CHANGED
@@ -19,6 +19,14 @@
|
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
21
|
module Conjur
|
22
|
+
# A Deputy is an actor, typically representing a service. It is given a login and
|
23
|
+
# an api key, just like {Conjur::Host}s and {Conjur::User}s, and can perform various
|
24
|
+
# actions.
|
25
|
+
#
|
26
|
+
# You should not create instances of this class directly. Instead, you can get a {Conjur::Deputy}
|
27
|
+
# instance with {Conjur::API#deputy} or {Conjur::API#create_deputy}.
|
28
|
+
#
|
29
|
+
# The deputies api is stable, but is primarily used internally.
|
22
30
|
class Deputy < RestClient::Resource
|
23
31
|
include Exists
|
24
32
|
include HasId
|
@@ -26,11 +34,20 @@ module Conjur
|
|
26
34
|
include HasAttributes
|
27
35
|
include ActsAsUser
|
28
36
|
include ActsAsResource
|
29
|
-
|
37
|
+
|
38
|
+
# Login for the deputy. Of the form "deputy/<deputy-id>".
|
39
|
+
#
|
40
|
+
# @return [String] the login.
|
30
41
|
def login
|
31
42
|
[ self.class.name.split('::')[-1].downcase, id ].join('/')
|
32
43
|
end
|
33
|
-
|
44
|
+
|
45
|
+
# API Key that can be used to login as the deputy.
|
46
|
+
#
|
47
|
+
# This is only available if the {Conjur::Deputy} was returned
|
48
|
+
# by {Conjur::API#create_deputy}.
|
49
|
+
#
|
50
|
+
# @return [String] the api key.
|
34
51
|
def api_key
|
35
52
|
self.attributes['api_key']
|
36
53
|
end
|
data/lib/conjur/env.rb
CHANGED
@@ -21,18 +21,33 @@
|
|
21
21
|
module Conjur
|
22
22
|
extend self
|
23
23
|
|
24
|
+
# @deprecated
|
25
|
+
# @api private
|
26
|
+
# This method delegates to {Conjur::Configuration#service_base_port}
|
27
|
+
#
|
28
|
+
# @return [Integer] the service base port
|
24
29
|
def service_base_port
|
25
30
|
Conjur.configuration.service_base_port
|
26
31
|
end
|
27
|
-
|
32
|
+
|
33
|
+
# This method delegates to {Conjur::Configuration#account}
|
34
|
+
#
|
35
|
+
# @return [String] the value of `Conjur.configuration.account`
|
28
36
|
def account
|
29
37
|
Conjur.configuration.account
|
30
38
|
end
|
31
|
-
|
39
|
+
|
40
|
+
# This method delegates to {Conjur::Configuration#env}
|
41
|
+
# @return [String] the value of `Conjur.configuration.env`
|
32
42
|
def env
|
33
43
|
Conjur.configuration.env
|
34
44
|
end
|
35
|
-
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
# @deprecated
|
48
|
+
# This method delegates to {Conjur::Configuration#stack}
|
49
|
+
#
|
50
|
+
# @return [String] the value of `Conjur.configuration.stack`
|
36
51
|
def stack
|
37
52
|
Conjur.configuration.stack
|
38
53
|
end
|
data/lib/conjur/escape.rb
CHANGED
@@ -19,21 +19,56 @@
|
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
21
|
module Conjur
|
22
|
+
|
23
|
+
# Provides helpers for escaping url components.
|
24
|
+
#
|
25
|
+
# The helpers are added as both class and isntance methods.
|
22
26
|
module Escape
|
23
27
|
module ClassMethods
|
28
|
+
# URL escape the entire string. This is essentially the same as calling `CGI.escape str`.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# fully_escape 'foo/bar@baz'
|
32
|
+
# # => "foo%2Fbar%40baz"
|
33
|
+
#
|
34
|
+
# @param [String] str the string to escape
|
35
|
+
# @return [String] the escaped string
|
24
36
|
def fully_escape(str)
|
25
37
|
require 'cgi'
|
26
38
|
CGI.escape(str.to_s)
|
27
39
|
end
|
28
|
-
|
40
|
+
|
41
|
+
# Escape a URI path component.
|
42
|
+
#
|
43
|
+
# This method simply calls {Conjur::Escape::ClassMethods#path_or_query_escape}.
|
44
|
+
#
|
45
|
+
# @param [String] str the string to escape
|
46
|
+
# @return [String] the escaped string
|
47
|
+
# @see Conjur::Escape::ClassMethods#path_or_query_escape
|
29
48
|
def path_escape(str)
|
30
49
|
path_or_query_escape str
|
31
50
|
end
|
32
51
|
|
52
|
+
# Escape a URI query value.
|
53
|
+
#
|
54
|
+
# This method simply calls {Conjur::Escape::ClassMethods#path_or_query_escape}.
|
55
|
+
#
|
56
|
+
# @param [String] str the string to escape
|
57
|
+
# @return [String] the escaped string
|
58
|
+
# @see Conjur::Escape::ClassMethods#path_or_query_escape
|
33
59
|
def query_escape(str)
|
34
60
|
path_or_query_escape str
|
35
61
|
end
|
36
|
-
|
62
|
+
|
63
|
+
# Escape a path or query value.
|
64
|
+
#
|
65
|
+
# This method is *similar* to `URI.escape`, but it has several important differences:
|
66
|
+
# * If a falsey value is given, the string `"false"` is returned.
|
67
|
+
# * If the value given responds to `#id`, the value returned by `str.id` is escaped instead.
|
68
|
+
# * The value is escaped without modifying `':'` or `'/'`.
|
69
|
+
#
|
70
|
+
# @param [String, FalseClass, NilClass, #id] str the value to escape
|
71
|
+
# @return [String] the value escaped as described
|
37
72
|
def path_or_query_escape(str)
|
38
73
|
return "false" unless str
|
39
74
|
str = str.id if str.respond_to?(:id)
|
@@ -43,19 +78,45 @@ module Conjur
|
|
43
78
|
URI.escape(str.to_s, Regexp.new("[^#{pattern}]"))
|
44
79
|
end
|
45
80
|
end
|
46
|
-
|
81
|
+
|
82
|
+
# @api private
|
83
|
+
# :nodoc:
|
47
84
|
def self.included(base)
|
48
85
|
base.extend ClassMethods
|
49
86
|
end
|
50
|
-
|
87
|
+
|
88
|
+
# URL escape the entire string. This is essentially the same as calling `CGI.escape str`.
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# fully_escape 'foo/bar@baz'
|
92
|
+
# # => "foo%2Fbar%40baz"
|
93
|
+
#
|
94
|
+
# @param [String] str the string to escape
|
95
|
+
# @return [String] the escaped string
|
96
|
+
# @see Conjur::Escape::ClassMethods#fully_escape
|
51
97
|
def fully_escape(str)
|
52
98
|
self.class.fully_escape str
|
53
99
|
end
|
54
100
|
|
101
|
+
# Escape a URI path component.
|
102
|
+
#
|
103
|
+
# This method simply calls {Conjur::Escape::ClassMethods#path_or_query_escape}.
|
104
|
+
#
|
105
|
+
# @param [String] str the string to escape
|
106
|
+
# @return [String] the escaped string
|
107
|
+
# @see Conjur::Escape::ClassMethods#path_or_query_escape
|
55
108
|
def path_escape(str)
|
56
109
|
self.class.path_escape str
|
57
110
|
end
|
58
111
|
|
112
|
+
|
113
|
+
# Escape a URI query value.
|
114
|
+
#
|
115
|
+
# This method simply calls {Conjur::Escape::ClassMethods#path_or_query_escape}.
|
116
|
+
#
|
117
|
+
# @param [String] str the string to escape
|
118
|
+
# @return [String] the escaped string
|
119
|
+
# @see Conjur::Escape::ClassMethods#path_or_query_escape
|
59
120
|
def query_escape(str)
|
60
121
|
self.class.query_escape str
|
61
122
|
end
|
data/lib/conjur/event_source.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
module Conjur
|
2
|
+
# @api private
|
2
3
|
# An EventSource instance is used to parse a stream in the format given by
|
3
4
|
# the Server Sent Events standard: http://www.whatwg.org/specs/web-apps/current-work/#server-sent-events
|
5
|
+
#
|
6
|
+
# This class is used internally by the audit methods in follow mode.
|
7
|
+
#
|
4
8
|
class EventSource
|
9
|
+
# @api private
|
10
|
+
# Representation of a SSE event
|
5
11
|
class Event < Struct.new(:data, :name, :id);
|
6
12
|
end
|
7
13
|
|
@@ -20,6 +26,8 @@ module Conjur
|
|
20
26
|
attr_accessor :json
|
21
27
|
alias json? json
|
22
28
|
|
29
|
+
# @api private
|
30
|
+
# Create an EventSource
|
23
31
|
def initialize
|
24
32
|
@json = true
|
25
33
|
@on = {}
|
@@ -27,9 +35,11 @@ module Conjur
|
|
27
35
|
@buffer = ""
|
28
36
|
end
|
29
37
|
|
30
|
-
#
|
38
|
+
# @api private
|
39
|
+
# Feed a chunk of data to the EventSource and dispatch any fully received
|
31
40
|
# events.
|
32
41
|
# @param [String] chunk the data to parse
|
42
|
+
# @return [void]
|
33
43
|
def feed chunk
|
34
44
|
@buffer << chunk
|
35
45
|
|
@@ -38,7 +48,10 @@ module Conjur
|
|
38
48
|
end
|
39
49
|
end
|
40
50
|
|
41
|
-
#
|
51
|
+
# Add a block to be called when events with an `'event'` field of `name` are received.
|
52
|
+
#
|
53
|
+
# @param [String, Symbol] name the name to listen for
|
54
|
+
# @yieldparam [Conjur::EventSource::Event] the event
|
42
55
|
def on name, &block
|
43
56
|
(@on[name.to_sym] ||= []) << block
|
44
57
|
end
|
data/lib/conjur/graph.rb
CHANGED
@@ -19,15 +19,30 @@
|
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
21
|
module Conjur
|
22
|
+
|
23
|
+
# A Graph represents a directed graph of roles.
|
24
|
+
#
|
25
|
+
# An instance of this class is returned by {Conjur::API#role_graph}.
|
26
|
+
#
|
27
|
+
# @example Graphs act like arrays of edges
|
28
|
+
# graph.each do |edge|
|
29
|
+
# puts "#{edge.parent} -> #{edge.child}"
|
30
|
+
# end
|
31
|
+
# # role1 -> role2
|
32
|
+
# # role2 -> role3
|
33
|
+
#
|
22
34
|
class Graph
|
23
35
|
|
24
36
|
include Enumerable
|
25
37
|
|
26
|
-
#
|
27
|
-
#
|
38
|
+
# Returns an array containing the directed edges of the graph.
|
39
|
+
#
|
40
|
+
# @return [Array<Conjur::Graph::Edge>] the edges of this graph
|
28
41
|
attr_reader :edges
|
29
42
|
|
30
43
|
# @api private
|
44
|
+
#
|
45
|
+
# @param val [String, Hash, Array, Graph] Data from which to initialize the instance
|
31
46
|
def initialize val
|
32
47
|
@edges = case val
|
33
48
|
when String then JSON.parse(val)['graph']
|
@@ -41,8 +56,9 @@ module Conjur
|
|
41
56
|
end
|
42
57
|
|
43
58
|
# Enumerates the edges of this graph.
|
44
|
-
#
|
45
|
-
# @
|
59
|
+
#
|
60
|
+
# @yieldparam [Conjur::Graph::Edge] edge each edge of the graph
|
61
|
+
# @return [Conjur::Graph] this graph
|
46
62
|
def each_edge
|
47
63
|
return enum_for(__method__) unless block_given?
|
48
64
|
edges.each{|e| yield e}
|
@@ -52,28 +68,59 @@ module Conjur
|
|
52
68
|
alias each each_edge
|
53
69
|
|
54
70
|
# Enumerates the vertices (roles) of this graph
|
55
|
-
# @yieldparam
|
71
|
+
# @yieldparam [Conjur::Role] vertex each vertex in this graph
|
56
72
|
# @return [Conjur::Graph] this graph
|
57
73
|
def each_vertex
|
58
74
|
return enum_for(__method__) unless block_given?
|
59
75
|
vertices.each{|v| yield v}
|
60
76
|
end
|
61
77
|
|
62
|
-
|
78
|
+
# Serialize the graph as JSON
|
79
|
+
# @param [Boolean] short when true, the graph is serialized as an array of arrays instead of an array of hashes.
|
80
|
+
# @return [String] the JSON serialized graph.
|
81
|
+
# @see #as_json
|
82
|
+
#
|
63
83
|
def to_json short = false
|
64
84
|
as_json(short).to_json
|
65
85
|
end
|
66
86
|
|
87
|
+
# Convert the graph to a JSON serializable data structure. The value returned by this method can have two
|
88
|
+
# forms: An array of arrays when `short` is `true`, or hash like
|
89
|
+
# `{ 'graph' => [ {'parent' => 'roleid', 'child' => 'roleid'} ]}` otherwise.
|
90
|
+
#
|
91
|
+
# @example Graph formats
|
92
|
+
# graph = api.role_graph 'conjur:group:pubkeys-1.0/key-managers'
|
93
|
+
#
|
94
|
+
# # Short format
|
95
|
+
# graph.as_json true
|
96
|
+
# # => [
|
97
|
+
# # ["conjur:group:pubkeys-1.0/key-managers", "conjur:group:pubkeys-1.0/admin"],
|
98
|
+
# # ["conjur:group:pubkeys-1.0/admin", "conjur:user:admin"]
|
99
|
+
# # ]
|
100
|
+
#
|
101
|
+
# # Default format (you can omit the false parameter in this case)
|
102
|
+
# graph.as_json false
|
103
|
+
# # => {
|
104
|
+
# # "graph" => [
|
105
|
+
# # {"parent"=>"conjur:group:pubkeys-1.0/key-managers", "child"=>"conjur:group:pubkeys-1.0/admin"},
|
106
|
+
# # {"parent"=>"conjur:group:pubkeys-1.0/admin", "child"=>"conjur:user:admin"}
|
107
|
+
# # ]
|
108
|
+
# #}
|
109
|
+
#
|
110
|
+
# @param [Boolean] short whether to use short of default format
|
111
|
+
# @return [Hash, Array] JSON serializable representation of the graph
|
67
112
|
def as_json short = false
|
68
113
|
edges = self.edges.map{|e| e.as_json(short)}
|
69
114
|
short ? edges : {'graph' => edges}
|
70
115
|
end
|
71
116
|
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
117
|
+
# Returns a string formatted for use by the {http://www.graphviz.org/ graphviz dot} tool.
|
118
|
+
#
|
119
|
+
# @param [String, NilClass] name An identifier to assign to the graph. This can be omitted unless you
|
120
|
+
# are writing multiple graphs to a single file. This must be in the ID format specified by
|
121
|
+
# http://www.graphviz.org/content/dot-language.
|
75
122
|
#
|
76
|
-
# @return [String] the dot format (used by
|
123
|
+
# @return [String] the dot format (used by graphviz, among others) representation of this graph.
|
77
124
|
def to_dot name = nil
|
78
125
|
dot = "digraph #{name || ''} {"
|
79
126
|
vertices.each do |v|
|
@@ -84,7 +131,9 @@ module Conjur
|
|
84
131
|
end
|
85
132
|
dot << "\n}"
|
86
133
|
end
|
87
|
-
|
134
|
+
|
135
|
+
# Return the vertices (roles) of the graph as an array.
|
136
|
+
# @return [Array<Conjur::Role>] the vertices/roles
|
88
137
|
def vertices
|
89
138
|
@vertices ||= edges.inject([]) {|a, pair| a.concat pair.to_a }.uniq
|
90
139
|
end
|
@@ -148,41 +197,83 @@ module Conjur
|
|
148
197
|
"#{parent_id} -> #{child_id}"
|
149
198
|
end
|
150
199
|
|
151
|
-
#
|
200
|
+
# Represents a directed Edge between a parent role and a child role.
|
201
|
+
#
|
202
|
+
# In this context, the parent role is a *member of* the child role. For example,
|
203
|
+
# the `admin` role is a parent of every role, either directly or indirectly, because
|
204
|
+
# it is added as a member to all roles it creates.
|
152
205
|
class Edge
|
206
|
+
|
207
|
+
# Return the parent of this edge. The {#parent} role *is a member of* the {#child} role.
|
208
|
+
# @return [Conjur::Role] the parent role
|
153
209
|
attr_reader :parent
|
210
|
+
|
211
|
+
# Return the child of this edge. The {#parent} role *is a member of* the {#child} role.
|
212
|
+
# @return [Conjur::Role] the child role
|
154
213
|
attr_reader :child
|
155
214
|
|
215
|
+
# Create a directed edge with a parent and child
|
216
|
+
#
|
217
|
+
# @param [Conjur::Role] parent the parent or source of this edge
|
218
|
+
# @param [Conjur::Role] child the child or destination of this edge
|
156
219
|
def initialize parent, child
|
157
220
|
@parent = parent
|
158
221
|
@child = child
|
159
222
|
end
|
160
223
|
|
224
|
+
# Serialize this edge as JSON.
|
225
|
+
#
|
226
|
+
# @see #as_json
|
227
|
+
# @param [Boolean] short when true, serialize the edge as an Array instead of a Hash
|
228
|
+
# @return [String] the JSON serialized edge
|
161
229
|
def to_json short = false
|
162
230
|
as_json(short).to_json
|
163
231
|
end
|
164
232
|
|
233
|
+
# Return a value suitable for JSON serialization.
|
234
|
+
#
|
235
|
+
# The `short` parameter determines whether to return a `["parent", "child"]` Array
|
236
|
+
# or a Hash like `{"parent" => "parent-role", "child" => "child-role"}`.
|
237
|
+
#
|
238
|
+
# @param [Boolean] short return an Array when true, otherwise return a Hash.
|
239
|
+
# @return [Array, Hash] value suitable for JSON serialization
|
165
240
|
def as_json short = false
|
166
241
|
short ? to_a : to_h
|
167
242
|
end
|
168
243
|
|
244
|
+
# Return this edge as a Hash like {"parent" => "...", "child" => "..."}.
|
245
|
+
#
|
246
|
+
# Note that the keys in the hash are strings.
|
247
|
+
#
|
248
|
+
# @return [Hash] a Hash representing this edge
|
169
249
|
def to_h
|
170
250
|
# return string keys to make testing less brittle
|
171
251
|
{'parent' => @parent, 'child' => @child}
|
172
252
|
end
|
173
253
|
|
254
|
+
# Return this edge as an Array like ["parent", "child"]
|
255
|
+
#
|
256
|
+
# @return [Array<String>] the edge as an Array
|
174
257
|
def to_a
|
175
258
|
[@parent, @child]
|
176
259
|
end
|
177
260
|
|
261
|
+
# @api private
|
262
|
+
# :nodoc:
|
178
263
|
def to_s
|
179
264
|
"<Edge #{parent.id} --> #{child.id}>"
|
180
265
|
end
|
181
266
|
|
267
|
+
# Support using edges as hash keys
|
268
|
+
# @api private
|
269
|
+
# :nodoc:
|
182
270
|
def hash
|
183
271
|
@hash ||= to_a.map(&:to_s).hash
|
184
272
|
end
|
185
273
|
|
274
|
+
# Support using edges as hash keys and equality testing
|
275
|
+
# @api private
|
276
|
+
# :nodoc:
|
186
277
|
def == other
|
187
278
|
other.kind_of?(self.class) and other.parent == parent and other.child == child
|
188
279
|
end
|