pump 0.0.1 → 0.0.2

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.
data/README.md CHANGED
@@ -1,29 +1,13 @@
1
1
  # Pump
2
2
 
3
- TODO: Write a gem description
3
+ Fast but inflexible XML encoding for ruby objects.
4
4
 
5
- ## Installation
5
+ ## Quick benchmark
6
6
 
7
- Add this line to your application's Gemfile:
7
+ Serializing an array of 100 random entries 1.000 times.
8
8
 
9
- gem 'pump'
10
-
11
- And then execute:
12
-
13
- $ bundle
14
-
15
- Or install it yourself as:
16
-
17
- $ gem install pump
18
-
19
- ## Usage
20
-
21
- TODO: Write usage instructions here
22
-
23
- ## Contributing
24
-
25
- 1. Fork it
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create new Pull Request
9
+ user system total real
10
+ Pump::Xml#encode 1.560000 0.020000 1.580000 ( 1.581375)
11
+ Pump::Xml#encode (optimized) 1.060000 0.010000 1.070000 ( 1.067321)
12
+ Ox 1.470000 0.000000 1.470000 ( 1.467247)
13
+ ActiveModel#serialize 22.840000 0.040000 22.880000 ( 22.871247)
@@ -0,0 +1,107 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rubygems'
4
+ require 'benchmark'
5
+
6
+ require 'pump'
7
+ require 'ox' rescue nil
8
+ require 'active_model' rescue nil
9
+
10
+ class Person < Struct.new(:name, :age, :created_at)
11
+ include ActiveModel::Serializers::Xml if defined?(ActiveModel)
12
+
13
+ def attributes
14
+ {'name' => name, 'age' => age, 'created_at' => created_at}
15
+ end
16
+ end
17
+
18
+ # Not optimized pump
19
+ pump = Pump::Xml.new('person', [
20
+ {:age => :age, :attributes => {:type => 'integer'}},
21
+ {:"created-at" => :created_at, :typecast => :xmlschema, :attributes => {:type => 'datetime'}, :never_blank => true},
22
+ {:name => :name}
23
+ ])
24
+
25
+ # Heavily optimized pump
26
+ pump_optimized = Pump::Xml.new('person', [
27
+ {:age => :age, :attributes => {:type => 'integer'}, :never_blank => true, :skip_encoding => true},
28
+ {:"created-at" => :created_at, :typecast => :xmlschema, :attributes => {:type => 'datetime'}, :never_blank => true, :skip_encoding => true},
29
+ {:name => :name, :never_blank => true}
30
+ ])
31
+
32
+ if defined?(Ox)
33
+ def serialize_with_ox(people)
34
+ doc = Ox::Document.new(:version => '1.0', :encoding => 'UTF-8')
35
+ root = Ox::Element.new('people')
36
+ root[:type] = "array"
37
+ people.each{|person| serialize_single_with_ox(person, root) }
38
+ doc << root
39
+ Ox.dump(doc, :with_xml => true)
40
+ end
41
+
42
+ def serialize_single_with_ox(person, base_root=nil)
43
+ root = Ox::Element.new('person')
44
+
45
+ created_at = Ox::Element.new('created-at')
46
+ created_at[:type] = "datetime"
47
+ created_at << person.created_at.xmlschema
48
+ root << created_at
49
+
50
+ age = Ox::Element.new('age')
51
+ age[:type] = "integer"
52
+ age << person.age.inspect
53
+ root << age
54
+
55
+ name = Ox::Element.new('name')
56
+ name << person.name
57
+ root << name
58
+
59
+ unless base_root
60
+ doc = Ox::Document.new(:version => '1.0', :encoding => 'UTF-8')
61
+ doc << root
62
+ Ox.dump(doc, :with_xml => true)
63
+ else
64
+ base_root << root
65
+ end
66
+ end
67
+ end
68
+
69
+ # Lets generate some random persons
70
+ array = []
71
+ 100.times do
72
+ array << Person.new((0...(rand(15)+5)).map{ ('a'..'z').to_a[rand(26)] }.join, rand(100), Time.now + rand(1000000))
73
+ end
74
+
75
+ times = ARGV[1] ? ARGV[1].to_i : 1000
76
+ puts "Starting benchmark serializing array with #{array.size} entries #{times} times\n\n"
77
+
78
+ Benchmark.bmbm { |x|
79
+
80
+ x.report("Pump::Xml#encode") {
81
+ times.times {
82
+ pump.encode(array)
83
+ }
84
+ }
85
+
86
+ x.report("Pump::Xml#encode (optimized)") {
87
+ times.times {
88
+ pump_optimized.encode(array)
89
+ }
90
+ }
91
+
92
+ if defined?(Ox)
93
+ x.report("Ox") {
94
+ times.times {
95
+ serialize_with_ox(array)
96
+ }
97
+ }
98
+ end
99
+
100
+ if defined?(ActiveModel)
101
+ x.report("ActiveModel#serialize") {
102
+ times.times {
103
+ array.to_xml
104
+ }
105
+ }
106
+ end
107
+ }
data/lib/pump/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pump
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/pump/xml/node.rb CHANGED
@@ -7,7 +7,7 @@ module Pump
7
7
  def initialize(name, attributes={}, nodes=[], options={})
