File: dsl.rb

package info (click to toggle)
ruby-xpath 3.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, buster, forky, sid, trixie
  • size: 140 kB
  • sloc: ruby: 847; makefile: 2
file content (174 lines) | stat: -rw-r--r-- 4,143 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# frozen_string_literal: true

module XPath
  module DSL
    def current
      Expression.new(:this_node)
    end

    def descendant(*expressions)
      Expression.new(:descendant, current, expressions)
    end

    def child(*expressions)
      Expression.new(:child, current, expressions)
    end

    def axis(name, *element_names)
      Expression.new(:axis, current, name, element_names)
    end

    def anywhere(*expressions)
      Expression.new(:anywhere, expressions)
    end

    def attr(expression)
      Expression.new(:attribute, current, expression)
    end

    def text
      Expression.new(:text, current)
    end

    def css(selector)
      Expression.new(:css, current, Literal.new(selector))
    end

    def function(name, *arguments)
      Expression.new(:function, name, *arguments)
    end

    def method(name, *arguments)
      Expression.new(:function, name, current, *arguments)
    end

    def where(expression)
      if expression
        Expression.new(:where, current, expression)
      else
        current
      end
    end
    alias_method :[], :where

    def is(expression)
      Expression.new(:is, current, expression)
    end

    def binary_operator(name, rhs)
      Expression.new(:binary_operator, name, current, rhs)
    end

    def union(*expressions)
      Union.new(*[self, expressions].flatten)
    end
    alias_method :+, :union

    def last
      function(:last)
    end

    def position
      function(:position)
    end

    METHODS = [
      # node set
      :count, :id, :local_name, :namespace_uri,
      # string
      :string, :concat, :starts_with, :contains, :substring_before,
      :substring_after, :substring, :string_length, :normalize_space,
      :translate,
      # boolean
      :boolean, :not, :true, :false, :lang,
      # number
      :number, :sum, :floor, :ceiling, :round
    ].freeze

    METHODS.each do |key|
      name = key.to_s.tr('_', '-').to_sym
      define_method key do |*args|
        method(name, *args)
      end
    end

    def qname
      method(:name)
    end

    alias_method :inverse, :not
    alias_method :~, :not
    alias_method :!, :not
    alias_method :normalize, :normalize_space
    alias_method :n, :normalize_space

    OPERATORS = [
      %i[equals = ==],
      %i[or or |],
      %i[and and &],
      %i[not_equals != !=],
      %i[lte <= <=],
      %i[lt < <],
      %i[gte >= >=],
      %i[gt > >],
      %i[plus +],
      %i[minus -],
      %i[multiply * *],
      %i[divide div /],
      %i[mod mod %]
    ].freeze

    OPERATORS.each do |(name, operator, alias_name)|
      define_method name do |rhs|
        binary_operator(operator, rhs)
      end
      alias_method alias_name, name if alias_name
    end

    AXES = %i[
      ancestor ancestor_or_self attribute descendant_or_self
      following following_sibling namespace parent preceding
      preceding_sibling self
    ].freeze

    AXES.each do |key|
      name = key.to_s.tr('_', '-').to_sym
      define_method key do |*element_names|
        axis(name, *element_names)
      end
    end

    alias_method :self_axis, :self

    def ends_with(suffix)
      function(:substring, current, function(:'string-length', current).minus(function(:'string-length', suffix)).plus(1)) == suffix
    end

    def contains_word(word)
      function(:concat, ' ', current.normalize_space, ' ').contains(" #{word} ")
    end

    UPPERCASE_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'
    LOWERCASE_LETTERS = 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'

    def lowercase
      method(:translate, UPPERCASE_LETTERS, LOWERCASE_LETTERS)
    end

    def uppercase
      method(:translate, LOWERCASE_LETTERS, UPPERCASE_LETTERS)
    end

    def one_of(*expressions)
      expressions.map { |e| current.equals(e) }.reduce(:or)
    end

    def next_sibling(*expressions)
      axis(:"following-sibling")[1].axis(:self, *expressions)
    end

    def previous_sibling(*expressions)
      axis(:"preceding-sibling")[1].axis(:self, *expressions)
    end
  end
end