sax-machine 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eced69c84dfa4d14f4e55e8f8f57af2c90f69281
4
- data.tar.gz: a00ec939b3a116885109a44283c68d0d301ebb63
3
+ metadata.gz: d0fb49e4c86a5d258cd1266f3b39f0279ff6ecc4
4
+ data.tar.gz: 9221aa8a9a9a552f1ac9ce7e5b962db2e5987515
5
5
  SHA512:
6
- metadata.gz: e728531fb1931246e61c4ecc5182f51f30ec2d7b4131976c282313f2db08c324a679c6f58f01fd02c2816fae380e4c711324764ca8cc192c04f4d8702218640f
7
- data.tar.gz: 3014692c51809a0dd4cd0f0656fdc63d6d669ad9d3ba863fec64ac5aa0238a7b0dda83658b693ff4de800d8073ddf55d0bb6f32f7b3ac4370bbb24da9428606b
6
+ metadata.gz: 6da8e076f9d871aebfee80887b007712768c60643d5690b514cd886758583e132971417533afb560de2a4c8aee988131a46a62da28b32be9ed2837c7f4cf9e86
7
+ data.tar.gz: 8f28dd9652e24a9d853ded552df3ca25e80cba2eead23bb1ead8d331bc50768778938d7709ab3ebaa7cc11c3ebb411c74de119b8c64897f3d1ae7a41c3457911
data/Gemfile CHANGED
@@ -9,5 +9,6 @@ group :development, :test do
9
9
  gem 'coveralls', require: false, platforms: [:mri]
10
10
 
11
11
  gem 'activerecord', '~> 4.1'
12
+ gem 'nokogiri', '~> 1.6'
12
13
  gem 'ox', '>= 2.1.2', platforms: [:mri, :rbx]
13
14
  end
data/HISTORY.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # HEAD
2
2
 
