pg-xml 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/pg-xml/activerecord.rb +85 -0
- data/lib/pg-xml/coder.rb +28 -0
- data/lib/pg-xml/pg_xml.rb +38 -0
- data/lib/pg-xml.rb +7 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c7fafbeb6bb53498ffc42dd1efe5d683c451a28c
|
4
|
+
data.tar.gz: cf61d5ecfe41b83d6835cd251152b2551403747a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: de4899da7e8003a8b68fdad57419f9d8a2049ba5c579bc270aa0447570db23c4741d2618637970959ef7aa6c93f26e5afefae83da8d641bed56aec706b01da60
|
7
|
+
data.tar.gz: c44359eba77634a371d44f581b652d6a6f0b5772b511327a54251e1bc5887c9e87a25b13a3509a9ae1f3f455286e266b3af1eec1f911f8c245afaf0406140f7f
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# use, e.g.,
|
2
|
+
# has_xml_column :properties
|
3
|
+
# in your ActiveRecord model
|
4
|
+
|
5
|
+
# in a migration, use e.g.
|
6
|
+
# add_xpath_index :users, :properties, '/xml/first_name'
|
7
|
+
# to index into the XML
|
8
|
+
|
9
|
+
module ActiveRecord
|
10
|
+
|
11
|
+
class Base
|
12
|
+
|
13
|
+
# define the has_xml_column :col_name name
|
14
|
+
def self.has_xml_column attr_name
|
15
|
+
|
16
|
+
# serialize using the custom serializer
|
17
|
+
serialize attr_name, ActiveRecord::Coders::XML
|
18
|
+
# validate XML before saving
|
19
|
+
validate "validate_#{attr_name}_xml"
|
20
|
+
|
21
|
+
# define the attribute writer to accept a string
|
22
|
+
# and convert it to XML
|
23
|
+
define_method("#{attr_name}=") do |val|
|
24
|
+
if val.is_a? String
|
25
|
+
val = PgXML.load(val)
|
26
|
+
end
|
27
|
+
self[attr_name] = val
|
28
|
+
end
|
29
|
+
|
30
|
+
# validate XML before writing to database
|
31
|
+
define_method("validate_#{attr_name}_xml") do
|
32
|
+
# TODO make this work with hpricot
|
33
|
+
return true if self[attr_name].nil? || self[attr_name].errors == []
|
34
|
+
self[attr_name].errors.each do |e|
|
35
|
+
errors.add(attr_name, e.to_s)
|
36
|
+
end
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# support the add_xpath_index and make it reversible
|
44
|
+
module ConnectionAdapters
|
45
|
+
module SchemaStatements
|
46
|
+
|
47
|
+
# enable adding an xpath index
|
48
|
+
def add_xpath_index table_name, column_name, path, index_name=nil
|
49
|
+
# generate an index name
|
50
|
+
index_name ||= "index_#{table_name}_on_#{column_name}__#{path}"
|
51
|
+
index_name = index_name.downcase.gsub(/[^a-z0-9]/, '_')
|
52
|
+
|
53
|
+
# create the index
|
54
|
+
execute "CREATE INDEX #{index_name} ON #{table_name}(xpath('#{path}',#{column_name}))"
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# can call in any of the following ways
|
59
|
+
# remove_xpath_index :users, :properties, '/xml/name'
|
60
|
+
# remove_xpath_index :index_users_on_properties__xml_name
|
61
|
+
# remove_xpath_index :users, :properties, '/xml/name', :index_users_on_properties__xml_name
|
62
|
+
def remove_xpath_index index_name_or_table_name, column_name=nil, path=nil, index_name=nil
|
63
|
+
# figure out which version was called
|
64
|
+
index_name = index_name_or_table_name if !column_name
|
65
|
+
|
66
|
+
# transform index name
|
67
|
+
index_name ||= "index_#{table_name}_on_#{column_name}__#{path}"
|
68
|
+
index_name = index_name.downcase.gsub(/[^a-z0-9]/, '_')
|
69
|
+
|
70
|
+
# drop the index
|
71
|
+
execute "DROP INDEX #{index_name}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# TODO this doesn't get called -- why?
|
75
|
+
# adding the xpath index should be reversible
|
76
|
+
def invert_add_xpath_index(args)
|
77
|
+
# table_name, column_name, path, index_name = *args
|
78
|
+
# [:remove_index, [table_name, column_name, path, index_name]]
|
79
|
+
[:remove_xpath_index, args]
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/lib/pg-xml/coder.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Coders
|
3
|
+
class XML
|
4
|
+
|
5
|
+
def self.load(xml_string)
|
6
|
+
new(PgXML.default).load(xml_string)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.dump(xml_document)
|
10
|
+
new(PgXML.default).dump(xml_document)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(default=nil)
|
14
|
+
@default=default
|
15
|
+
end
|
16
|
+
|
17
|
+
def dump(xml_document)
|
18
|
+
(xml_document.nil? && @default.nil?) ? nil : PgXML.dump( xml_document || @default )
|
19
|
+
end
|
20
|
+
|
21
|
+
def load(xml_string)
|
22
|
+
xml_string.nil? ? @default : PgXML.load(xml_string)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# note: none of this is tested against Hpricot
|
2
|
+
# the goal was to allow either hpricot or nokogiri, but
|
3
|
+
# support for "using either" is half-assed at best
|
4
|
+
|
5
|
+
module PgXML
|
6
|
+
|
7
|
+
# complex line of code determining whether we have access to nokogiri or hpricot.
|
8
|
+
XML_PARSER_TYPE = ( require('nokogiri') || true rescue # evaluates to true if we have nokogiri
|
9
|
+
require('hpricot') && false rescue # evaluates to valse if we have hpricot
|
10
|
+
raise LoadError, "Either nokogiri or hpricot is required" # raises an error if we have neither
|
11
|
+
) ? :nokogiri : :hpricot
|
12
|
+
|
13
|
+
# get which XML parser we are using
|
14
|
+
def self.parser_type
|
15
|
+
XML_PARSER_TYPE
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.load xml_str
|
19
|
+
parse xml_str
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.dump xml_document
|
23
|
+
xml_document.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
# Valid xml requires a root node.
|
27
|
+
def self.default
|
28
|
+
parse '<xml></xml>'
|
29
|
+
end
|
30
|
+
|
31
|
+
# use our parser to create an XML document
|
32
|
+
# warning: nokogiri is friendly -- if you feed it invalid XML it will fail silently
|
33
|
+
# Nokogiri::XML.parse('<xml><xml>').to_s ~> <xml><xml/></xml>
|
34
|
+
def self.parse xml_str
|
35
|
+
XML_PARSER_TYPE == :nokogiri ? Nokogiri::XML.parse(xml_str) : Hpricot(xml_str)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/pg-xml.rb
ADDED
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pg-xml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kai Stinchcombe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pg
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: postgresql xml datatype support for activerecord -- heavily based on
|
28
|
+
pg-hstore and activerecord-postgres-hstore
|
29
|
+
email:
|
30
|
+
- k@i.stinchcom.be
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/pg-xml.rb
|
36
|
+
- lib/pg-xml/activerecord.rb
|
37
|
+
- lib/pg-xml/coder.rb
|
38
|
+
- lib/pg-xml/pg_xml.rb
|
39
|
+
homepage:
|
40
|
+
licenses: []
|
41
|
+
metadata: {}
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
- lib/pg-xml
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.0.3
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: postgresql xml datatype support for activerecord -- heavily based on pg-hstore
|
63
|
+
and activerecord-postgres-hstore
|
64
|
+
test_files: []
|