dm-filemaker-adapter 0.0.1
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 +15 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +56 -0
- data/Rakefile +15 -0
- data/dm-filemaker-adapter.gemspec +27 -0
- data/lib/dm-filemaker-adapter/adapter.rb +320 -0
- data/lib/dm-filemaker-adapter/dm-fmresultset.yml +86 -0
- data/lib/dm-filemaker-adapter/version.rb +5 -0
- data/lib/dm-filemaker-adapter.rb +7 -0
- data/spec/dm-filemaker-adapter/adapter_spec.rb +11 -0
- data/spec/spec_helper.rb +2 -0
- metadata +129 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
!binary "U0hBMQ==":
|
|
3
|
+
metadata.gz: !binary |-
|
|
4
|
+
OTEzN2JmZDA1YjA0Yzg0ZjcxMjllNDkxZDQxM2E4NTE0NzhjYzEwZA==
|
|
5
|
+
data.tar.gz: !binary |-
|
|
6
|
+
YmI3MzhmYWEyODk0MjYwNzNlOTI0NGNhYTJhMWFiYjU1MjA4YjM4OA==
|
|
7
|
+
SHA512:
|
|
8
|
+
metadata.gz: !binary |-
|
|
9
|
+
NjBiZTQxMmFiOWEzYmMxYWUwNzkxOTcyM2QzNThlMDgyMGJjMDA1OTNmYWNi
|
|
10
|
+
OGU0MmM2YTI4Yzc0Y2YxMDI2MGVmMDRiZGEwYTUyYzYzMjMwOGFiYzU2ZTAz
|
|
11
|
+
M2JhNzRmODE0ZjkwZGM4ZDBhZGIxNTM4YzIwYzNhNzEwM2VjZDk=
|
|
12
|
+
data.tar.gz: !binary |-
|
|
13
|
+
MTRjNDRhYWM3NzkxMjkzNTkwOGQ1N2M0ZmNiYzdkYWU0NGJhMDMyZTc3Njgx
|
|
14
|
+
MmQzYzNlODE2M2UzMWFlOWQ4Y2M0OTYzOTA4NWI0OGZkNTQ2YTdkZjZlMGE1
|
|
15
|
+
MDllNzgxMDBhNzMxMzc0ZTNkM2U3MTg2NjQzNmQ4Mzk0N2I5ODM=
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2015 wbr
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# dm-filemaker-adapter
|
|
2
|
+
|
|
3
|
+
A Filemaker adapter for DataMapper, allowing DataMapper to use Filemaker Server as a datastore.
|
|
4
|
+
|
|
5
|
+
dm-filemaker-adapter uses the ginjo-rfm gem as the backend command and xml parser. Ginjo-rfm is a full featured filemaker-ruby adapter that exposes most of Filemaker's xml interface functionality in ruby. dm-filemaker-adapter doesn't tap into all of rfm's features, but rather, it provides DataMapper the ability to use Filemaker Server as a backend datastore. All of the basic functionality of DataMapper's CRUD interface is supported, including compound queries and OR queries (using Filemaker's -findquery command), query operators like :field.gt=>..., lazy-loading where possible, first & last record, aggregate queries, ranges, field mapping, and more.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
gem 'dm-filemaker-adapter'
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
$ bundle
|
|
16
|
+
|
|
17
|
+
Or install it yourself as:
|
|
18
|
+
|
|
19
|
+
$ gem install dm-filemaker-adapter
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
DB_CONFIG = {
|
|
24
|
+
adapter: 'filemaker',
|
|
25
|
+
host: 'my.server.com',
|
|
26
|
+
account_name: 'my-user-name',
|
|
27
|
+
password: 'xxxxxxxxxx',
|
|
28
|
+
database: 'db-name'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
DataMapper.setup(:default, DB_CONFIG)
|
|
32
|
+
|
|
33
|
+
class User
|
|
34
|
+
include DataMapper::Resource
|
|
35
|
+
storage_names[:default] = 'user_xml' # This is your filemaker layout for the user table.
|
|
36
|
+
|
|
37
|
+
# Property & field names in this list must be lowercase, regardless of what they are in Filemaker.
|
|
38
|
+
|
|
39
|
+
property :userid, String, :key=>true, :required=>false
|
|
40
|
+
property :email, String
|
|
41
|
+
property :login, String, :field=>'username'
|
|
42
|
+
property :updated, DateTime, :field=>'updated_at'
|
|
43
|
+
property :encrypted_password, BCryptPassword
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
DataMapper.finalize
|
|
47
|
+
|
|
48
|
+
User.get 'usr1035'
|
|
49
|
+
User.first :email => 'wbr'
|
|
50
|
+
User.all :updated.gt => 3.days.ago
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
Bundler.require
|
|
3
|
+
|
|
4
|
+
require "bundler/gem_tasks"
|
|
5
|
+
require "rspec/core/rake_task"
|
|
6
|
+
|
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
8
|
+
|
|
9
|
+
task :default => :spec
|
|
10
|
+
|
|
11
|
+
desc "version"
|
|
12
|
+
task :version do
|
|
13
|
+
require 'dm-filemaker-adapter/version'
|
|
14
|
+
p DataMapper::FilemakerAdapter::VERSION
|
|
15
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'dm-filemaker-adapter/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "dm-filemaker-adapter"
|
|
8
|
+
spec.version = DataMapper::FilemakerAdapter::VERSION
|
|
9
|
+
spec.authors = ["William Richardson"]
|
|
10
|
+
spec.email = ["https://github.com/ginjo/dm-filemaker-adapter"]
|
|
11
|
+
spec.summary = %q{Filemaker adapter for DataMapper}
|
|
12
|
+
spec.description = %q{Use Filemaker Server as a datastore for DataMapper ORM}
|
|
13
|
+
spec.homepage = ""
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_dependency "data_mapper"
|
|
22
|
+
spec.add_dependency "ginjo-rfm"
|
|
23
|
+
|
|
24
|
+
spec.add_development_dependency "bundler"
|
|
25
|
+
spec.add_development_dependency "rake"
|
|
26
|
+
spec.add_development_dependency "rspec"
|
|
27
|
+
end
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# Property & field names in dm-filemaker-adapter models must be declared lowercase, regardless of what they are in FMP.
|
|
2
|
+
|
|
3
|
+
module DataMapper
|
|
4
|
+
[Resource, Model, Adapters]
|
|
5
|
+
|
|
6
|
+
# All this to tack on class and instance methods to the model/resource.
|
|
7
|
+
module Resource
|
|
8
|
+
class << self
|
|
9
|
+
alias_method :included_orig, :included
|
|
10
|
+
def included(klass)
|
|
11
|
+
included_orig(klass)
|
|
12
|
+
if klass.repository.adapter.to_s[/filemaker/i]
|
|
13
|
+
klass.instance_eval do
|
|
14
|
+
extend repository.adapter.class::ModelMethods
|
|
15
|
+
include repository.adapter.class::ResourceMethods
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module Model
|
|
23
|
+
#attr_accessor :last_query
|
|
24
|
+
alias_method :finalize_orig, :finalize
|
|
25
|
+
def finalize(*args)
|
|
26
|
+
property :record_id, Integer, :lazy=>false
|
|
27
|
+
property :mod_id, Integer, :lazy=>false
|
|
28
|
+
finalize_orig
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
module Adapters
|
|
35
|
+
|
|
36
|
+
class FilemakerAdapter < AbstractAdapter
|
|
37
|
+
@fmresultset_template_path = File.expand_path('../dm-fmresultset.yml', __FILE__).to_s
|
|
38
|
+
class << self; attr_accessor :fmresultset_template_path; end
|
|
39
|
+
VERSION = DataMapper::FilemakerAdapter::VERSION
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### UTILITY METHODS ###
|
|
43
|
+
|
|
44
|
+
# Class methods extended onto model.
|
|
45
|
+
module ModelMethods
|
|
46
|
+
def layout
|
|
47
|
+
Rfm.layout(storage_name, repository.adapter.options.symbolize_keys)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Instance methods included in model.
|
|
52
|
+
module ResourceMethods
|
|
53
|
+
def layout
|
|
54
|
+
model.layout
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### ADAPTER CORE METHODS ###
|
|
61
|
+
|
|
62
|
+
# Persists one or many new resources
|
|
63
|
+
#
|
|
64
|
+
# @example
|
|
65
|
+
# adapter.create(collection) # => 1
|
|
66
|
+
#
|
|
67
|
+
# Adapters provide specific implementation of this method
|
|
68
|
+
#
|
|
69
|
+
# @param [Enumerable<Resource>] resources
|
|
70
|
+
# The list of resources (model instances) to create
|
|
71
|
+
#
|
|
72
|
+
# @return [Integer]
|
|
73
|
+
# The number of records that were actually saved into the data-store
|
|
74
|
+
#
|
|
75
|
+
# @api semipublic
|
|
76
|
+
def create(resources)
|
|
77
|
+
#resources[0].model.last_query = resources
|
|
78
|
+
counter = 0
|
|
79
|
+
resources.each do |resource|
|
|
80
|
+
fm_params = fmp_attributes resource.dirty_attributes
|
|
81
|
+
rslt = layout(resource.model).create(fm_params, :template=>self.class.fmresultset_template_path)
|
|
82
|
+
merge_fmp_response(resource, rslt[0])
|
|
83
|
+
counter +=1
|
|
84
|
+
end
|
|
85
|
+
counter
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Reads one or many resources from a datastore
|
|
89
|
+
#
|
|
90
|
+
# @example
|
|
91
|
+
# adapter.read(query) # => [ { 'name' => 'Dan Kubb' } ]
|
|
92
|
+
#
|
|
93
|
+
# Adapters provide specific implementation of this method
|
|
94
|
+
#
|
|
95
|
+
# @param [Query] query
|
|
96
|
+
# the query to match resources in the datastore
|
|
97
|
+
#
|
|
98
|
+
# @return [Enumerable<Hash>]
|
|
99
|
+
# an array of hashes to become resources
|
|
100
|
+
#
|
|
101
|
+
# @api semipublic
|
|
102
|
+
# def read(query)
|
|
103
|
+
# raise NotImplementedError, "#{self.class}#read not implemented"
|
|
104
|
+
# end
|
|
105
|
+
#
|
|
106
|
+
def read(query)
|
|
107
|
+
#query.model.last_query = query
|
|
108
|
+
#y query
|
|
109
|
+
_layout = layout(query.model)
|
|
110
|
+
opts = fmp_options(query)
|
|
111
|
+
opts[:template] = self.class.fmresultset_template_path
|
|
112
|
+
prms = fmp_query(query.conditions) #.to_set.first)
|
|
113
|
+
rslt = prms.empty? ? _layout.all(opts) : _layout.find(prms, opts)
|
|
114
|
+
rslt.dup.each_with_index(){|r, i| rslt[i] = r.to_h}
|
|
115
|
+
rslt
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Takes a query and returns number of matched records.
|
|
119
|
+
# An empty query will return the total record count
|
|
120
|
+
def aggregate(query)
|
|
121
|
+
#query.model.last_query = query
|
|
122
|
+
#y query
|
|
123
|
+
_layout = layout(query.model)
|
|
124
|
+
opts = fmp_options(query)
|
|
125
|
+
opts[:template] = self.class.fmresultset_template_path
|
|
126
|
+
prms = fmp_query(query.conditions) #.to_set.first)
|
|
127
|
+
#[prms.empty? ? _layout.all(:max_records=>0).foundset_count : _layout.count(prms)]
|
|
128
|
+
[prms.empty? ? _layout.view.total_count : _layout.count(prms)]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Updates one or many existing resources
|
|
132
|
+
#
|
|
133
|
+
# @example
|
|
134
|
+
# adapter.update(attributes, collection) # => 1
|
|
135
|
+
#
|
|
136
|
+
# Adapters provide specific implementation of this method
|
|
137
|
+
#
|
|
138
|
+
# @param [Hash(Property => Object)] attributes
|
|
139
|
+
# hash of attribute values to set, keyed by Property
|
|
140
|
+
# @param [Collection] collection
|
|
141
|
+
# collection of records to be updated
|
|
142
|
+
#
|
|
143
|
+
# @return [Integer]
|
|
144
|
+
# the number of records updated
|
|
145
|
+
#
|
|
146
|
+
# @api semipublic
|
|
147
|
+
def update(attributes, collection)
|
|
148
|
+
#collection[0].model.last_query = [attributes, collection]
|
|
149
|
+
fm_params = fmp_attributes(attributes)
|
|
150
|
+
counter = 0
|
|
151
|
+
collection.each do |resource|
|
|
152
|
+
rslt = layout(resource.model).edit(resource.record_id, fm_params, :template=>self.class.fmresultset_template_path)
|
|
153
|
+
merge_fmp_response(resource, rslt[0])
|
|
154
|
+
resource.persistence_state = DataMapper::Resource::PersistenceState::Clean.new resource
|
|
155
|
+
counter +=1
|
|
156
|
+
end
|
|
157
|
+
counter
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Deletes one or many existing resources
|
|
161
|
+
#
|
|
162
|
+
# @example
|
|
163
|
+
# adapter.delete(collection) # => 1
|
|
164
|
+
#
|
|
165
|
+
# Adapters provide specific implementation of this method
|
|
166
|
+
#
|
|
167
|
+
# @param [Collection] collection
|
|
168
|
+
# collection of records to be deleted
|
|
169
|
+
#
|
|
170
|
+
# @return [Integer]
|
|
171
|
+
# the number of records deleted
|
|
172
|
+
#
|
|
173
|
+
# @api semipublic
|
|
174
|
+
def delete(collection)
|
|
175
|
+
counter = 0
|
|
176
|
+
collection.each do |resource|
|
|
177
|
+
rslt = layout(resource.model).delete(resource.record_id, :template=>self.class.fmresultset_template_path)
|
|
178
|
+
counter +=1
|
|
179
|
+
end
|
|
180
|
+
counter
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
### ADAPTER HELPER METHODS ###
|
|
186
|
+
|
|
187
|
+
# Create fmp layout object from model object.
|
|
188
|
+
def layout(model)
|
|
189
|
+
#Rfm.layout(model.storage_name, options.symbolize_keys) #query.repository.adapter.options.symbolize_keys)
|
|
190
|
+
model.layout
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Convert dm query object to fmp query params (hash)
|
|
194
|
+
def fmp_query(input)
|
|
195
|
+
#puts "CONDITIONS input #{input.class.name} (#{input})"
|
|
196
|
+
if input.class.name[/OrOperation/]
|
|
197
|
+
input.operands.collect {|o| fmp_query o}
|
|
198
|
+
elsif input.class.name[/AndOperation/]
|
|
199
|
+
h = Hash.new
|
|
200
|
+
input.operands.each do |k,v|
|
|
201
|
+
r = fmp_query(k)
|
|
202
|
+
#puts "CONDITIONS operand #{r}"
|
|
203
|
+
if r.is_a?(Hash)
|
|
204
|
+
h.merge!(r)
|
|
205
|
+
else
|
|
206
|
+
h=r
|
|
207
|
+
break
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
h
|
|
211
|
+
elsif input.class.name[/NullOperation/] || input.nil?
|
|
212
|
+
{}
|
|
213
|
+
else
|
|
214
|
+
#puts "FMP_QUERY OPERATION #{input.class}"
|
|
215
|
+
val = input.loaded_value
|
|
216
|
+
|
|
217
|
+
if val.to_s != ''
|
|
218
|
+
|
|
219
|
+
operation = input.class.name
|
|
220
|
+
operator = case
|
|
221
|
+
when operation[/EqualTo/]; '='
|
|
222
|
+
when operation[/GreaterThan/]; '>'
|
|
223
|
+
when operation[/LessThan/]; '<'
|
|
224
|
+
when operation[/Like/]; ''
|
|
225
|
+
when operation[/Null/]; ''
|
|
226
|
+
else ''
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
val = val._to_fm if val.respond_to? :_to_fm
|
|
230
|
+
{input.subject.field.to_s => "#{operator}#{val}"}
|
|
231
|
+
else
|
|
232
|
+
{}
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# Convert dm attributes hash to regular hash
|
|
239
|
+
# TODO: Should the result be string or symbol keys?
|
|
240
|
+
def fmp_attributes(attributes)
|
|
241
|
+
#puts "ATTRIBUTES"
|
|
242
|
+
y attributes
|
|
243
|
+
fm_params = Hash.new
|
|
244
|
+
attributes.to_h.each do |k,v|
|
|
245
|
+
fm_params[k.field] = v.respond_to?(:_to_fm) ? v._to_fm : v
|
|
246
|
+
end
|
|
247
|
+
# fm_params = Hash.new
|
|
248
|
+
# resource.dirty_attributes.each do |a,v|
|
|
249
|
+
# fm_params[a.field] = v.respond_to?(:_to_fm) ? v._to_fm : v
|
|
250
|
+
# end
|
|
251
|
+
fm_params
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Get fmp options hash from query
|
|
255
|
+
def fmp_options(query)
|
|
256
|
+
fm_options = {}
|
|
257
|
+
fm_options[:skip_records] = query.offset if query.offset
|
|
258
|
+
fm_options[:max_records] = query.limit if query.limit
|
|
259
|
+
if query.order
|
|
260
|
+
fm_options[:sort_field] = query.order.collect do |ord|
|
|
261
|
+
ord.target.field
|
|
262
|
+
end
|
|
263
|
+
fm_options[:sort_order] = query.order.collect do |ord|
|
|
264
|
+
ord.operator.to_s + 'end'
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
fm_options
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def merge_fmp_response(resource, record)
|
|
271
|
+
resource.model.properties.to_a.each do |property|
|
|
272
|
+
if record.key?(property.field.to_s)
|
|
273
|
+
resource[property.name] = record[property.field.to_s]
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# # This is supposed to convert property objects to field name. Not sure if it works.
|
|
279
|
+
# def get_field_name(field)
|
|
280
|
+
# return field.field if field.respond_to? :field
|
|
281
|
+
# field
|
|
282
|
+
# end
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
protected :fmp_query, :fmp_attributes, :fmp_options, :merge_fmp_response
|
|
286
|
+
|
|
287
|
+
end # FilemakerAdapter
|
|
288
|
+
end # Adapters
|
|
289
|
+
end # DataMapper
|
|
290
|
+
|
|
291
|
+
class Time
|
|
292
|
+
def _to_fm
|
|
293
|
+
d = strftime('%m/%d/%Y') unless Date.today == Date.parse(self.to_s)
|
|
294
|
+
t = strftime('%T')
|
|
295
|
+
d ? "#{d} #{t}" : t
|
|
296
|
+
end
|
|
297
|
+
end # Time
|
|
298
|
+
|
|
299
|
+
class DateTime
|
|
300
|
+
def _to_fm
|
|
301
|
+
d = strftime('%m/%d/%Y')
|
|
302
|
+
t =strftime('%T')
|
|
303
|
+
"#{d} #{t}"
|
|
304
|
+
end
|
|
305
|
+
end # Time
|
|
306
|
+
|
|
307
|
+
class Timestamp
|
|
308
|
+
def _to_fm
|
|
309
|
+
d = strftime('%m/%d/%Y')
|
|
310
|
+
t =strftime('%T')
|
|
311
|
+
"#{d} #{t}"
|
|
312
|
+
end
|
|
313
|
+
end # Time
|
|
314
|
+
|
|
315
|
+
class Date
|
|
316
|
+
def _to_fm
|
|
317
|
+
strftime('%m/%d/%Y')
|
|
318
|
+
end
|
|
319
|
+
end # Time
|
|
320
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# YAML structure defining a SAX parsing scheme for fmresultset xml.
|
|
3
|
+
# The initial object of this parse should be a new instance of Rfm::Resultset.
|
|
4
|
+
---
|
|
5
|
+
attach_elements: _meta
|
|
6
|
+
attach_attributes: _meta
|
|
7
|
+
create_accessors: all
|
|
8
|
+
elements:
|
|
9
|
+
- name: doctype
|
|
10
|
+
attach: none
|
|
11
|
+
attributes:
|
|
12
|
+
- name: value
|
|
13
|
+
as_name: doctype
|
|
14
|
+
- name: fmresultset
|
|
15
|
+
attach: none
|
|
16
|
+
- name: product
|
|
17
|
+
- name: error
|
|
18
|
+
attach: none
|
|
19
|
+
before_close: :check_for_errors
|
|
20
|
+
attributes:
|
|
21
|
+
- name: code
|
|
22
|
+
as_name: error
|
|
23
|
+
- name: datasource
|
|
24
|
+
attach: none
|
|
25
|
+
before_close: [object, end_datasource_element_callback, self]
|
|
26
|
+
attributes:
|
|
27
|
+
- name: total_count
|
|
28
|
+
accessor: none
|
|
29
|
+
- name: metadata
|
|
30
|
+
attach: none
|
|
31
|
+
- name: field_definition
|
|
32
|
+
# These two steps can be used to create the attachment to resultset-meta automatically,
|
|
33
|
+
# but the field-mapping translation won't happen.
|
|
34
|
+
# attach: [_meta, 'Rfm::Metadata::Field', allocate]
|
|
35
|
+
# as_name: field_meta
|
|
36
|
+
attach: [cursor, 'Rfm::Metadata::Field', ':allocate']
|
|
37
|
+
delimiter: name
|
|
38
|
+
attach_attributes: private
|
|
39
|
+
before_close: [object, field_definition_element_close_callback, self]
|
|
40
|
+
- name: relatedset_definition
|
|
41
|
+
delimiter: table
|
|
42
|
+
as_name: portal_meta
|
|
43
|
+
attach_attributes: private
|
|
44
|
+
elements:
|
|
45
|
+
- name: field_definition
|
|
46
|
+
attach: [cursor, 'Rfm::Metadata::Field', ':allocate']
|
|
47
|
+
delimiter: name
|
|
48
|
+
as_name: field_meta
|
|
49
|
+
attach_attributes: private
|
|
50
|
+
before_close: [object, relatedset_field_definition_element_close_callback, self]
|
|
51
|
+
- name: resultset
|
|
52
|
+
attach: none
|
|
53
|
+
attributes:
|
|
54
|
+
- name: count
|
|
55
|
+
accessor: none
|
|
56
|
+
- name: fetch_size
|
|
57
|
+
accessor: none
|
|
58
|
+
- name: record
|
|
59
|
+
#attach: [cursor, object, handle_new_record, _attributes]
|
|
60
|
+
#attach_attributes: none
|
|
61
|
+
attach: [array, 'Rfm::Record', new, object]
|
|
62
|
+
attach_attributes: hash
|
|
63
|
+
before_close: '@loaded=true'
|
|
64
|
+
elements:
|
|
65
|
+
- name: field
|
|
66
|
+
attach: [cursor, 'Rfm::Metadata::Datum', ':allocate']
|
|
67
|
+
compact: false
|
|
68
|
+
before_close: [object, field_element_close_callback, self]
|
|
69
|
+
- name: relatedset
|
|
70
|
+
attach: [private, Array, ':allocate']
|
|
71
|
+
as_name: portals
|
|
72
|
+
attach_attributes: private
|
|
73
|
+
create_accessors: all
|
|
74
|
+
delimiter: table
|
|
75
|
+
elements:
|
|
76
|
+
- name: record
|
|
77
|
+
#class: Rfm::Record
|
|
78
|
+
attach: [default, 'Rfm::Record', ':allocate']
|
|
79
|
+
attach_attributes: hash
|
|
80
|
+
before_close: '@loaded=true'
|
|
81
|
+
elements:
|
|
82
|
+
- name: field
|
|
83
|
+
compact: true
|
|
84
|
+
attach: [cursor, 'Rfm::Metadata::Datum', ':allocate']
|
|
85
|
+
before_close: [object, portal_field_element_close_callback, self]
|
|
86
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe DataMapper::Adapters::FilemakerAdapter do
|
|
4
|
+
it 'has a version number' do
|
|
5
|
+
expect(DataMapper::Adapters::FilemakerAdapter::VERSION).not_to be nil
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'does something useful' do
|
|
9
|
+
expect(true).to eq(true)
|
|
10
|
+
end
|
|
11
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dm-filemaker-adapter
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- William Richardson
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-02-16 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: data_mapper
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ! '>='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ! '>='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: ginjo-rfm
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ! '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ! '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ! '>='
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ! '>='
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ! '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ! '>='
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ! '>='
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ! '>='
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
description: Use Filemaker Server as a datastore for DataMapper ORM
|
|
84
|
+
email:
|
|
85
|
+
- https://github.com/ginjo/dm-filemaker-adapter
|
|
86
|
+
executables: []
|
|
87
|
+
extensions: []
|
|
88
|
+
extra_rdoc_files: []
|
|
89
|
+
files:
|
|
90
|
+
- .gitignore
|
|
91
|
+
- .rspec
|
|
92
|
+
- Gemfile
|
|
93
|
+
- LICENSE.txt
|
|
94
|
+
- README.md
|
|
95
|
+
- Rakefile
|
|
96
|
+
- dm-filemaker-adapter.gemspec
|
|
97
|
+
- lib/dm-filemaker-adapter.rb
|
|
98
|
+
- lib/dm-filemaker-adapter/adapter.rb
|
|
99
|
+
- lib/dm-filemaker-adapter/dm-fmresultset.yml
|
|
100
|
+
- lib/dm-filemaker-adapter/version.rb
|
|
101
|
+
- spec/dm-filemaker-adapter/adapter_spec.rb
|
|
102
|
+
- spec/spec_helper.rb
|
|
103
|
+
homepage: ''
|
|
104
|
+
licenses:
|
|
105
|
+
- MIT
|
|
106
|
+
metadata: {}
|
|
107
|
+
post_install_message:
|
|
108
|
+
rdoc_options: []
|
|
109
|
+
require_paths:
|
|
110
|
+
- lib
|
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - ! '>='
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '0'
|
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
|
+
requirements:
|
|
118
|
+
- - ! '>='
|
|
119
|
+
- !ruby/object:Gem::Version
|
|
120
|
+
version: '0'
|
|
121
|
+
requirements: []
|
|
122
|
+
rubyforge_project:
|
|
123
|
+
rubygems_version: 2.4.5
|
|
124
|
+
signing_key:
|
|
125
|
+
specification_version: 4
|
|
126
|
+
summary: Filemaker adapter for DataMapper
|
|
127
|
+
test_files:
|
|
128
|
+
- spec/dm-filemaker-adapter/adapter_spec.rb
|
|
129
|
+
- spec/spec_helper.rb
|