3
+ # 1.0.0
4
+
5
+ * Make `nokogiri` dependency optional
6
+ * Add :default argument for elements [[#51](https://github.com/pauldix/sax-machine/pull/51)]
3
7
 
4
8
  # 0.3.0
5
9
 
data/README.md CHANGED
@@ -28,30 +28,26 @@ $ bundle
28
28
 
29
29
  ## Usage
30
30
 
31
- To use **Nokogiri** as a SAX handler:
31
+ SAX Machine can use either `nokogiri` or `ox` as XML SAX handler.
32
+
33
+ To use **Nokogiri** add this line to your Gemfile:
32
34
 
33
35
  ```ruby
34
- require 'sax-machine'
36
+ gem 'nokogiri', '~> 1.6'
35
37
  ```
36
38
 
37
- To use **Ox** as a SAX handler:
38
-
39
- Add this line to your application's Gemfile:
39
+ To use **Ox** add this line to your Gemfile:
40
40
 
41
41
  ```ruby
42
42
  gem 'ox', '>= 2.1.2'
43
43
  ```
44
44
 
45
- Tell SAXMachine to use Ox:
45
+ You can also specify which handler to use manually, like this:
46
46
 
47
47
  ```ruby
48
- require 'sax-machine'
49
- SAXMachine.handler = :ox
48
+ SAXMachine.handler = :nokogiri
50
49
  ```
51
50
 
52
- Please note that this operation is not thread-safe, so it's better to define
53
- handler at initialization stage and do not change it during runtime.
54
-
55
51
  ## Examples
56
52
 
57
53
  Include `SAXMachine` in any class and define properties to parse:
@@ -69,7 +65,8 @@ class AtomEntry
69
65
  # The :as argument makes this available through entry.author instead of .name
70
66
  element :name, as: :author
71
67
  element "feedburner:origLink", as: :url
72
- element :summary
68
+ # The :default argument specifies default value for element when it's missing
69
+ element :summary, class: String, default: "No summary available"
73
70
  element :content, class: AtomContent
74
71
  element :published
75
72
  ancestor :ancestor
@@ -104,6 +101,7 @@ feed.feed_url # The URL of the blog feed
104
101
  feed.entries.first.title # Title of the first entry
105
102
  feed.entries.first.author # The author of the first entry
106
103
  feed.entries.first.url # Permalink on the blog for this entry
104
+ feed.entries.first.summary # Returns "No summary available" if summary is missing
107
105
  feed.entries.first.ancestor # The Atom ancestor
108
106
  feed.entries.first.content # Instance of AtomContent
109
107
  feed.entries.first.content.text # Entry content text
@@ -14,4 +14,8 @@ module SAXMachine
14
14
  end
15
15
  end
16
16
 
17
- SAXMachine.handler = :nokogiri
17
+ begin
18
+ SAXMachine.handler = :ox
19
+ rescue LoadError
20
+ SAXMachine.handler = :nokogiri
21
+ end
@@ -25,10 +25,7 @@ module SAXMachine
25
25
  def attrs_match?(attrs)
26
26
  attrs.key?(@name) || attrs.value?(@name)
27
27
  end
28
-
29
- def has_value_and_attrs_match?(attrs)
30
- attrs_match?(attrs)
31
- end
28
+ alias_method :has_value_and_attrs_match?, :attrs_match?
32
29
 
33
30
  def collection?
34
31
  false
@@ -1,7 +1,7 @@
1
1
  module SAXMachine
2
2
  class SAXConfig
3
3
  class ElementConfig
4
- attr_reader :name, :setter, :data_class, :collection
4
+ attr_reader :name, :as, :setter, :data_class, :collection, :default
5
5
 
6
6
  def initialize(name, options)
7
7
  @name = name.to_s
@@ -15,6 +15,7 @@ module SAXMachine
15
15
 
16
16
  @as = options[:as]
17
17
  @collection = options[:collection]
18
+ @default = options[:default]
18
19
 
19
20
  @setter = if @collection
20
21
  "add_#{options[:as]}"
@@ -104,19 +104,18 @@ module SAXMachine
104
104
  else
105
105
  value =
106
106
  case config.data_class.to_s
107
- when "String" then value.to_s
108
- when "Integer" then value.to_i
109
- when "Float" then value.to_f
107
+ when "String" then value != NO_BUFFER ? value.to_s : value
108
+ when "Integer" then value != NO_BUFFER ? value.to_i : value
109
+ when "Float" then value != NO_BUFFER ? value.to_f : value
110
110
  # Assumes that time elements will be string-based and are not
111
111
  # something else, e.g. seconds since epoch
112
- when "Time" then Time.parse(value.to_s)
112
+ when "Time" then value != NO_BUFFER ? Time.parse(value.to_s) : value
113
113
  when "" then value
114
114
  else
115
115
  element
116
116
  end
117
117
 
118
118
  object.send(config.setter, value) unless value == NO_BUFFER
119
-
120
119
  mark_as_parsed(object, config)
121
120
  end
122
121
 
@@ -18,7 +18,7 @@ module SAXMachine
18
18
  end
19
19
 
20
20
  def columns
21
- @top_level_elements.map { |name, ecs| ecs }.flatten
21
+ @top_level_elements.map { |_, ecs| ecs }.flatten
22
22
  end
23
23
 
24
24
  def initialize_copy(sax_config)
@@ -33,6 +33,15 @@ module SAXMachine
33
33
  attributes.each do |name, value|
34
34
  send("#{name}=", value)
35
35
  end
36
+
37
+ self.class.sax_config.top_level_elements.each do |_, configs|
38
+ configs.each do |config|
39
+ next unless config.default
40
+ next unless send(config.as).nil?
41
+
42
+ send(config.setter, config.default)
43
+ end
44
+ end
36
45
  end
37
46
  end
38
47
 
@@ -48,19 +57,19 @@ module SAXMachine
48
57
  def element(name, options = {})
49
58
  real_name = (options[:as] ||= name).to_s
50
59
  sax_config.add_top_level_element(name, options)
51
- create_attr real_name
60
+ create_attr(real_name)
52
61
  end
53
62
 
54
63
  def attribute(name, options = {})
55
64
  real_name = (options[:as] ||= name).to_s
56
65
  sax_config.add_top_level_attribute(self.class.to_s, options.merge(name: name))
57
- create_attr real_name
66
+ create_attr(real_name)
58
67
  end
59
68
 
60
69
  def value(name, options = {})
61
70
  real_name = (options[:as] ||= name).to_s
62
71
  sax_config.add_top_level_element_value(self.class.to_s, options.merge(name: name))
63
- create_attr real_name
72
+ create_attr(real_name)
64
73
  end
65
74
 
66
75
  def ancestor(name, options = {})
@@ -1,3 +1,3 @@
1
1
  module SAXMachine
2
- VERSION = "0.3.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -16,6 +16,5 @@ Gem::Specification.new do |s|
16
16
  s.require_paths = ["lib"]
17
17
  s.platform = Gem::Platform::RUBY
18
18
 
19
- s.add_dependency "nokogiri", "~> 1.6.0"
20
19
  s.add_development_dependency "rspec", "~> 3.0"
21
20
  end
@@ -1,23 +1,21 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
  require 'active_record'
3
3
 
4
- describe "ActiveRecord" do
5
- describe "integration" do
6
- before :each do
7
- class MySaxModel < ActiveRecord::Base
8
- SAXMachine.configure(MySaxModel) do |c|
9
- c.element :title
10
- end
4
+ describe "SAXMachine ActiveRecord integration" do
5
+ before do
6
+ class MySaxModel < ActiveRecord::Base
7
+ SAXMachine.configure(MySaxModel) do |c|
8
+ c.element :title
11
9
  end
12
10
  end
11
+ end
13
12
 
14
- after :each do
15
- Object.send(:remove_const, :MySaxModel)
16
- end
13
+ after do
14
+ Object.send(:remove_const, :MySaxModel)
15
+ end
17
16
 
18
- it "parses document" do
19
- document = MySaxModel.parse("<xml><title>My Title</title></xml>")
20
- expect(document.title).to eq("My Title")
21
- end
17
+ it "parses document" do
18
+ document = MySaxModel.parse("<xml><title>My Title</title></xml>")
19
+ expect(document.title).to eq("My Title")
22
20
  end
23
21
  end
@@ -1,32 +1,35 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
- class A
4
- SAXMachine.configure(A) do |c|
5
- c.element :title
6
- end
7
- end
3
+ describe "SAXMachine configure" do
4
+ before do
5
+ class A
6
+ SAXMachine.configure(A) do |c|
7
+ c.element :title
8
+ end
9
+ end
8
10
 
9
- class B < A
10
- SAXMachine.configure(B) do |c|
11
- c.element :b
12
- end
13
- end
11
+ class B < A
12
+ SAXMachine.configure(B) do |c|
13
+ c.element :b
14
+ end
15
+ end
14
16
 
15
- class C < B
16
- SAXMachine.configure(C) do |c|
17
- c.element :c
18
- end
19
- end
17
+ class C < B
18
+ SAXMachine.configure(C) do |c|
19
+ c.element :c
20
+ end
21
+ end
20
22
 
21
- describe "SAXMachine configure" do
22
- before do
23
23
  xml = "<top><title>Test</title><b>Matched!</b><c>And Again</c></top>"
24
- @a = A.new
25
- @a.parse xml
26
- @b = B.new
27
- @b.parse xml
28
- @c = C.new
29
- @c.parse xml
24
+ @a = A.parse xml
25
+ @b = B.parse xml
26
+ @c = C.parse xml
27
+ end
28
+
29
+ after do
30
+ Object.send(:remove_const, :A)
31
+ Object.send(:remove_const, :B)
32
+ Object.send(:remove_const, :C)
30
33
  end
31
34
 
32
35
  it { expect(@a).to be_a(A) }
@@ -3,10 +3,13 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
3
  describe "SAXMachine" do
4
4
  describe "element" do
5
5
  describe "when parsing a single element" do
6
- before :each do
6
+ before do
7
7
  @klass = Class.new do
8
8
  include SAXMachine
9
9
  element :title
10
+ ancestor :body
11
+ value :something, required: false
12
+ attribute :anything, required: true
10
13
  end
11
14
  end
12
15
 
@@ -21,10 +24,6 @@ describe "SAXMachine" do
21
24
  expect(document.title).to eq("Title")
22
25
  end
23
26
 
24
- it "allows introspection of the elements" do
25
- expect(@klass.column_names).to match_array([:title])
26
- end
27
-
28
27
  it "does not overwrites the getter is there is already one present" do
29
28
  @klass = Class.new do
30
29
  def title
@@ -97,6 +96,38 @@ describe "SAXMachine" do
97
96
  expect(document.title).to eq("My Title")
98
97
  end
99
98
 
99
+ describe "the introspection" do
100
+ it "allows to get column names" do
101
+ expect(@klass.column_names).to match_array([:title])
102
+ end
103
+
104
+ it "allows to get elements" do
105
+ expect(@klass.sax_config.top_level_elements.values.flatten.map(&:to_s)).to \
106
+ match_array(["name: title dataclass: setter: title= required: value: as:title collection: with: {}"])
107
+ end
108
+
109
+ it "allows to get ancestors" do
110
+ expect(@klass.sax_config.ancestors.map(&:column)).to \
111
+ match_array([:body])
112
+ end
113
+
114
+ it "allows to get values" do
115
+ expect(@klass.sax_config.top_level_element_value.map(&:column)).to \
116
+ match_array([:something])
117
+ expect(@klass.sax_config.top_level_element_value.map(&:required?)).to \
118
+ match_array([false])
119
+ end
120
+
121
+ it "allows to get attributes" do
122
+ expect(@klass.sax_config.top_level_attributes.map(&:column)).to \
123
+ match_array([:anything])
124
+ expect(@klass.sax_config.top_level_attributes.map(&:required?)).to \
125
+ match_array([true])
126
+ expect(@klass.sax_config.top_level_attributes.map(&:collection?)).to \
127
+ match_array([false])
128
+ end
129
+ end
130
+
100
131
  describe "the class attribute" do
101
132
  before(:each) do
102
133
  @klass = Class.new do
@@ -153,6 +184,21 @@ describe "SAXMachine" do
153
184
  end
154
185
  end
155
186
 
187
+ describe "the default attribute" do
188
+ it "is available" do
189
+ @klass = Class.new do
190
+ include SAXMachine
191
+ element :number, class: Integer, default: 0
192
+ end
193
+
194
+ document = @klass.parse("<no>number</no>")
195
+ expect(document.number).to eq(0)
196
+
197
+ document = @klass.parse("<number></number>")
198
+ expect(document.number).to eq(0)
199
+ end
200
+ end
201
+
156
202
  describe "the required attribute" do
157
203
  it "is available" do
158
204
  @klass = Class.new do
@@ -165,7 +211,7 @@ describe "SAXMachine" do
165
211
  end
166
212
 
167
213
  describe "when parsing multiple elements" do
168
- before :each do
214
+ before do
169
215
  @klass = Class.new do
170
216
  include SAXMachine
171
217
  element :title
@@ -212,7 +258,7 @@ describe "SAXMachine" do
212
258
 
213
259
  describe "when using options for parsing elements" do
214
260
  describe "using the 'as' option" do
215
- before :each do
261
+ before do
216
262
  @klass = Class.new do
217
263
  include SAXMachine
218
264
  element :description, as: :summary
@@ -233,7 +279,7 @@ describe "SAXMachine" do
233
279
 
234
280
  describe "using the :with option" do
235
281
  describe "and the :value option" do
236
- before :each do
282
+ before do
237
283
  @klass = Class.new do
238
284
  include SAXMachine
239
285
  element :link, value: :href, with: { foo: "bar" }
@@ -251,7 +297,7 @@ describe "SAXMachine" do
251
297
  end
252
298
 
253
299
  describe "and the :as option" do
254
- before :each do
300
+ before do
255
301
  @klass = Class.new do
256
302
  include SAXMachine
257
303
  element :link, value: :href, as: :url, with: { foo: "bar" }
@@ -268,7 +314,7 @@ describe "SAXMachine" do
268
314
  end
269
315
 
270
316
  describe "with only one element" do
271
- before :each do
317
+ before do
272
318
  @klass = Class.new do
273
319
  include SAXMachine
274
320
  element :link, with: { foo: "bar" }
@@ -297,7 +343,7 @@ describe "SAXMachine" do
297
343
  end
298
344
 
299
345
  describe "with multiple elements of same tag" do
300
- before :each do
346
+ before do
301
347
  @klass = Class.new do
302
348
  include SAXMachine
303
349
  element :link, as: :first, with: { foo: "bar" }
@@ -317,7 +363,7 @@ describe "SAXMachine" do
317
363
  end
318
364
 
319
365
  describe "with only one element as a regular expression" do
320
- before :each do
366
+ before do
321
367
  @klass = Class.new do
322
368
  include SAXMachine
323
369
  element :link, with: { foo: /ar$/ }
@@ -347,7 +393,7 @@ describe "SAXMachine" do
347
393
  end
348
394
 
349
395
  describe "using the 'value' option" do
350
- before :each do
396
+ before do
351
397
  @klass = Class.new do
352
398
  include SAXMachine
353
399
  element :link, value: :foo
@@ -395,7 +441,7 @@ describe "SAXMachine" do
395
441
  end
396
442
 
397
443
  describe "when desiring both the content and attributes of an element" do
398
- before :each do
444
+ before do
399
445
  @klass = Class.new do
400
446
  include SAXMachine
401
447
  element :link
@@ -416,7 +462,7 @@ describe "SAXMachine" do
416
462
 
417
463
  describe "elements" do
418
464
  describe "when parsing multiple elements" do
419
- before :each do
465
+ before do
420
466
  @klass = Class.new do
421
467
  include SAXMachine
422
468
  elements :entry, as: :entries
@@ -451,7 +497,7 @@ describe "SAXMachine" do
451
497
  end
452
498
 
453
499
  describe "when using the with and class options" do
454
- before :each do
500
+ before do
455
501
  class Bar
456
502
  include SAXMachine
457
503
  element :title
@@ -484,7 +530,7 @@ describe "SAXMachine" do
484
530
  end
485
531
 
486
532
  describe "when using the class option" do
487
- before :each do
533
+ before do
488
534
  class Foo
489
535
  include SAXMachine
490
536
  element :title
@@ -559,7 +605,7 @@ describe "SAXMachine" do
559
605
  end
560
606
 
561
607
  describe "full example" do
562
- before :each do
608
+ before do
563
609
  @xml = File.read("spec/fixtures/atom.xml")
564
610
 
565
611
  class AtomEntry
@@ -881,11 +927,20 @@ describe "SAXMachine" do
881
927
  end
882
928
 
883
929
  @errors = []
884
- @item = ItemElement5.parse(@xml, ->(x) { @errors << x })
930
+ @warnings = []
931
+ @item = ItemElement5.parse(
932
+ @xml,
933
+ ->(x) { @errors << x },
934
+ ->(x) { @warnings << x },
935
+ )
885
936
  end
886
937
 
887
938
  it "has error" do
888
939
  expect(@errors.uniq.size).to eq(1)
889
940
  end
941
+
942
+ it "has no warning" do
943
+ expect(@warnings.uniq.size).to eq(0)
944
+ end
890
945
  end
891
946
  end
@@ -1,20 +1,20 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
- class A
4
- include SAXMachine
5
- element :title
6
- end
3
+ describe "SAXMachine inheritance" do
4
+ before do
5
+ class A
6
+ include SAXMachine
7
+ element :title
8
+ end
7
9
 
8
- class B < A
9
- element :b
10
- end
10
+ class B < A
11
+ element :b
12
+ end
11
13
 
12
- class C < B
13
- element :c
14
- end
14
+ class C < B
15
+ element :c
16
+ end
15
17
 
16
- describe "SAXMachine inheritance" do
17
- before do
18
18
  xml = "<top><title>Test</title><b>Matched!</b><c>And Again</c></top>"
19
19
  @a = A.new
20
20
  @a.parse xml
@@ -24,6 +24,12 @@ describe "SAXMachine inheritance" do
24
24
  @c.parse xml
25
25
  end
26
26
 
27
+ after do
28
+ Object.send(:remove_const, :A)
29
+ Object.send(:remove_const, :B)
30
+ Object.send(:remove_const, :C)
31
+ end
32
+
27
33
  it { expect(@a).to be_a(A) }
28
34
  it { expect(@a).not_to be_a(B) }
29
35
  it { expect(@a).to be_a(SAXMachine) }
@@ -14,9 +14,7 @@ rescue LoadError
14
14
  end
15
15
 
16
16
  require File.expand_path(File.dirname(__FILE__) + '/../lib/sax-machine')
17
- if ENV['HANDLER'] == 'ox'
18
- SAXMachine.handler = :ox
19
- end
17
+ SAXMachine.handler = ENV['HANDLER'].to_sym
20
18
 
21
19
  RSpec.configure do |config|
22
20
  config.run_all_when_everything_filtered = true
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sax-machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Dix
@@ -11,22 +11,8 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-07-22 00:00:00.000000000 Z
14
+ date: 2014-08-05 00:00:00.000000000 Z
15
15
  dependencies:
16
- - !ruby/object:Gem::Dependency
17
- name: nokogiri
18
- requirement: !ruby/object:Gem::Requirement
19
- requirements:
20
- - - "~>"
21
- - !ruby/object:Gem::Version
22
- version: 1.6.0
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: 1.6.0
30
16
  - !ruby/object:Gem::Dependency
31
17
  name: rspec
32
18
  requirement: !ruby/object:Gem::Requirement