diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index aa17a67..326fb81 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- ruby: ['2.5', '2.6', '2.7']
+ ruby: ['2.5', '2.6', '2.7', '3.0']
handler: ['nokogiri', 'ox', 'oga']
steps:
- uses: actions/checkout@v2
diff --git a/.ruby-version b/.ruby-version
index 73462a5..cb2b00e 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.5.1
+3.0.1
diff --git a/Gemfile b/Gemfile
index 99c3e27..4c51661 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,12 +8,12 @@ group :development, :test do
gem 'simplecov', require: false, platforms: [:mri]
gem 'coveralls', require: false, platforms: [:mri]
- gem 'activerecord', '~> 5.0.0'
+ gem 'activerecord', '~> 6.0'
gem 'nokogiri', '>= 1.8.2'
gem 'ox', '>= 2.10.0'
gem 'oga', '>= 2.15'
end
group :test do
- gem 'sqlite3', '~> 1.3.13'
+ gem 'sqlite3', '~> 1.4.2'
end
diff --git a/README.md b/README.md
index 5e217fd..1ba23b4 100644
--- a/README.md
+++ b/README.md
@@ -182,6 +182,25 @@ document determines the value assigned to the alias.
If an element is defined in the source but is blank (e.g., ``), it is ignored, and non-empty one is picked.
+## Parsing Errors
+By default, there are no notification or exceptions on parsing errors and warnings. For Nokogiri and Ox, you can
+specify a custom behavior by passing procs that receive a string:
+
+```ruby
+on_error = ->(error_string) { raise error_string }
+on_warning = ->(warning_string) { raise warning_string }
+
+feed = Atom.parse(xml, on_error, on_warning)
+```
+
+You can also use a global setting (e.g. in `config/initializers/saxophone.rb` when using Saxophone together
+with Ruby on Rails):
+
+```ruby
+Saxophone.on_error = ->(error_string) { raise error_string }
+Saxophone.on_warning = ->(warning_string) { raise warning_string }
+```
+
## Contributing
1. Fork it
diff --git a/lib/saxophone.rb b/lib/saxophone.rb
index 682de3c..25bbbad 100644
--- a/lib/saxophone.rb
+++ b/lib/saxophone.rb
@@ -14,6 +14,22 @@ def self.handler=(handler)
@@handler = handler
end
end
+
+ def self.on_error
+ @@on_error ||= ->(_) {}
+ end
+
+ def self.on_error=(on_error_proc)
+ @@on_error = on_error_proc
+ end
+
+ def self.on_warning
+ @@on_warning ||= ->(_) {}
+ end
+
+ def self.on_warning=(on_warning_proc)
+ @@on_warning = on_warning_proc
+ end
end
# Try handlers
diff --git a/lib/saxophone/sax_document.rb b/lib/saxophone/sax_document.rb
index 8e27510..1445b8a 100644
--- a/lib/saxophone/sax_document.rb
+++ b/lib/saxophone/sax_document.rb
@@ -4,7 +4,7 @@ def self.included(base)
base.extend(ClassMethods)
end
- def parse(xml_input, on_error = ->(_){}, on_warning = ->(_){})
+ def parse(xml_input, on_error = Saxophone.on_error, on_warning = Saxophone.on_warning)
handler_klass = Saxophone.const_get("SAX#{Saxophone.handler.capitalize}Handler")
handler = handler_klass.new(self, on_error, on_warning)
@@ -15,7 +15,7 @@ def parse(xml_input, on_error = ->(_){}, on_warning = ->(_){})
module InstanceMethods
def initialize(attributes = {})
- attributes.each do |name, value|
+ attributes&.each do |name, value|
send("#{name}=", value)
end
diff --git a/lib/saxophone/version.rb b/lib/saxophone/version.rb
index 4b8492c..5cb53c2 100644
--- a/lib/saxophone/version.rb
+++ b/lib/saxophone/version.rb
@@ -1,3 +1,3 @@
module Saxophone
- VERSION = "1.0.0"
+ VERSION = "1.1.0"
end
diff --git a/spec/saxophone/global_config_spec.rb b/spec/saxophone/global_config_spec.rb
new file mode 100644
index 0000000..6045798
--- /dev/null
+++ b/spec/saxophone/global_config_spec.rb
@@ -0,0 +1,88 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe "Saxophone global configurations" do
+ before(:all) do
+ class A
+ include Saxophone
+ end
+ end
+
+ after(:all) do
+ Object.send(:remove_const, :A)
+ end
+
+ describe "handler config" do
+ after(:all) do
+ Saxophone.handler = :ox # restore default settings
+ end
+
+ context "with handler set to ox" do
+ before do
+ Saxophone.handler = :ox
+ end
+
+ it "uses ox as handler" do
+ expect(Saxophone.handler).to eq :ox
+ end
+ end
+
+ context "with handler set to oga" do
+ before do
+ Saxophone.handler = :oga
+ end
+
+ it "uses oga as handler" do
+ expect(Saxophone.handler).to eq :oga
+ end
+ end
+
+ context "with handler set to nokogiri" do
+ before do
+ Saxophone.handler = :nokogiri
+ end
+
+ it "uses nokogiri as handler" do
+ expect(Saxophone.handler).to eq :nokogiri
+ end
+ end
+
+ context "with handler set to some other value" do
+ it "raises error" do
+ expect { Saxophone.handler = :not_a_valid_handler }.to raise_error(LoadError)
+ end
+ end
+ end
+
+ describe "global on_error config" do
+ broken_xml = "Te & stMatched!And Again"
+ xml = "TestMatched!And Again"
+
+ context "not configured" do
+ it "does not raise exception on valid xml" do
+ expect { A.parse xml }.not_to raise_error
+ end
+
+ it "does not raise exception on broken xml" do
+ expect { A.parse broken_xml }.not_to raise_error
+ end
+ end
+
+ context "configured to raise exception" do
+ before(:all) do
+ Saxophone.on_error = ->(error_string) { raise error_string }
+ end
+
+ after(:all) do
+ Saxophone.on_error = ->(_) {} # restore default settings
+ end
+
+ it "does not raise exception on valid xml" do
+ expect { A.parse xml }.not_to raise_error
+ end
+
+ it "raises exception on broken xml" do
+ expect { A.parse broken_xml }.to raise_error(RuntimeError)
+ end
+ end
+ end
+end