8
8
  @name = name
9
9
  @attributes = attributes || {}
10
- @options = options || {}
10
+ @options = (options || {}).dup
11
11
  @nodes = []
12
12
  Array(nodes).each{|node| add_node(node) }
13
13
  end
@@ -19,6 +19,7 @@ module Pump
19
19
 
20
20
  def add_node(node)
21
21
  node.level = level + 1
22
+ node.options[:extra_indent] = options[:extra_indent]
22
23
  nodes << node
23
24
  end
24
25
 
@@ -26,8 +27,12 @@ module Pump
26
27
  @level || options[:level] || 0
27
28
  end
28
29
 
29
- def indent
30
- (level)*(options[:indent] || 2)
30
+ def tabs
31
+ " " * ((level + extra_indent) * 2)
32
+ end
33
+
34
+ def extra_indent
35
+ options[:extra_indent] || 0
31
36
  end
32
37
  end
33
38
  end
data/lib/pump/xml/tag.rb CHANGED
@@ -7,9 +7,7 @@ module Pump
7
7
 
8
8
  def initialize(*args)
9
9
  super
10
- if value_nodes?
11
- nodes.first.options = options
12
- end
10
+ nodes.first.options = options if value_nodes?
13
11
  end
14
12
 
15
13
  def to_s
@@ -32,15 +30,15 @@ module Pump
32
30
 
33
31
  def prefix
34
32
  if level == 0
35
- options[:instruct] ? INSTRUCT : ""
33
+ options[:instruct] ? INSTRUCT : (tabs)
36
34
  else
37
- "\n#{" "*indent}"
35
+ "\n#{tabs}"
38
36
  end
39
37
  end
40
38
 
41
39
  def value_and_close_tag(path=nil)
42
40
  value = value_nodes? ? nodes.first.to_s(path) : (nodes.map(&:to_s).join << "\n")
43
- ">#{value}</#{name}>"
41
+ ">#{value}#{tabs unless value_nodes?}</#{name}>"
44
42
  end
45
43
 
46
44
  def value_and_close_tag_with_blank_check
@@ -0,0 +1,32 @@
1
+ require 'pump/xml/node'
2
+ require 'active_support/core_ext/string/inflections'
3
+
4
+ module Pump
5
+ class Xml
6
+ class TagArray < Node
7
+ def initialize(name, attributes={}, nodes=[], options={})
8
+ tag = Tag.new(name, attributes, nodes, {:level => 1, :extra_indent => options[:extra_indent]})
9
+ array_root = options[:array_root] || name.pluralize
10
+ super(array_root, {}, [tag], options)
11
+ end
12
+
13
+ def to_s
14
+ "#{prefix}<#{name} type=\\\"array\\\"#{loop_and_close_tag}"
15
+ end
16
+
17
+ private
18
+
19
+ def prefix
20
+ options[:instruct] ? "#{Tag::INSTRUCT}" : tabs
21
+ end
22
+
23
+ def loop_and_close_tag
24
+ "\#{ objects.empty? ? \" />\" : \">#{tag_loop}#{tabs}</#{name}>\" }"
25
+ end
26
+
27
+ def tag_loop
28
+ "\#{objects.map{|object| \"#{nodes.first}\" }.join('')}\n"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -10,7 +10,17 @@ module Pump
10
10
  end
11
11
 
12
12
  def to_s(plain_path=nil)
13
- "\#{#{plain_path || plain}#{'.to_s.encode(:xml => :text)' unless options[:skip_encoding]}}"
13
+ "\#{#{plain_path || plain}#{cast}}"
14
+ end
15
+
16
+ private
17
+
18
+ def cast
19
+ if options[:typecast]
20
+ ".#{options[:typecast]}"
21
+ elsif !options[:skip_encoding]
22
+ '.to_s.encode(:xml => :text)'
23
+ end
14
24
  end
