conjur-api 4.14.0 → 4.15.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.
- 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
|