diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..b600db1 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,89 @@ +require: + - rubocop-rspec + +AllCops: + DisabledByDefault: false + TargetRubyVersion: 2.3 + Exclude: + - 'xpath.gemspec' + +#################### Lint ################################ + +Metrics/LineLength: + Exclude: + - 'spec/**/*' + Max: 120 + Enabled: false + +Metrics/BlockLength: + Exclude: + - 'spec/**/*' + +Metrics/AbcSize: + Enabled: false + +Metrics/ClassLength: + CountComments: false + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/MethodLength: + CountComments: false + Enabled: false + +Metrics/ModuleLength: + Enabled: false + CountComments: false + +Metrics/PerceivedComplexity: + Enabled: false + +Metrics/ParameterLists: + Enabled: false + +Layout/EndAlignment: + EnforcedStyleAlignWith: variable + +Style/Alias: + Enabled: false + +Style/Documentation: + Exclude: + - 'spec/**/*' + Enabled: false + +Style/EvenOdd: + Enabled: false + +Lint/BooleanSymbol: + Enabled: false + +Style/Attr: + Enabled: false + +Layout/AccessModifierIndentation: + EnforcedStyle: outdent + +RSpec/ExampleWording: + Enabled: false + +RSpec/InstanceVariable: + Enabled: false + +RSpec/ExampleLength: + Enabled: false + +RSpec/DescribedClass: + Enabled: false + +RSpec/DescribeClass: + Enabled: false + +RSpec/FilePath: + Enabled: false + +Style/MethodCallWithoutArgsParentheses: + Exclude: + - spec/xpath_spec.rb diff --git a/.travis.yml b/.travis.yml index b47788a..575a784 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,15 +6,10 @@ env: - JAVA_OPTS=-Djava.security.egd=file:/dev/urandom rvm: - - 2.2 - 2.3 - 2.4 - jruby-9.1.15.0 - 2.5 -matrix: - allow_failures: - - rvm: 2.5 - before_install: - gem update bundler \ No newline at end of file diff --git a/Gemfile b/Gemfile index b4e2a20..7f4f5e9 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ -source "https://rubygems.org" +# frozen_string_literal: true + +source 'https://rubygems.org' gemspec diff --git a/Rakefile b/Rakefile index 20ad07c..55ed35f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,16 +1,18 @@ +# frozen_string_literal: true + require 'bundler/gem_tasks' require 'rspec/core/rake_task' require 'yard' -desc "Run all examples" +desc 'Run all examples' RSpec::Core::RakeTask.new(:spec) do |t| - #t.rspec_path = 'bin/rspec' + # t.rspec_path = 'bin/rspec' t.rspec_opts = %w[--color] end YARD::Rake::YardocTask.new do |t| - t.files = ['lib/**/*.rb', 'README.md'] - #t.options = ['--any', '--extra', '--opts'] # optional + t.files = ['lib/**/*.rb', 'README.md'] + # t.options = ['--any', '--extra', '--opts'] # optional end -task :default => :spec +task default: :spec diff --git a/lib/xpath.rb b/lib/xpath.rb index 1c43d5c..3f19cf1 100644 --- a/lib/xpath.rb +++ b/lib/xpath.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'nokogiri' require 'xpath/dsl' diff --git a/lib/xpath/dsl.rb b/lib/xpath/dsl.rb index bd52a08..b6de457 100644 --- a/lib/xpath/dsl.rb +++ b/lib/xpath/dsl.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module XPath module DSL def current @@ -80,11 +82,11 @@ def position # boolean :boolean, :not, :true, :false, :lang, # number - :number, :sum, :floor, :ceiling, :round, - ] + :number, :sum, :floor, :ceiling, :round + ].freeze METHODS.each do |key| - name = key.to_s.gsub("_", "-").to_sym + name = key.to_s.tr('_', '-').to_sym define_method key do |*args| method(name, *args) end @@ -101,20 +103,20 @@ def qname alias_method :n, :normalize_space OPERATORS = [ - [:equals, :"=", :==], - [:or, :or, :|], - [:and, :and, :&], - [:not_equals, :!=, :!=], - [:lte, :<=, :<=], - [:lt, :<, :<], - [:gte, :>=, :>=], - [:gt, :>, :>], - [:plus, :+], - [:minus, :-], - [:multiply, :*, :*], - [:divide, :div, :/], - [:mod, :mod, :%], - ] + %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| @@ -123,14 +125,14 @@ def qname alias_method alias_name, name if alias_name end - AXES = [ - :ancestor, :ancestor_or_self, :attribute, :descendant_or_self, - :following, :following_sibling, :namespace, :parent, :preceding, - :preceding_sibling, :self, - ] + 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.gsub("_", "-").to_sym + name = key.to_s.tr('_', '-').to_sym define_method key do |*element_names| axis(name, *element_names) end @@ -143,7 +145,7 @@ def ends_with(suffix) end def contains_word(word) - function(:concat, " ", current.normalize_space, " ").contains(" #{word} ") + function(:concat, ' ', current.normalize_space, ' ').contains(" #{word} ") end UPPERCASE_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ' @@ -158,11 +160,7 @@ def uppercase end def one_of(*expressions) - expressions.map do |e| - current.equals(e) - end.reduce do |a, b| - a.or(b) - end + expressions.map { |e| current.equals(e) }.reduce(:or) end def next_sibling(*expressions) diff --git a/lib/xpath/expression.rb b/lib/xpath/expression.rb index cd4c6ec..fd3a61b 100644 --- a/lib/xpath/expression.rb +++ b/lib/xpath/expression.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module XPath class Expression attr_accessor :expression, :arguments @@ -12,7 +14,7 @@ def current self end - def to_xpath(type=nil) + def to_xpath(type = nil) Renderer.render(self, type) end alias_method :to_s, :to_xpath diff --git a/lib/xpath/literal.rb b/lib/xpath/literal.rb index 9768758..42620da 100644 --- a/lib/xpath/literal.rb +++ b/lib/xpath/literal.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module XPath class Literal attr_reader :value diff --git a/lib/xpath/renderer.rb b/lib/xpath/renderer.rb index dc10e35..199b3f9 100644 --- a/lib/xpath/renderer.rb +++ b/lib/xpath/renderer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module XPath class Renderer def self.render(node, type) @@ -15,11 +17,11 @@ def render(node) def convert_argument(argument) case argument - when Expression, Union then render(argument) - when Array then argument.map { |element| convert_argument(element) } - when String then string_literal(argument) - when Literal then argument.value - else argument.to_s + when Expression, Union then render(argument) + when Array then argument.map { |element| convert_argument(element) } + when String then string_literal(argument) + when Literal then argument.value + else argument.to_s end end @@ -27,7 +29,7 @@ def string_literal(string) if string.include?("'") string = string.split("'", -1).map do |substr| "'#{substr}'" - end.join(%q{,"'",}) + end.join(%q(,"'",)) "concat(#{string})" else "'#{string}'" @@ -51,7 +53,7 @@ def axis(current, name, element_names) end def anywhere(element_names) - with_element_conditions("//", element_names) + with_element_conditions('//', element_names) end def where(on, condition) @@ -72,7 +74,7 @@ def binary_operator(name, left, right) def is(one, two) if @type == :exact - binary_operator("=", one, two) + binary_operator('=', one, two) else function(:contains, one, two) end @@ -102,7 +104,7 @@ def union(*expressions) end def function(name, *arguments) - "#{name}(#{arguments.join(", ")})" + "#{name}(#{arguments.join(', ')})" end private @@ -111,7 +113,7 @@ def with_element_conditions(expression, element_names) if element_names.length == 1 "#{expression}#{element_names.first}" elsif element_names.length > 1 - "#{expression}*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]" + "#{expression}*[#{element_names.map { |e| "self::#{e}" }.join(' | ')}]" else "#{expression}*" end diff --git a/lib/xpath/union.rb b/lib/xpath/union.rb index e14385f..68c5a24 100644 --- a/lib/xpath/union.rb +++ b/lib/xpath/union.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module XPath class Union include Enumerable @@ -17,11 +19,11 @@ def each(&block) arguments.each(&block) end - def method_missing(*args) + def method_missing(*args) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing XPath::Union.new(*arguments.map { |e| e.send(*args) }) end - def to_xpath(type=nil) + def to_xpath(type = nil) Renderer.render(self, type) end alias_method :to_s, :to_xpath diff --git a/lib/xpath/version.rb b/lib/xpath/version.rb index 4bb08d2..a85279e 100644 --- a/lib/xpath/version.rb +++ b/lib/xpath/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module XPath VERSION = '3.1.0' end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index eb224a6..8f61314 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'xpath' require 'pry' diff --git a/spec/union_spec.rb b/spec/union_spec.rb index 50b8091..4eba7c5 100644 --- a/spec/union_spec.rb +++ b/spec/union_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe XPath::Union do @@ -5,7 +7,7 @@ let(:doc) { Nokogiri::HTML(template) } describe '#expressions' do - it "should return the expressions" do + it 'should return the expressions' do @expr1 = XPath.generate { |x| x.descendant(:p) } @expr2 = XPath.generate { |x| x.descendant(:div) } @collection = XPath::Union.new(@expr1, @expr2) @@ -14,7 +16,7 @@ end describe '#each' do - it "should iterate through the expressions" do + it 'should iterate through the expressions' do @expr1 = XPath.generate { |x| x.descendant(:p) } @expr2 = XPath.generate { |x| x.descendant(:div) } @collection = XPath::Union.new(@expr1, @expr2) @@ -25,16 +27,16 @@ end describe '#map' do - it "should map the expressions" do + it 'should map the expressions' do @expr1 = XPath.generate { |x| x.descendant(:p) } @expr2 = XPath.generate { |x| x.descendant(:div) } @collection = XPath::Union.new(@expr1, @expr2) - @collection.map { |expr| expr.expression }.should eq [:descendant, :descendant] + @collection.map(&:expression).should eq %i[descendant descendant] end end describe '#to_xpath' do - it "should create a valid xpath expression" do + it 'should create a valid xpath expression' do @expr1 = XPath.generate { |x| x.descendant(:p) } @expr2 = XPath.generate { |x| x.descendant(:div).where(x.attr(:id) == 'foo') } @collection = XPath::Union.new(@expr1, @expr2) @@ -45,9 +47,8 @@ end end - describe '#where and others' do - it "should be delegated to the individual expressions" do + it 'should be delegated to the individual expressions' do @expr1 = XPath.generate { |x| x.descendant(:p) } @expr2 = XPath.generate { |x| x.descendant(:div) } @collection = XPath::Union.new(@expr1, @expr2) @@ -59,6 +60,4 @@ @results[0][:id].should eq 'fooDiv' end end - end - diff --git a/spec/xpath_spec.rb b/spec/xpath_spec.rb index b278d58..a9c714b 100644 --- a/spec/xpath_spec.rb +++ b/spec/xpath_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'nokogiri' @@ -14,23 +16,23 @@ def foo_div let(:template) { File.read(File.expand_path('fixtures/simple.html', File.dirname(__FILE__))) } let(:doc) { Nokogiri::HTML(template) } - def xpath(type=nil, &block) + def xpath(type = nil, &block) doc.xpath XPath.generate(&block).to_xpath(type) end - it "should work as a mixin" do + it 'should work as a mixin' do xpath = Thingy.new.foo_div.to_xpath doc.xpath(xpath).first[:title].should eq 'fooDiv' end describe '#descendant' do - it "should find nodes that are nested below the current node" do + it 'should find nodes that are nested below the current node' do @results = xpath { |x| x.descendant(:p) } - @results[0].text.should eq "Blah" - @results[1].text.should eq "Bax" + @results[0].text.should eq 'Blah' + @results[1].text.should eq 'Bax' end - it "should not find nodes outside the context" do + it 'should not find nodes outside the context' do @results = xpath do |x| foo_div = x.descendant(:div).where(x.attr(:id) == 'foo') x.descendant(:p).where(x.attr(:id) == foo_div.attr(:title)) @@ -38,13 +40,13 @@ def xpath(type=nil, &block) @results[0].should be_nil end - it "should find multiple kinds of nodes" do + it 'should find multiple kinds of nodes' do @results = xpath { |x| x.descendant(:p, :ul) } @results[0].text.should eq 'Blah' @results[3].text.should eq 'A list' end - it "should find all nodes when no arguments given" do + it 'should find all nodes when no arguments given' do @results = xpath { |x| x.descendant[x.attr(:id) == 'foo'].descendant } @results[0].text.should eq 'Blah' @results[4].text.should eq 'A list' @@ -52,24 +54,24 @@ def xpath(type=nil, &block) end describe '#child' do - it "should find nodes that are nested directly below the current node" do + it 'should find nodes that are nested directly below the current node' do @results = xpath { |x| x.descendant(:div).child(:p) } - @results[0].text.should eq "Blah" - @results[1].text.should eq "Bax" + @results[0].text.should eq 'Blah' + @results[1].text.should eq 'Bax' end - it "should not find nodes that are nested further down below the current node" do + it 'should not find nodes that are nested further down below the current node' do @results = xpath { |x| x.child(:p) } @results[0].should be_nil end - it "should find multiple kinds of nodes" do + it 'should find multiple kinds of nodes' do @results = xpath { |x| x.descendant(:div).child(:p, :ul) } @results[0].text.should eq 'Blah' @results[3].text.should eq 'A list' end - it "should find all nodes when no arguments given" do + it 'should find all nodes when no arguments given' do @results = xpath { |x| x.descendant[x.attr(:id) == 'foo'].child } @results[0].text.should eq 'Blah' @results[3].text.should eq 'A list' @@ -77,19 +79,19 @@ def xpath(type=nil, &block) end describe '#axis' do - it "should find nodes given the xpath axis" do + it 'should find nodes given the xpath axis' do @results = xpath { |x| x.axis(:descendant, :p) } - @results[0].text.should eq "Blah" + @results[0].text.should eq 'Blah' end - it "should find nodes given the xpath axis without a specific tag" do + it 'should find nodes given the xpath axis without a specific tag' do @results = xpath { |x| x.descendant(:div)[x.attr(:id) == 'foo'].axis(:descendant) } - @results[0][:id].should eq "fooDiv" + @results[0][:id].should eq 'fooDiv' end end describe '#next_sibling' do - it "should find nodes which are immediate siblings of the current node" do + it 'should find nodes which are immediate siblings of the current node' do xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:p) }.first.text.should eq 'Bax' xpath { |x| x.descendant(:p)[x.attr(:id) == 'fooDiv'].next_sibling(:ul, :p) }.first.text.should eq 'Bax' xpath { |x| x.descendant(:p)[x.attr(:title) == 'monkey'].next_sibling(:ul, :p) }.first.text.should eq 'A list' @@ -99,7 +101,7 @@ def xpath(type=nil, &block) end describe '#previous_sibling' do - it "should find nodes which are exactly preceding the current node" do + it 'should find nodes which are exactly preceding the current node' do xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling(:p) }.first.text.should eq 'Bax' xpath { |x| x.descendant(:p)[x.attr(:id) == 'wooDiv'].previous_sibling(:ul, :p) }.first.text.should eq 'Bax' xpath { |x| x.descendant(:p)[x.attr(:title) == 'gorilla'].previous_sibling(:ul, :p) }.first.text.should eq 'A list' @@ -109,17 +111,17 @@ def xpath(type=nil, &block) end describe '#anywhere' do - it "should find nodes regardless of the context" do + it 'should find nodes regardless of the context' do @results = xpath do |x| foo_div = x.anywhere(:div).where(x.attr(:id) == 'foo') x.descendant(:p).where(x.attr(:id) == foo_div.attr(:title)) end - @results[0].text.should eq "Blah" + @results[0].text.should eq 'Blah' end - it "should find multiple kinds of nodes regardless of the context" do + it 'should find multiple kinds of nodes regardless of the context' do @results = xpath do |x| - context=x.descendant(:div).where(x.attr(:id)=='woo') + context = x.descendant(:div).where(x.attr(:id) == 'woo') context.anywhere(:p, :ul) end @@ -129,9 +131,9 @@ def xpath(type=nil, &block) @results[6].text.should eq 'Bax' end - it "should find all nodes when no arguments given regardless of the context" do + it 'should find all nodes when no arguments given regardless of the context' do @results = xpath do |x| - context=x.descendant(:div).where(x.attr(:id)=='woo') + context = x.descendant(:div).where(x.attr(:id) == 'woo') context.anywhere end @results[0].name.should eq 'html' @@ -142,72 +144,71 @@ def xpath(type=nil, &block) @results[13].text.should eq 'A list' @results[15].text.should eq 'Bax' end - end describe '#contains' do - it "should find nodes that contain the given string" do + it 'should find nodes that contain the given string' do @results = xpath do |x| x.descendant(:div).where(x.attr(:title).contains('ooD')) end - @results[0][:id].should eq "foo" + @results[0][:id].should eq 'foo' end - it "should find nodes that contain the given expression" do + it 'should find nodes that contain the given expression' do @results = xpath do |x| expression = x.anywhere(:div).where(x.attr(:title) == 'fooDiv').attr(:id) x.descendant(:div).where(x.attr(:title).contains(expression)) end - @results[0][:id].should eq "foo" + @results[0][:id].should eq 'foo' end end - describe "#contains_word" do - it "should find nodes that contain the given word in its entirety" do + describe '#contains_word' do + it 'should find nodes that contain the given word in its entirety' do @results = xpath do |x| x.descendant.where(x.attr(:class).contains_word('fish')) end - @results[0].text.should eq "Bax" - @results[1].text.should eq "llama" + @results[0].text.should eq 'Bax' + @results[1].text.should eq 'llama' @results.length.should eq 2 end end describe '#starts_with' do - it "should find nodes that begin with the given string" do + it 'should find nodes that begin with the given string' do @results = xpath do |x| x.descendant(:*).where(x.attr(:id).starts_with('foo')) end @results.size.should eq 2 - @results[0][:id].should eq "foo" - @results[1][:id].should eq "fooDiv" + @results[0][:id].should eq 'foo' + @results[1][:id].should eq 'fooDiv' end - it "should find nodes that contain the given expression" do + it 'should find nodes that contain the given expression' do @results = xpath do |x| expression = x.anywhere(:div).where(x.attr(:title) == 'fooDiv').attr(:id) x.descendant(:div).where(x.attr(:title).starts_with(expression)) end - @results[0][:id].should eq "foo" + @results[0][:id].should eq 'foo' end end describe '#ends_with' do - it "should find nodes that end with the given string" do + it 'should find nodes that end with the given string' do @results = xpath do |x| x.descendant(:*).where(x.attr(:id).ends_with('oof')) end @results.size.should eq 2 - @results[0][:id].should eq "oof" - @results[1][:id].should eq "viDoof" + @results[0][:id].should eq 'oof' + @results[1][:id].should eq 'viDoof' end - it "should find nodes that contain the given expression" do + it 'should find nodes that contain the given expression' do @results = xpath do |x| expression = x.anywhere(:div).where(x.attr(:title) == 'viDoof').attr(:id) x.descendant(:div).where(x.attr(:title).ends_with(expression)) end - @results[0][:id].should eq "oof" + @results[0][:id].should eq 'oof' end end @@ -216,7 +217,7 @@ def xpath(type=nil, &block) @results = xpath do |x| x.descendant(:div).where(x.attr(:title).uppercase == 'VIDOOF') end - @results[0][:id].should eq "oof" + @results[0][:id].should eq 'oof' end end @@ -225,7 +226,7 @@ def xpath(type=nil, &block) @results = xpath do |x| x.descendant(:div).where(x.attr(:title).lowercase == 'vidoof') end - @results[0][:id].should eq "oof" + @results[0][:id].should eq 'oof' end end @@ -240,177 +241,176 @@ def xpath(type=nil, &block) end describe '#substring' do - context "when called with one argument" do - it "should select the part of a string after the specified character" do - @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "substring").text.substring(7) } - @results.should eq "there" + context 'when called with one argument' do + it 'should select the part of a string after the specified character' do + @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'substring').text.substring(7) } + @results.should eq 'there' end end - context "when called with two arguments" do - it "should select the part of a string after the specified character, up to the given length" do - @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "substring").text.substring(2, 4) } - @results.should eq "ello" + context 'when called with two arguments' do + it 'should select the part of a string after the specified character, up to the given length' do + @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'substring').text.substring(2, 4) } + @results.should eq 'ello' end end end describe '#function' do - it "should call the given xpath function" do + it 'should call the given xpath function' do @results = xpath { |x| x.function(:boolean, x.function(:true) == x.function(:false)) } @results.should eq false end end describe '#method' do - it "should call the given xpath function with the current node as the first argument" do - @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "string-length").text.method(:"string-length") } + it 'should call the given xpath function with the current node as the first argument' do + @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'string-length').text.method(:"string-length") } @results.should eq 11 end end describe '#string_length' do - it "should return the length of a string" do - @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == "string-length").text.string_length } + it 'should return the length of a string' do + @results = xpath { |x| x.descendant(:span).where(x.attr(:id) == 'string-length').text.string_length } @results.should eq 11 end end describe '#where' do - it "should limit the expression to find only certain nodes" do - xpath { |x| x.descendant(:div).where(:"@id = 'foo'") }.first[:title].should eq "fooDiv" + it 'should limit the expression to find only certain nodes' do + xpath { |x| x.descendant(:div).where(:"@id = 'foo'") }.first[:title].should eq 'fooDiv' end - it "should be aliased as []" do - xpath { |x| x.descendant(:div)[:"@id = 'foo'"] }.first[:title].should eq "fooDiv" + it 'should be aliased as []' do + xpath { |x| x.descendant(:div)[:"@id = 'foo'"] }.first[:title].should eq 'fooDiv' end - it "should be a no-op when nil condition is passed" do - XPath.descendant(:div).where(nil).to_s.should eq ".//div" + it 'should be a no-op when nil condition is passed' do + XPath.descendant(:div).where(nil).to_s.should eq './/div' end end describe '#inverse' do - it "should invert the expression" do + it 'should invert the expression' do xpath { |x| x.descendant(:p).where(x.attr(:id).equals('fooDiv').inverse) }.first.text.should eq 'Bax' end - it "should be aliased as the unary tilde" do + it 'should be aliased as the unary tilde' do xpath { |x| x.descendant(:p).where(~x.attr(:id).equals('fooDiv')) }.first.text.should eq 'Bax' end - it "should be aliased as the unary bang" do + it 'should be aliased as the unary bang' do xpath { |x| x.descendant(:p).where(!x.attr(:id).equals('fooDiv')) }.first.text.should eq 'Bax' end end describe '#equals' do - it "should limit the expression to find only certain nodes" do - xpath { |x| x.descendant(:div).where(x.attr(:id).equals('foo')) }.first[:title].should eq "fooDiv" + it 'should limit the expression to find only certain nodes' do + xpath { |x| x.descendant(:div).where(x.attr(:id).equals('foo')) }.first[:title].should eq 'fooDiv' end - it "should be aliased as ==" do - xpath { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }.first[:title].should eq "fooDiv" + it 'should be aliased as ==' do + xpath { |x| x.descendant(:div).where(x.attr(:id) == 'foo') }.first[:title].should eq 'fooDiv' end end describe '#not_equals' do - it "should match only when not equal" do - xpath { |x| x.descendant(:div).where(x.attr(:id).not_equals('bar')) }.first[:title].should eq "fooDiv" + it 'should match only when not equal' do + xpath { |x| x.descendant(:div).where(x.attr(:id).not_equals('bar')) }.first[:title].should eq 'fooDiv' end - it "should be aliased as !=" do - xpath { |x| x.descendant(:div).where(x.attr(:id) != 'bar') }.first[:title].should eq "fooDiv" + it 'should be aliased as !=' do + xpath { |x| x.descendant(:div).where(x.attr(:id) != 'bar') }.first[:title].should eq 'fooDiv' end end - describe '#is' do - it "uses equality when :exact given" do - xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq "fooDiv" + it 'uses equality when :exact given' do + xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq 'fooDiv' xpath(:exact) { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first.should be_nil end - it "uses substring matching otherwise" do - xpath { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq "fooDiv" - xpath { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first[:title].should eq "fooDiv" + it 'uses substring matching otherwise' do + xpath { |x| x.descendant(:div).where(x.attr(:id).is('foo')) }.first[:title].should eq 'fooDiv' + xpath { |x| x.descendant(:div).where(x.attr(:id).is('oo')) }.first[:title].should eq 'fooDiv' end end describe '#one_of' do - it "should return all nodes where the condition matches" do + it 'should return all nodes where the condition matches' do @results = xpath do |x| p = x.anywhere(:div).where(x.attr(:id) == 'foo').attr(:title) x.descendant(:*).where(x.attr(:id).one_of('foo', p, 'baz')) end - @results[0][:title].should eq "fooDiv" - @results[1].text.should eq "Blah" - @results[2][:title].should eq "bazDiv" + @results[0][:title].should eq 'fooDiv' + @results[1].text.should eq 'Blah' + @results[2][:title].should eq 'bazDiv' end end describe '#and' do - it "should find all nodes in both expression" do + it 'should find all nodes in both expression' do @results = xpath do |x| x.descendant(:*).where(x.contains('Bax').and(x.attr(:title).equals('monkey'))) end - @results[0][:title].should eq "monkey" + @results[0][:title].should eq 'monkey' end - it "should be aliased as ampersand (&)" do + it 'should be aliased as ampersand (&)' do @results = xpath do |x| x.descendant(:*).where(x.contains('Bax') & x.attr(:title).equals('monkey')) end - @results[0][:title].should eq "monkey" + @results[0][:title].should eq 'monkey' end end describe '#or' do - it "should find all nodes in either expression" do + it 'should find all nodes in either expression' do @results = xpath do |x| x.descendant(:*).where(x.attr(:id).equals('foo').or(x.attr(:id).equals('fooDiv'))) end - @results[0][:title].should eq "fooDiv" - @results[1].text.should eq "Blah" + @results[0][:title].should eq 'fooDiv' + @results[1].text.should eq 'Blah' end - it "should be aliased as pipe (|)" do + it 'should be aliased as pipe (|)' do @results = xpath do |x| x.descendant(:*).where(x.attr(:id).equals('foo') | x.attr(:id).equals('fooDiv')) end - @results[0][:title].should eq "fooDiv" - @results[1].text.should eq "Blah" + @results[0][:title].should eq 'fooDiv' + @results[1].text.should eq 'Blah' end end describe '#attr' do - it "should be an attribute" do + it 'should be an attribute' do @results = xpath { |x| x.descendant(:div).where(x.attr(:id)) } - @results[0][:title].should eq "barDiv" - @results[1][:title].should eq "fooDiv" + @results[0][:title].should eq 'barDiv' + @results[1][:title].should eq 'fooDiv' end end describe '#css' do - it "should find nodes by the given CSS selector" do + it 'should find nodes by the given CSS selector' do @results = xpath { |x| x.css('#preference p') } @results[0].text.should eq 'allamas' @results[1].text.should eq 'llama' end - it "should respect previous expression" do + it 'should respect previous expression' do @results = xpath { |x| x.descendant[x.attr(:id) == 'moar'].css('p') } @results[0].text.should eq 'chimp' @results[1].text.should eq 'flamingo' end - it "should be composable" do + it 'should be composable' do @results = xpath { |x| x.css('#moar').descendant(:p) } @results[0].text.should eq 'chimp' @results[1].text.should eq 'flamingo' end - it "should allow comma separated selectors" do + it 'should allow comma separated selectors' do @results = xpath { |x| x.descendant[x.attr(:id) == 'moar'].css('div, p') } @results[0].text.should eq 'chimp' @results[1].text.should eq 'elephant' @@ -420,12 +420,12 @@ def xpath(type=nil, &block) describe '#qname' do it "should match the node's name" do - xpath { |x| x.descendant(:*).where(x.qname == 'ul') }.first.text.should eq "A list" + xpath { |x| x.descendant(:*).where(x.qname == 'ul') }.first.text.should eq 'A list' end end describe '#union' do - it "should create a union expression" do + it 'should create a union expression' do @expr1 = XPath.generate { |x| x.descendant(:p) } @expr2 = XPath.generate { |x| x.descendant(:div) } @collection = @expr1.union(@expr2) @@ -437,7 +437,7 @@ def xpath(type=nil, &block) @results[0][:id].should eq 'fooDiv' end - it "should be aliased as +" do + it 'should be aliased as +' do @expr1 = XPath.generate { |x| x.descendant(:p) } @expr2 = XPath.generate { |x| x.descendant(:div) } @collection = @expr1 + @expr2 @@ -450,113 +450,113 @@ def xpath(type=nil, &block) end end - describe "#last" do - it "returns the number of elements in the context" do + describe '#last' do + it 'returns the number of elements in the context' do @results = xpath { |x| x.descendant(:p)[XPath.position() == XPath.last()] } - @results[0].text.should eq "Bax" - @results[1].text.should eq "Blah" - @results[2].text.should eq "llama" + @results[0].text.should eq 'Bax' + @results[1].text.should eq 'Blah' + @results[2].text.should eq 'llama' end end - describe "#position" do - it "returns the position of elements in the context" do + describe '#position' do + it 'returns the position of elements in the context' do @results = xpath { |x| x.descendant(:p)[XPath.position() == 2] } - @results[0].text.should eq "Bax" - @results[1].text.should eq "Bax" + @results[0].text.should eq 'Bax' + @results[1].text.should eq 'Bax' end end - describe "#count" do - it "counts the number of occurrences" do + describe '#count' do + it 'counts the number of occurrences' do @results = xpath { |x| x.descendant(:div)[x.descendant(:p).count == 2] } - @results[0][:id].should eq "preference" + @results[0][:id].should eq 'preference' end end - describe "#lte" do - it "checks lesser than or equal" do + describe '#lte' do + it 'checks lesser than or equal' do @results = xpath { |x| x.descendant(:p)[XPath.position() <= 2] } - @results[0].text.should eq "Blah" - @results[1].text.should eq "Bax" - @results[2][:title].should eq "gorilla" - @results[3].text.should eq "Bax" + @results[0].text.should eq 'Blah' + @results[1].text.should eq 'Bax' + @results[2][:title].should eq 'gorilla' + @results[3].text.should eq 'Bax' end end - describe "#lt" do - it "checks lesser than" do + describe '#lt' do + it 'checks lesser than' do @results = xpath { |x| x.descendant(:p)[XPath.position() < 2] } - @results[0].text.should eq "Blah" - @results[1][:title].should eq "gorilla" + @results[0].text.should eq 'Blah' + @results[1][:title].should eq 'gorilla' end end - describe "#gte" do - it "checks greater than or equal" do + describe '#gte' do + it 'checks greater than or equal' do @results = xpath { |x| x.descendant(:p)[XPath.position() >= 2] } - @results[0].text.should eq "Bax" - @results[1][:title].should eq "monkey" - @results[2].text.should eq "Bax" - @results[3].text.should eq "Blah" + @results[0].text.should eq 'Bax' + @results[1][:title].should eq 'monkey' + @results[2].text.should eq 'Bax' + @results[3].text.should eq 'Blah' end end - describe "#gt" do - it "checks greater than" do + describe '#gt' do + it 'checks greater than' do @results = xpath { |x| x.descendant(:p)[XPath.position() > 2] } - @results[0][:title].should eq "monkey" - @results[1].text.should eq "Blah" + @results[0][:title].should eq 'monkey' + @results[1].text.should eq 'Blah' end end - describe "#plus" do - it "adds stuff" do + describe '#plus' do + it 'adds stuff' do @results = xpath { |x| x.descendant(:p)[XPath.position().plus(1) == 2] } - @results[0][:id].should eq "fooDiv" - @results[1][:title].should eq "gorilla" + @results[0][:id].should eq 'fooDiv' + @results[1][:title].should eq 'gorilla' end end - describe "#minus" do - it "subtracts stuff" do + describe '#minus' do + it 'subtracts stuff' do @results = xpath { |x| x.descendant(:p)[XPath.position().minus(1) == 0] } - @results[0][:id].should eq "fooDiv" - @results[1][:title].should eq "gorilla" + @results[0][:id].should eq 'fooDiv' + @results[1][:title].should eq 'gorilla' end end - describe "#multiply" do - it "multiplies stuff" do + describe '#multiply' do + it 'multiplies stuff' do @results = xpath { |x| x.descendant(:p)[XPath.position() * 3 == 3] } - @results[0][:id].should eq "fooDiv" - @results[1][:title].should eq "gorilla" + @results[0][:id].should eq 'fooDiv' + @results[1][:title].should eq 'gorilla' end end - describe "#divide" do - it "divides stuff" do + describe '#divide' do + it 'divides stuff' do @results = xpath { |x| x.descendant(:p)[XPath.position() / 2 == 1] } - @results[0].text.should eq "Bax" - @results[1].text.should eq "Bax" + @results[0].text.should eq 'Bax' + @results[1].text.should eq 'Bax' end end - describe "#mod" do - it "take modulo" do + describe '#mod' do + it 'take modulo' do @results = xpath { |x| x.descendant(:p)[XPath.position() % 2 == 1] } - @results[0].text.should eq "Blah" - @results[1][:title].should eq "monkey" - @results[2][:title].should eq "gorilla" + @results[0].text.should eq 'Blah' + @results[1][:title].should eq 'monkey' + @results[2][:title].should eq 'gorilla' end end - describe "#ancestor" do - it "finds ancestor nodes" do + describe '#ancestor' do + it 'finds ancestor nodes' do @results = xpath { |x| x.descendant(:p)[1].ancestor } - @results[0].node_name.should eq "html" - @results[1].node_name.should eq "body" - @results[2][:id].should eq "foo" + @results[0].node_name.should eq 'html' + @results[1].node_name.should eq 'body' + @results[2][:id].should eq 'foo' end end end diff --git a/xpath.gemspec b/xpath.gemspec index 6c04df2..4d5ed21 100644 --- a/xpath.gemspec +++ b/xpath.gemspec @@ -1,31 +1,31 @@ +# frozen_string_literal: true + lib = File.expand_path('lib', File.dirname(__FILE__)) $:.unshift lib unless $:.include?(lib) require 'xpath/version' Gem::Specification.new do |s| - s.name = "xpath" + s.name = 'xpath' s.version = XPath::VERSION - s.required_ruby_version = ">= 2.2" + s.required_ruby_version = '>= 2.3' - s.authors = ["Jonas Nicklas"] - s.email = ["jonas.nicklas@gmail.com"] - s.description = "XPath is a Ruby DSL for generating XPath expressions" - s.license = "MIT" + s.authors = ['Jonas Nicklas'] + s.email = ['jonas.nicklas@gmail.com'] + s.description = 'XPath is a Ruby DSL for generating XPath expressions' + s.license = 'MIT' - s.files = Dir.glob("{lib,spec}/**/*") + %w(README.md) + s.files = Dir.glob('{lib,spec}/**/*') + %w[README.md] - s.homepage = "https://github.com/teamcapybara/xpath" - s.summary = "Generate XPath expressions from Ruby" + s.homepage = 'https://github.com/teamcapybara/xpath' + s.summary = 'Generate XPath expressions from Ruby' - s.add_dependency("nokogiri", ["~> 1.8"]) + s.add_dependency('nokogiri', ['~> 1.8']) - s.add_development_dependency("rspec", ["~> 3.0"]) - s.add_development_dependency("yard", [">= 0.5.8"]) - s.add_development_dependency("rake") - s.add_development_dependency("pry") + s.add_development_dependency('pry') + s.add_development_dependency('rake') + s.add_development_dependency('rspec', ['~> 3.0']) + s.add_development_dependency('yard', ['>= 0.5.8']) - if File.exist?("gem-private_key.pem") - s.signing_key = 'gem-private_key.pem' - end + s.signing_key = 'gem-private_key.pem' if File.exist?('gem-private_key.pem') s.cert_chain = ['gem-public_cert.pem'] end