15
25
  end
16
26
  end
data/lib/pump/xml.rb CHANGED
@@ -1,38 +1,54 @@
1
1
  require "pump/xml/tag"
2
2
  require "pump/xml/value"
3
+ require "pump/xml/tag_array"
3
4
 
4
5
  module Pump
5
6
  class Xml
6
- def initialize(root_name, tags)
7
- @root_name = root_name
8
- @tags = tags
9
- build
7
+ attr_reader :root_tag_name, :tag_config, :options
8
+
9
+ def initialize(root_tag_name, tag_config, options={})
10
+ @root_tag_name = root_tag_name
11
+ @tag_config = tag_config
12
+ @options = options
13
+
14
+ compile
10
15
  end
11
16
 
12
- def serialize(object)
13
- # "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<#{@root_name}>\n <name>#{record.name}</name>\n</#{@root_name}>"
17
+ def encode(object)
18
+ Array === object ? encode_array(object) : encode_single(object)
14
19
  end
15
20
 
16
21
  private
17
22
 
18
- def build
19
- self.instance_eval build_string
23
+ def compile
24
+ instance_eval(compile_string)
20
25
  end
21
26
 
22
- def build_string
23
- root_node = Tag.new(@root_name, {}, tags, {:instruct => true})
27
+ def compile_string
24
28
  <<-EOV
25
- def serialize(object)
26
- "#{root_node}"
29
+ def encode_single(object)
30
+ "#{Tag.new(root_tag_name, {}, sub_tags, tag_options)}"
31
+ end
32
+
33
+ def encode_array(objects)
34
+ "#{TagArray.new(root_tag_name, {}, sub_tags, tag_options)}"
27
35
  end
28
36
  EOV
29
37
  end
30
38
 
31
- def tags
32
- @tags.map do |options|
33
- tag_name, method_name = options.keys.first, options.values.first
34
- Tag.new(tag_name, options[:attributes], Value.new(method_name), options)
39
+ def sub_tags
40
+ tag_config.map do |config|
41
+ tag_name, method_name = config.keys.first, config.values.first
42
+ Tag.new(tag_name, config[:attributes], Value.new(method_name), config)
35
43
  end
36
44
  end
45
+
46
+ def tag_options
47
+ {:instruct => add_instruct?, :extra_indent => options[:extra_indent], :array_root => options[:array_root] }
48
+ end
49
+
50
+ def add_instruct?
51
+ options.has_key?(:instruct) ? options[:instruct] : true
52
+ end
37
53
  end
38
54
  end
data/pump.gemspec CHANGED
@@ -8,15 +8,17 @@ Gem::Specification.new do |gem|
8
8
  gem.version = Pump::VERSION
9
9
  gem.authors = ["Sebastian Munz"]
10
10
  gem.email = ["sebastian@yo.lk"]
11
- gem.description = %q{Fast but inflexible XML dumping for ruby objects.}
12
- gem.summary = %q{Fast but inflexible XML dumping for ruby objects.}
11
+ gem.description = %q{Fast but inflexible XML encoding for ruby objects.}
12
+ gem.summary = %q{Fast but inflexible XML encoding for ruby objects.}
13
13
  gem.homepage = "https://github.com/yolk/pump"
14
+ gem.license = 'MIT'
14
15
 
15
16
  gem.files = `git ls-files`.split($/)
16
17
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
19
  gem.require_paths = ["lib"]
19
20
 
21
+ gem.add_dependency 'activesupport'
20
22
  gem.add_development_dependency 'rspec', '>= 2.12.0'
21
23
  gem.add_development_dependency 'guard-rspec', '>=2.2.2'
22
24
  end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pump::Xml::TagArray do
4
+ describe ".new" do
5
+ it "requires one parameter" do
6
+ lambda{ Pump::Xml::TagArray.new }.should raise_error(ArgumentError)
7
+ lambda{ Pump::Xml::TagArray.new('tag') }.should_not raise_error
8
+ end
9
+ end
10
+ end
@@ -9,17 +9,87 @@ describe Pump::Xml do
9
9
  end
10
10
  end
11
11
 
12
- describe "#serialize" do
13
- let(:person) { Struct.new(:name, :age).new('Benny', 9) }
12
+ describe "#encode" do
13
+ let(:person) { Struct.new(:name, :age, :last_name).new('Benny', 9, 'Hellman') }
14
14
  let(:xml) { Pump::Xml.new('person', [{:name => :name}]) }
15
15
 
16
16
  it "requires one object" do
