pump 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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