rfilemaker 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.rdoc +46 -44
- data/VERSION +1 -1
- data/lib/rfilemaker.rb +13 -0
- data/lib/rfilemaker/field.rb +12 -2
- data/lib/rfilemaker/record.rb +12 -6
- data/lib/rfilemaker/result_set.rb +4 -1
- data/lib/rfilemaker/row.rb +7 -10
- data/rfilemaker.gemspec +4 -2
- data/spec/full_parse_test_spec.rb +2 -2
- data/spec/rfilemaker/record_spec.rb +12 -0
- data/spec/rfilemaker_spec.rb +15 -0
- metadata +5 -3
data/README.rdoc
CHANGED
@@ -7,62 +7,64 @@ This library parses a Filemaker Pro FMPXMLRESULT type document, nothing more, no
|
|
7
7
|
== Installation
|
8
8
|
|
9
9
|
Easy, just use:
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
|
11
|
+
gem install rfilemaker
|
12
|
+
|
13
13
|
== Usage
|
14
14
|
|
15
15
|
To parse a Filemaker Pro export file named 'export.xml', use:
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
RFilemaker.parse('export.xml')
|
18
|
+
|
19
19
|
This returns an array of hashes, each of which represent a row in your Filemaker database.
|
20
20
|
Fields are automatically converted to their ruby types.
|
21
21
|
|
22
22
|
For instance, parsing the following XML export:
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
24
|
+
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
|
25
|
+
<ERRORCODE>0</ERRORCODE>
|
26
|
+
<PRODUCT BUILD="5/23/2002" NAME="FileMaker Pro"
|
27
|
+
VERSION="7.0"/>
|
28
|
+
<DATABASE DATEFORMAT="MM/dd/yy" LAYOUT="summary"
|
29
|
+
NAME="Employees.fp7" RECORDS="23" TIMEFORMAT="hh:mm:ss"/>
|
30
|
+
<METADATA>
|
31
|
+
<FIELD EMPTYOK="NO" MAXREPEAT="1" NAME="First Name" TYPE="TEXT"/>
|
32
|
+
<FIELD EMPTYOK="NO" MAXREPEAT="1" NAME="Last Name" TYPE="TEXT"/>
|
33
|
+
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="Department" TYPE="TEXT"/>
|
34
|
+
</METADATA>
|
35
|
+
<RESULTSET FOUND="2">
|
36
|
+
<ROW MODID="47" RECORDID="34">
|
37
|
+
<COL>
|
38
|
+
<DATA>Joe</DATA>
|
39
|
+
</COL>
|
40
|
+
<COL>
|
41
|
+
<DATA>Smith</DATA>
|
42
|
+
</COL>
|
43
|
+
<COL>
|
44
|
+
<DATA>Engineering</DATA>
|
45
|
+
</COL>
|
46
|
+
</ROW>
|
47
|
+
<ROW MODID="89" RECORDID="78">
|
48
|
+
<COL>
|
49
|
+
<DATA>Susan</DATA>
|
50
|
+
</COL>
|
51
|
+
<COL>
|
52
|
+
<DATA>Jones</DATA>
|
53
|
+
</COL>
|
54
|
+
<COL>
|
55
|
+
<DATA>Marketing</DATA>
|
56
|
+
</COL>
|
57
|
+
</ROW>
|
58
|
+
</RESULTSET>
|
59
|
+
</FMPXMLRESULT>
|
60
60
|
|
61
61
|
gives this Ruby hash:
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
[{"last name"=>"Smith", "department"=>"Engineering", "first name"=>"Joe"},
|
64
|
+
{"last name"=>"Jones", "department"=>"Marketing", "first name"=>"Susan"}]
|
65
|
+
|
66
|
+
The resulting hash is case-insensitive, so keys can be looked up in any given case.
|
67
|
+
|
66
68
|
== Note on Patches/Pull Requests
|
67
69
|
|
68
70
|
* Fork the project.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/lib/rfilemaker.rb
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
require 'nokogiri'
|
2
2
|
|
3
3
|
module RFilemaker
|
4
|
+
class SpecialHash < Hash # :nodoc: all
|
5
|
+
def []=(key, value)
|
6
|
+
super(key.downcase, value)
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](key)
|
10
|
+
super(key.to_s.downcase)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Parse a FMPXMLRESULT string into an Array
|
15
|
+
#
|
16
|
+
# Each element in the Array is a Hash, representing a row in the imported XML
|
4
17
|
def self.parse(string)
|
5
18
|
doc = Nokogiri::XML.parse(string)
|
6
19
|
ResultSet.new(doc)
|
data/lib/rfilemaker/field.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
module RFilemaker
|
2
2
|
class Field
|
3
|
-
|
3
|
+
# Name of the field
|
4
|
+
attr_reader :name
|
5
|
+
# Type of the field as symbol
|
6
|
+
attr_reader :type
|
4
7
|
|
5
8
|
def initialize(xml, result_set)
|
6
9
|
@name = xml['NAME']
|
@@ -8,7 +11,14 @@ module RFilemaker
|
|
8
11
|
@empty_ok = xml['EMPTYOK'] == 'YES'
|
9
12
|
@result_set = result_set
|
10
13
|
end
|
11
|
-
|
14
|
+
|
15
|
+
# Coerce a value to the Ruby equivalent of the Filemaker Type
|
16
|
+
#
|
17
|
+
# * 'DATE' gets converted to Date
|
18
|
+
# * 'TIME' gets converted to DateTime
|
19
|
+
# * 'TIMESTAMP' gets converted to DateTime
|
20
|
+
# * 'NUMBER' gets converted to float or integer
|
21
|
+
# * everything else gets converted to a string
|
12
22
|
def coerce(value)
|
13
23
|
return nil if value.nil? || value == ''
|
14
24
|
|
data/lib/rfilemaker/record.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
module RFilemaker
|
2
|
-
|
3
|
-
|
2
|
+
# Represents a Row in the Filemaker database, as a plain Hash
|
3
|
+
class Record < SpecialHash
|
4
|
+
# Record id from Filemaker
|
5
|
+
attr_reader :record_id
|
6
|
+
|
7
|
+
# Modification id from Filemaker
|
8
|
+
attr_reader :mod_id
|
4
9
|
|
5
10
|
def initialize(row, fields)
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@mod_id = @row.mod_id
|
11
|
+
@record_id = row[:record_id]
|
12
|
+
@mod_id = row[:mod_id]
|
9
13
|
|
10
14
|
fields.each_with_index do |field, index|
|
11
|
-
data = row
|
15
|
+
data = row[:columns][index]
|
12
16
|
|
13
17
|
if data.is_a?(Array)
|
14
18
|
self[field.name] = []
|
@@ -17,6 +21,8 @@ module RFilemaker
|
|
17
21
|
self[field.name] = field.coerce(data)
|
18
22
|
end
|
19
23
|
end
|
24
|
+
|
25
|
+
freeze
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module RFilemaker
|
2
2
|
class ResultSet < Array
|
3
|
+
# Parse a Filemaker date format to something strptime understands
|
3
4
|
def self.parse_date_format(string)
|
4
5
|
string = string.gsub(/yyyy|yy/, '%y').gsub(/mm|m|MM|M/, '%m').gsub(/dd|d|DD|D/, '%d')
|
5
6
|
string.gsub(/hh|h/, '%I').gsub(/kk|k/, '%H').gsub(/mm/, '%M').gsub(/ss/, '%S').gsub(/a/, '%p')
|
@@ -8,6 +9,8 @@ module RFilemaker
|
|
8
9
|
attr_reader :fields, :rows
|
9
10
|
attr_reader :date_format, :time_format
|
10
11
|
|
12
|
+
# Generates a new ResultSet (or plain Ruby Array) for the given XML document
|
13
|
+
# Items in the ResultSet are Hashes, representing rows in the Filemaker export
|
11
14
|
def initialize(doc)
|
12
15
|
@fields = extract_fields(doc)
|
13
16
|
@rows = extract_rows(doc)
|
@@ -21,7 +24,7 @@ module RFilemaker
|
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
24
|
-
private
|
27
|
+
private # :nodoc: all
|
25
28
|
def extract_fields(doc)
|
26
29
|
doc.css('METADATA FIELD').collect do |xml|
|
27
30
|
Field.new(xml, self)
|
data/lib/rfilemaker/row.rb
CHANGED
@@ -1,18 +1,15 @@
|
|
1
1
|
module RFilemaker
|
2
|
-
class Row
|
3
|
-
attr_reader :record_id, :mod_id, :columns
|
4
|
-
|
2
|
+
class Row < Hash # :nodoc: all
|
5
3
|
def initialize(xml)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
xml.css('COL').each do |col|
|
4
|
+
self[:record_id] = xml['RECORDID'].to_i
|
5
|
+
self[:mod_id] = xml['MODID'].to_i
|
6
|
+
|
7
|
+
self[:columns] = xml.css('COL').collect do |col|
|
11
8
|
datas = col.css('DATA')
|
12
9
|
if datas.size > 1
|
13
|
-
|
10
|
+
datas.collect { |x| x.inner_text }
|
14
11
|
else
|
15
|
-
|
12
|
+
datas.inner_text
|
16
13
|
end
|
17
14
|
end
|
18
15
|
end
|
data/rfilemaker.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rfilemaker}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Bart Zonneveld"]
|
12
|
-
s.date = %q{2010-06-
|
12
|
+
s.date = %q{2010-06-02}
|
13
13
|
s.description = %q{Ruby library to parse Filemaker Pro FMPXMLRESULT files}
|
14
14
|
s.email = %q{loop@superinfinite.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
"rfilemaker.gemspec",
|
32
32
|
"spec/full_parse_test_spec.rb",
|
33
33
|
"spec/rfilemaker/field_spec.rb",
|
34
|
+
"spec/rfilemaker/record_spec.rb",
|
34
35
|
"spec/rfilemaker/result_set_spec.rb",
|
35
36
|
"spec/rfilemaker_spec.rb",
|
36
37
|
"spec/spec.opts",
|
@@ -44,6 +45,7 @@ Gem::Specification.new do |s|
|
|
44
45
|
s.test_files = [
|
45
46
|
"spec/full_parse_test_spec.rb",
|
46
47
|
"spec/rfilemaker/field_spec.rb",
|
48
|
+
"spec/rfilemaker/record_spec.rb",
|
47
49
|
"spec/rfilemaker/result_set_spec.rb",
|
48
50
|
"spec/rfilemaker_spec.rb",
|
49
51
|
"spec/spec_helper.rb"
|
@@ -43,14 +43,14 @@ describe RFilemaker do
|
|
43
43
|
describe "records" do
|
44
44
|
it "should parse the first record correctly" do
|
45
45
|
r = @result[0]
|
46
|
-
r.should == { '
|
46
|
+
r.should == { 'name' => 'Joe Smith', 'date joined' => [Date.new(2010,3,2), Date.new(2010,5,2)] }
|
47
47
|
r.mod_id.should == 47
|
48
48
|
r.record_id.should == 34
|
49
49
|
end
|
50
50
|
|
51
51
|
it "should parse the second record correctly" do
|
52
52
|
r = @result[1]
|
53
|
-
r.should == { '
|
53
|
+
r.should == { 'name' => 'Susan Jones', 'date joined' => [Date.new(2009,4,5), Date.new(2009,10,5)] }
|
54
54
|
r.mod_id.should == 89
|
55
55
|
r.record_id.should == 78
|
56
56
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe RFilemaker::Record do
|
4
|
+
before(:each) do
|
5
|
+
@row = { :record_id => 1, :mod_id => 2 }
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should be frozen" do
|
9
|
+
r = RFilemaker::Record.new(@row, [])
|
10
|
+
r.should be_frozen
|
11
|
+
end
|
12
|
+
end
|
data/spec/rfilemaker_spec.rb
CHANGED
@@ -24,4 +24,19 @@ describe RFilemaker do
|
|
24
24
|
it "should return the resultset" do
|
25
25
|
parse.should == 'result set'
|
26
26
|
end
|
27
|
+
|
28
|
+
describe "'special' hash" do
|
29
|
+
before(:each) do
|
30
|
+
@h = RFilemaker::SpecialHash.new
|
31
|
+
@h['Foo BAR'] = 'baz'
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should lookup keys as string" do
|
35
|
+
@h['Foo BAR'].should == 'baz'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should lookup keys as lowercase strings" do
|
39
|
+
@h['foo bar'].should == 'baz'
|
40
|
+
end
|
41
|
+
end
|
27
42
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Bart Zonneveld
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-06-
|
17
|
+
date: 2010-06-02 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- rfilemaker.gemspec
|
70
70
|
- spec/full_parse_test_spec.rb
|
71
71
|
- spec/rfilemaker/field_spec.rb
|
72
|
+
- spec/rfilemaker/record_spec.rb
|
72
73
|
- spec/rfilemaker/result_set_spec.rb
|
73
74
|
- spec/rfilemaker_spec.rb
|
74
75
|
- spec/spec.opts
|
@@ -106,6 +107,7 @@ summary: Ruby library to parse Filemaker Pro FMPXMLRESULT files
|
|
106
107
|
test_files:
|
107
108
|
- spec/full_parse_test_spec.rb
|
108
109
|
- spec/rfilemaker/field_spec.rb
|
110
|
+
- spec/rfilemaker/record_spec.rb
|
109
111
|
- spec/rfilemaker/result_set_spec.rb
|
110
112
|
- spec/rfilemaker_spec.rb
|
111
113
|
- spec/spec_helper.rb
|