17
- lambda{ xml.serialize }.should raise_error(ArgumentError)
18
- lambda{ xml.serialize(person) }.should_not raise_error
17
+ lambda{ xml.encode }.should raise_error(ArgumentError)
18
+ lambda{ xml.encode(person) }.should_not raise_error
19
19
  end
20
20
 
21
21
  it "returns xml string" do
22
- xml.serialize(person).should eql("#{XML_INSTRUCT}<person>\n <name>Benny</name>\n</person>")
22
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <name>Benny</name>\n</person>")
23
+ end
24
+
25
+ context "with array" do
26
+
27
+ context "with one entry" do
28
+ let(:people) { [person] }
29
+
30
+ it "returns xml string" do
31
+ xml.encode(people).should eql("#{XML_INSTRUCT}<people type=\"array\">\n <person>\n <name>Benny</name>\n </person>\n</people>")
32
+ end
33
+ end
34
+
35
+ context "with multiple entries" do
36
+ let(:people) { [person, Struct.new(:name, :age).new('Carlo', 5)] }
37
+
38
+ it "returns xml string" do
39
+ xml.encode(people).should eql("#{XML_INSTRUCT}<people type=\"array\">\n <person>\n <name>Benny</name>\n </person>\n <person>\n <name>Carlo</name>\n </person>\n</people>")
40
+ end
41
+ end
42
+
43
+ context "with empty array" do
44
+ let(:people) { [] }
45
+
46
+ it "returns xml string" do
47
+ xml.encode(people).should eql("#{XML_INSTRUCT}<people type=\"array\" />")
48
+ end
49
+ end
50
+
51
+ context "with no instruct" do
52
+ let(:xml) { Pump::Xml.new('person', [{:name => :name}], :instruct => false) }
53
+ let(:people) { [] }
54
+
55
+ it "returns xml string" do
56
+ xml.encode(people).should eql("<people type=\"array\" />")
57
+ end
58
+ end
59
+
60
+ context "with extra_indent" do
61
+ let(:people) { [person] }
62
+ let(:xml) { Pump::Xml.new('person', [{:name => :name}], :instruct => false, :extra_indent => 1) }
63
+
64
+ it "returns xml string" do
65
+ xml.encode(people).should eql(" <people type=\"array\">\n <person>\n <name>Benny</name>\n </person>\n </people>")
66
+ end
67
+ end
68
+
69
+ context "with array_root" do
70
+ let(:people) { [person] }
71
+ let(:xml) { Pump::Xml.new('person', [{:name => :name}], :instruct => false, :array_root => "personas") }
72
+
73
+ it "returns xml string" do
74
+ xml.encode(people).should eql("<personas type=\"array\">\n <person>\n <name>Benny</name>\n </person>\n</personas>")
75
+ end
76
+ end
77
+ end
78
+
79
+ context "with no instruct" do
80
+ let(:xml) { Pump::Xml.new('person', [{:name => :name}], :instruct => false) }
81
+
82
+ it "returns xml string" do
83
+ xml.encode(person).should eql("<person>\n <name>Benny</name>\n</person>")
84
+ end
85
+ end
86
+
87
+ context "with extra_indent" do
88
+ let(:xml) { Pump::Xml.new('person', [{:name => :name}], :instruct => false, :extra_indent => 1) }
89
+
90
+ it "returns xml string" do
91
+ xml.encode(person).should eql(" <person>\n <name>Benny</name>\n </person>")
92
+ end
23
93
  end
24
94
 
25
95
  context "with attribute" do
@@ -31,16 +101,15 @@ describe Pump::Xml do
31
101
  end
32
102
 
33
103
  it do
34
- xml.serialize(person).should eql("#{XML_INSTRUCT}<person>\n <name>Benny</name>\n <age type=\"integer\">9</age>\n</person>")
104
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <name>Benny</name>\n <age type=\"integer\">9</age>\n</person>")
35
105
  end
36
-
37
106
  end
38
107
 
39
108
  context "with blank name" do
40
109
  let(:person) { Struct.new(:name, :age).new('', 9) }
41
110
 
42
111
  it do
43
- xml.serialize(person).should eql("#{XML_INSTRUCT}<person>\n <name/>\n</person>")
112
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <name/>\n</person>")
44
113
  end
45
114
  end
46
115
 
@@ -48,14 +117,56 @@ describe Pump::Xml do
48
117
  let(:person) { Struct.new(:name, :age).new(nil, 9) }
49
118
 
50
119
  it do
51
- xml.serialize(person).should eql("#{XML_INSTRUCT}<person>\n <name/>\n</person>")
120
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <name/>\n</person>")
52
121
  end
