Class: NoSE::KeyPath
Overview
A path from a primary key to a chain of foreign keys
Instance Method Summary collapse
-
#+(other) ⇒ KeyPath
Combine two key paths by gluing together the keys.
-
#==(other, check_reverse = true) ⇒ Boolean
(also: #eql?)
Two key paths are equal if their underlying keys are equal or the reverse.
-
#[](index) ⇒ KeyPath
Return a slice of the path.
-
#entities ⇒ Array<Entity>
Return all the entities along the path.
-
#find_field_parent(field) ⇒ Object
Find the parent of a given field.
-
#include?(key) ⇒ Boolean
Check if a key is included in the path.
-
#initialize(keys = []) ⇒ KeyPath
constructor
A new instance of KeyPath.
-
#path_for_field(field) ⇒ Array<String>
Get the named path to reach this field through the list of keys.
-
#reverse ⇒ KeyPath
Return the reverse of this path.
-
#reverse! ⇒ void
Reverse this path in place.
-
#splice(target, entity) ⇒ KeyPath
Find where the path intersects the given entity and splice in the target path.
-
#split(entity) ⇒ KeyPath
Split the path where it intersects the given entity.
-
#start_with?(other, check_reverse = true) ⇒ Boolean
Check if this path starts with another path.
-
#subpaths(include_self = true) ⇒ Enumerable<KeyPath>
Produce all subpaths of this path.
-
#to_a ⇒ KeyPath
Simple wrapper so that we continue to be a KeyPath.
Methods included from Enumerable
#partitions, #prefixes, #product_by, #sum_by
Constructor Details
#initialize(keys = []) ⇒ KeyPath
Returns a new instance of KeyPath.
112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/nose/statements.rb', line 112 def initialize(keys = []) fail InvalidKeyPathException, 'first key must be an ID' \ unless keys.empty? || keys.first.instance_of?(Fields::IDField) keys_match = keys.each_cons(2).all? do |prev_key, key| key.parent == prev_key.entity end fail InvalidKeyPathException, 'keys must match along the path' \ unless keys_match @keys = keys end |
Instance Method Details
#+(other) ⇒ KeyPath
Combine two key paths by gluing together the keys
149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/nose/statements.rb', line 149 def +(other) fail TypeError unless other.is_a? KeyPath other_keys = other.instance_variable_get(:@keys) # Just copy if there's no combining necessary return dup if other_keys.empty? return other.dup if @keys.empty? # Only allow combining if the entities match fail ArgumentError unless other_keys.first.parent == entities.last # Combine the two paths KeyPath.new(@keys + other_keys[1..-1]) end |
#==(other, check_reverse = true) ⇒ Boolean Also known as: eql?
Two key paths are equal if their underlying keys are equal or the reverse
127 128 129 130 |
# File 'lib/nose/statements.rb', line 127 def ==(other, check_reverse = true) @keys == other.instance_variable_get(:@keys) || (check_reverse && reverse.send(:==, other.reverse, false)) end |
#[](index) ⇒ KeyPath
Return a slice of the path
166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/nose/statements.rb', line 166 def [](index) if index.is_a? Range keys = @keys[index] keys[0] = keys[0].entity.id_field \ unless keys.empty? || keys[0].instance_of?(Fields::IDField) KeyPath.new(keys) else key = @keys[index] key = key.entity.id_field \ unless key.nil? || key.instance_of?(Fields::IDField) key end end |
#entities ⇒ Array<Entity>
Return all the entities along the path
200 201 202 |
# File 'lib/nose/statements.rb', line 200 def entities @entities ||= @keys.map(&:entity) end |
#find_field_parent(field) ⇒ Object
Find the parent of a given field
238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/nose/statements.rb', line 238 def find_field_parent(field) parent = find do |key| field.parent == key.parent || (key.is_a?(Fields::ForeignKeyField) && field.parent == key.entity) end # This field is not on this portion of the path, so skip return nil if parent.nil? parent = parent.parent unless parent.is_a?(Fields::ForeignKeyField) parent end |
#include?(key) ⇒ Boolean
Check if a key is included in the path
143 144 145 |
# File 'lib/nose/statements.rb', line 143 def include?(key) @keys.include?(key) || entities.any? { |e| e.id_field == key } end |
#path_for_field(field) ⇒ Array<String>
Get the named path to reach this field through the list of keys
228 229 230 231 232 233 234 |
# File 'lib/nose/statements.rb', line 228 def path_for_field(field) return [field.name] if @keys.first.parent == field.parent @keys.each_cons(2).take_while do |prev_key, _| prev_key.entity != field.parent end.map(&:last).map(&:name) << field.name end |
#reverse ⇒ KeyPath
Return the reverse of this path
182 183 184 |
# File 'lib/nose/statements.rb', line 182 def reverse KeyPath.new reverse_path end |
#reverse! ⇒ void
This method returns an undefined value.
Reverse this path in place
188 189 190 |
# File 'lib/nose/statements.rb', line 188 def reverse! @keys = reverse_path end |
#splice(target, entity) ⇒ KeyPath
Find where the path intersects the given entity and splice in the target path
222 223 224 |
# File 'lib/nose/statements.rb', line 222 def splice(target, entity) split(entity) + target end |
#split(entity) ⇒ KeyPath
Split the path where it intersects the given entity
206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/nose/statements.rb', line 206 def split(entity) if first.parent == entity query_keys = KeyPath.new([entity.id_field]) else query_keys = [] each do |key| query_keys << key break if key.is_a?(Fields::ForeignKeyField) && key.entity == entity end query_keys = KeyPath.new(query_keys) end end |
#start_with?(other, check_reverse = true) ⇒ Boolean
Check if this path starts with another path
135 136 137 138 139 |
# File 'lib/nose/statements.rb', line 135 def start_with?(other, check_reverse = true) other_keys = other.instance_variable_get(:@keys) @keys[0..other_keys.length - 1] == other_keys || (check_reverse && reverse.start_with?(other.reverse, false)) end |
#subpaths(include_self = true) ⇒ Enumerable<KeyPath>
Produce all subpaths of this path
253 254 255 256 257 258 259 260 261 262 |
# File 'lib/nose/statements.rb', line 253 def subpaths(include_self = true) Enumerator.new do |enum| enum.yield self if include_self 1.upto(@keys.length) do |i| i.upto(@keys.length) do |j| enum.yield self[i - 1..j - 1] end end end end |
#to_a ⇒ KeyPath
Simple wrapper so that we continue to be a KeyPath
194 195 196 |
# File 'lib/nose/statements.rb', line 194 def to_a self end |