lib/jini.rb in jini-1.1.5 vs lib/jini.rb in jini-1.2.5
- old
+ new
@@ -1,7 +1,5 @@
-# frozen_string_literal: true
-
# (The MIT License)
#
# Copyright (c) 2022 Ivanchuk Ivan
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -38,10 +36,13 @@
# => 'parent/child[@toy="plane"]
class Jini
# When path not valid
class InvalidPath < StandardError; end
+ # When method not found
+ class UnsupportedOperation < StandardError; end
+
# Makes new object.
# By default it creates an empty path and you can ignore the head parameter.
# @param head [String]
# @since 0.0.1
def initialize(head = '')
@@ -56,10 +57,72 @@
copy = @head.split(%r{//|/})
copy.each(&method(:space_check))
@head.to_s
end
+ class << self
+ # From.
+ # Creates new Jini object from XPATH.
+ #
+ # @param [String] xpath
+ # @raise [InvalidPath] when XPATH is invalid
+ # @return [Jini] object
+ def from(xpath)
+ raise InvalidPath, 'XPATH isn\'t valid' unless xpath_match?(xpath)
+ Jini.new(xpath)
+ end
+
+ private
+
+ # This regex matches general case of XPATH.
+ # @param xpath [String]
+ # @return [Boolean] matching regex
+ def xpath_match?(xpath)
+ xpath.match?(xpath_regex)
+ end
+
+ def prefix_regex
+ %r{^/?}
+ end
+
+ def tag_regex
+ /([a-zA-Z0-9]+:)?[a-zA-Z0-9]+/
+ end
+
+ def bracket_regex
+ /(\[[^\[\]]*\])/
+ end
+
+ def attr_regex
+ /@\w+=[^\]]+/
+ end
+
+ def or_regex
+ /(\|)/
+ end
+
+ def namespace_regex
+ /([a-zA-Z0-9]+:)?[a-zA-Z0-9]+/
+ end
+
+ def sub_xpath_regex
+ /#{namespace_regex}#{bracket_regex}*(#{attr_regex})*/
+ end
+
+ def or_sub_xpath_regex
+ /#{or_regex}#{namespace_regex}#{bracket_regex}*(#{attr_regex})*/
+ end
+
+ def xpath_regex
+ %r{\A#{prefix_regex}#{namespace_regex}(/#{sub_xpath_regex})*#{or_sub_xpath_regex}*(/#{sub_xpath_regex})*\Z}
+ end
+ end
+
+ def ==(other)
+ self.class == other.class && to_s == other.to_s
+ end
+
# Additional node for xpath.
# @param node [String] the node
# @return [Jini] object with additional node
# @since 0.0.1
def add_node(node)
@@ -235,11 +298,11 @@
raise InvalidPath, 'Invalid path, empty node' if node.length.eql? 0
end
# @param node [String] node for check
def space_check(node)
- raise InvalidPath, "Nodes can't contain spaces: #{node} – contain space." if valid? node
+ raise InvalidPath, "Nodes can't contain spaces: #{node} – contain space." if valid_node? node
end
# Regex: '[' or ']' or '@' or '//'.
# @param node [String]
# @return [Boolean] matching regex
@@ -248,11 +311,11 @@
end
# Regex: '[' or ']' or '@' or '=' or '<' or '>'.
# @param node [String] node for check
# @return [Boolean] matching regex
- def valid?(node)
+ def valid_node?(node)
!node.match(/[|]|@|=|>|</) && node.include?(' ')
end
# Some action between two statements.
def action_between(action, alpha, beta)
@@ -261,7 +324,15 @@
# Purging head from token.
# @param [Regexp | String] token to be purged from the head
def purge_head(token)
@head.gsub(token, '')
+ end
+
+ def method_missing(method_name, *_args)
+ raise UnsupportedOperation, "The method #{method_name} was not found!"
+ end
+
+ def respond_to_missing?(_method_name, _include_private = false)
+ true
end
end