53
122
 
54
123
  context "and with :nil_check => true" do
55
124
  let(:xml) { Pump::Xml.new('person', [{:name => :name, :nil_check => true}]) }
56
125
 
57
126
  it do
58
- xml.serialize(person).should eql("#{XML_INSTRUCT}<person>\n <name nil=\"true\"/>\n</person>")
127
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <name nil=\"true\"/>\n</person>")
128
+ end
129
+ end
130
+ end
131
+
132
+ context "with multiple attrubutes" do
133
+ let(:xml) { Pump::Xml.new('person', [{:name => :name}, {:age => :age}]) }
134
+
135
+ it "returns xml string" do
136
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <name>Benny</name>\n <age>9</age>\n</person>")
137
+ end
138
+ end
139
+
140
+ context "with renamed attrubutes" do
141
+ let(:xml) { Pump::Xml.new('person', [{"last-name" => :last_name}]) }
142
+
143
+ it "returns xml string" do
144
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <last-name>Hellman</last-name>\n</person>")
145
+ end
146
+ end
147
+
148
+ context "with date attribute" do
149
+ let(:person) { Struct.new(:at).new(Date.new(2013, 2, 7)) }
150
+ let(:xml) { Pump::Xml.new('person', [{:at => :at, :attributes => {:type => 'date'}}]) }
151
+
152
+ it "returns xml string" do
153
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <at type=\"date\">2013-02-07</at>\n</person>")
154
+ end
155
+ end
156
+
157
+ context "with datetime attribute" do
158
+ let(:person) { Struct.new(:at).new(Time.new(2013, 2, 7, 0, 0, 0)) }
159
+ let(:xml) { Pump::Xml.new('person', [{:at => :at, :typecast => :xmlschema, :attributes => {:type => 'datetime'}}]) }
160
+
161
+ it "returns xml string" do
162
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <at type=\"datetime\">2013-02-07T00:00:00+01:00</at>\n</person>")
163
+ end
164
+
165
+ context "but nil" do
166
+ let(:person) { Struct.new(:at).new(nil) }
167
+
168
+ it "returns xml string" do
169
+ xml.encode(person).should eql("#{XML_INSTRUCT}<person>\n <at type=\"datetime\"/>\n</person>")
59
170
  end
60
171
  end
61
172
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pump
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-29 00:00:00.000000000 Z
12
+ date: 2013-02-08 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
14
30
  - !ruby/object:Gem::Dependency
15
31
  name: rspec
16
32
  requirement: !ruby/object:Gem::Requirement
@@ -43,7 +59,7 @@ dependencies:
43
59
  - - ! '>='
44
60
  - !ruby/object:Gem::Version
45
61
  version: 2.2.2
46
- description: Fast but inflexible XML dumping for ruby objects.
62
+ description: Fast but inflexible XML encoding for ruby objects.
47
63
  email:
48
64
  - sebastian@yo.lk
49
65
  executables: []
@@ -60,19 +76,23 @@ files:
60
76
  - LICENSE.txt
61
77
  - README.md
62
78
  - Rakefile
79
+ - benchmarks/encode.rb
63
80
  - lib/pump.rb
64
81
  - lib/pump/version.rb
65
82
  - lib/pump/xml.rb
66
83
  - lib/pump/xml/node.rb
67
84
  - lib/pump/xml/tag.rb
85
+ - lib/pump/xml/tag_array.rb
68
86
  - lib/pump/xml/value.rb
69
87
  - pump.gemspec
88
+ - spec/pump/xml/tag_array_spec.rb
70
89
  - spec/pump/xml/tag_spec.rb
71
90
  - spec/pump/xml/value_spec.rb
72
91
  - spec/pump/xml_spec.rb
73
92
  - spec/spec_helper.rb
74
93
  homepage: https://github.com/yolk/pump
75
- licenses: []
94
+ licenses:
95
+ - MIT
76
96
  post_install_message:
77
97
  rdoc_options: []
78
98
  require_paths:
@@ -94,8 +114,9 @@ rubyforge_project:
94
114
  rubygems_version: 1.8.23
95
115
  signing_key:
96
116
  specification_version: 3
97
- summary: Fast but inflexible XML dumping for ruby objects.
117
+ summary: Fast but inflexible XML encoding for ruby objects.
98
118
  test_files:
119
+ - spec/pump/xml/tag_array_spec.rb
99
120
  - spec/pump/xml/tag_spec.rb
100
121
  - spec/pump/xml/value_spec.rb
101
122
  - spec/pump/xml_spec.rb