rom-fmp 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +60 -0
- data/Rakefile +12 -0
- data/lib/rom-fmp.rb +1 -0
- data/lib/rom/fmp.rb +21 -0
- data/lib/rom/fmp/commands.rb +19 -0
- data/lib/rom/fmp/commands/create.rb +29 -0
- data/lib/rom/fmp/commands/delete.rb +18 -0
- data/lib/rom/fmp/commands/update.rb +54 -0
- data/lib/rom/fmp/dataset.rb +121 -0
- data/lib/rom/fmp/gateway.rb +28 -0
- data/lib/rom/fmp/header.rb +57 -0
- data/lib/rom/fmp/micro01.rb +55 -0
- data/lib/rom/fmp/micro02.rb +161 -0
- data/lib/rom/fmp/mini.rb +91 -0
- data/lib/rom/fmp/relation.rb +18 -0
- data/lib/rom/fmp/relation/associations.rb +104 -0
- data/lib/rom/fmp/relation/class_methods.rb +48 -0
- data/lib/rom/fmp/relation/inspection.rb +16 -0
- data/lib/rom/fmp/rfm/fmresultset.yml +86 -0
- data/lib/rom/fmp/rfm/layout.rb +8 -0
- data/lib/rom/fmp/version.rb +5 -0
- data/rom-fmp.gemspec +28 -0
- data/spec/rom/fmp_spec.rb +12 -0
- data/spec/rom/gateway_spec.rb +53 -0
- data/spec/rom/relation_spec.rb +17 -0
- data/spec/spec_helper.rb +15 -0
- metadata +164 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 03a589c1294de76a28b2e07762205c329356ae90
|
4
|
+
data.tar.gz: 754d5e2f884652cfaf214b64a638192c1c445089
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d0ccd25e0f13e3e7587a0d8d76191fa37ff905321a59fe651921fb7146a3593c0e412840b3168f027a5568ab07cc4bb8875dd24e5657ed97056d372ae4d50a5c
|
7
|
+
data.tar.gz: f3bd10a8bdd7e7bee0391e69b0e75443f56f544d51072b7f6e5152756cc0ca79a457e86ab39e795de1e7c6f3ab062de0eeda15faf5a7a7b2c149cd219b817b18
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# rom-fmp changelog
|
2
|
+
|
3
|
+
## v0.0.4 2015-09-17
|
4
|
+
|
5
|
+
* Rewrite gateway, dataset, relation.
|
6
|
+
* Basic relation support for :find, :all, :count, :create, :update, :delete.
|
7
|
+
* Supports command composition/chaining and command currying.
|
8
|
+
|
9
|
+
## v0.0.1 2015-03-30
|
10
|
+
|
11
|
+
* Initial commit.
|
12
|
+
* Basic reads using relations.
|
13
|
+
|
data/Gemfile
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,60 @@
|
|
1
|
+
# rom-fmp
|
2
|
+
|
3
|
+
A filemaker adapter for the rom-rb data mapping & persistence gem.
|
4
|
+
See [rom-rb](https://github.com/rom-rb) on github or [rom-rb.org](http://rom-rb.org)
|
5
|
+
for more information about Ruby Object Mapper.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'rom-fmp'
|
12
|
+
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install rom-fmp
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
require 'rom/fmp'
|
25
|
+
|
26
|
+
DB_CONFIG = {
|
27
|
+
adapter: 'fmp',
|
28
|
+
host: 'my.fm.server.com',
|
29
|
+
account_name: 'my_account',
|
30
|
+
password: '12345',
|
31
|
+
database: 'MyFmpDatabase',
|
32
|
+
}
|
33
|
+
|
34
|
+
ROM.use(:auto_registration)
|
35
|
+
ROM.setup(:fmp, DB_CONFIG)
|
36
|
+
|
37
|
+
class Users < ROM::Relation[:fmp]
|
38
|
+
register_as :users
|
39
|
+
dataset :user_xml # Filemaker layout name.
|
40
|
+
|
41
|
+
def by_login(name)
|
42
|
+
find(:login=>name.to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
def activated
|
46
|
+
find(:activated_at=>'>1/1/2000')
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
rom_env = ROM.finalize.env
|
52
|
+
|
53
|
+
rom_users_relation = rom_env.relation(:users)
|
54
|
+
|
55
|
+
activated_users_by_login = rom_users_relation.activated.by_login
|
56
|
+
|
57
|
+
activated_users_by_login.call('bill').to_a
|
58
|
+
activated_users_by_login.('bill').to_a
|
59
|
+
activated_users_by_login['bill'].to_a
|
60
|
+
|
data/Rakefile
ADDED
data/lib/rom-fmp.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rom/fmp'
|
data/lib/rom/fmp.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rfm'
|
2
|
+
require 'rom'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
module FMP
|
7
|
+
ConstraintError = Class.new(ROM::CommandError)
|
8
|
+
FMRESULTSET_TEMPLATE = {:template=>YAML.load_file(File.expand_path("../fmp/rfm/fmresultset.yml", __FILE__))}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require "rom/fmp/version"
|
13
|
+
require "rom/fmp/dataset"
|
14
|
+
require "rom/fmp/gateway"
|
15
|
+
require "rom/fmp/relation"
|
16
|
+
|
17
|
+
#require "rom/fmp/mini"
|
18
|
+
#require "rom/fmp/micro01"
|
19
|
+
#require "rom/fmp/micro02"
|
20
|
+
|
21
|
+
ROM.register_adapter(:fmp, ROM::FMP)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
### NOT USED YET ###
|
2
|
+
|
3
|
+
require 'rom/commands'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
module FMP
|
7
|
+
module Commands
|
8
|
+
ERRORS = [
|
9
|
+
# Sequel::UniqueConstraintViolation,
|
10
|
+
# Sequel::NotNullConstraintViolation
|
11
|
+
].freeze
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'rom/fmp/commands/create'
|
17
|
+
require 'rom/fmp/commands/update'
|
18
|
+
require 'rom/fmp/commands/delete'
|
19
|
+
#require 'rom/fmp/commands_ext/postgres'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rom/fmp/commands'
|
2
|
+
#require 'rom/fmp/commands/transaction'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module FMP
|
6
|
+
module Commands
|
7
|
+
class Create < ROM::Commands::Create
|
8
|
+
#include Transaction
|
9
|
+
|
10
|
+
def execute(tuples)
|
11
|
+
insert_tuples = Array([tuples]).flatten.map do |tuple|
|
12
|
+
attributes = input[tuple]
|
13
|
+
validator.call(attributes)
|
14
|
+
attributes.to_h
|
15
|
+
end
|
16
|
+
|
17
|
+
insert(insert_tuples)
|
18
|
+
rescue *ERRORS => e
|
19
|
+
raise ConstraintError, e.message
|
20
|
+
end
|
21
|
+
|
22
|
+
def insert(tuples)
|
23
|
+
pks = tuples.map { |tuple| relation.insert(tuple) }
|
24
|
+
relation.where(relation.primary_key => pks)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rom/fmp/commands'
|
2
|
+
#require 'rom/fmp/commands/transaction'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module FMP
|
6
|
+
module Commands
|
7
|
+
class Delete < ROM::Commands::Delete
|
8
|
+
# include Transaction
|
9
|
+
|
10
|
+
def execute
|
11
|
+
deleted = target.to_a
|
12
|
+
target.delete
|
13
|
+
deleted
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rom/fmp/commands'
|
2
|
+
#require 'rom/fmp/commands/transaction'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module FMP
|
6
|
+
module Commands
|
7
|
+
class Update < ROM::Commands::Update
|
8
|
+
#include Transaction
|
9
|
+
|
10
|
+
option :original, type: Hash, reader: true
|
11
|
+
|
12
|
+
alias_method :to, :call
|
13
|
+
|
14
|
+
def execute(tuple)
|
15
|
+
attributes = input[tuple]
|
16
|
+
validator.call(attributes)
|
17
|
+
|
18
|
+
changed = diff(attributes.to_h)
|
19
|
+
|
20
|
+
if changed.any?
|
21
|
+
update(changed)
|
22
|
+
else
|
23
|
+
[]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def change(original)
|
28
|
+
self.class.new(relation, options.merge(original: original))
|
29
|
+
end
|
30
|
+
|
31
|
+
def update(tuple)
|
32
|
+
pks = relation.map { |t| t[primary_key] }
|
33
|
+
dataset = relation.dataset
|
34
|
+
dataset.update(tuple)
|
35
|
+
dataset.unfiltered.where(primary_key => pks).to_a
|
36
|
+
end
|
37
|
+
|
38
|
+
def primary_key
|
39
|
+
relation.primary_key
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def diff(tuple)
|
45
|
+
if original
|
46
|
+
Hash[tuple.to_a - (tuple.to_a & original.to_a)]
|
47
|
+
else
|
48
|
+
tuple
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
#require 'rom/support/array_dataset'
|
2
|
+
require 'rom'
|
3
|
+
require 'rfm'
|
4
|
+
require 'rom/fmp/rfm/layout'
|
5
|
+
require 'charlatan'
|
6
|
+
|
7
|
+
module ROM
|
8
|
+
module FMP
|
9
|
+
|
10
|
+
class Dataset
|
11
|
+
# This was used but is not now.
|
12
|
+
# include Rom::ArrayDataset
|
13
|
+
|
14
|
+
# Used to compile compound queries from chained relations.
|
15
|
+
include ::Rfm::Scope
|
16
|
+
|
17
|
+
DEFAULT_REQUEST_OPTIONS = {}
|
18
|
+
|
19
|
+
# Dataset instance expects to hold Array of data in @data,
|
20
|
+
# but it will also hold a FM Layout instance.
|
21
|
+
# If any call to Dataset instance returns Array instance,
|
22
|
+
# it will be wrapped in a new Dataset instance.
|
23
|
+
include Charlatan.new(:data, kind: Array)
|
24
|
+
attr_reader :layout, :data, :queries
|
25
|
+
|
26
|
+
# Store layout, data, query in new dataset.
|
27
|
+
def initialize(_layout, _data=[], _queries=[])
|
28
|
+
@layout = _layout
|
29
|
+
@queries = _queries
|
30
|
+
#puts "DATASET NEW queries:#{@queries}"
|
31
|
+
super(_data)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
# Creates new dataset with current args and resultset. Not lazy.
|
37
|
+
# This may not be how rom or sql uses 'where'. Find out more.
|
38
|
+
def where(*args)
|
39
|
+
#self.class.new(layout, layout.find(*args), args)
|
40
|
+
get_results(:find, args)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates new dataset with existing data & queries, plus new query
|
44
|
+
def find(*args)
|
45
|
+
#self.class.new(layout, data, (queries.dup << args))
|
46
|
+
wrap_data(data, (queries.dup << args))
|
47
|
+
end
|
48
|
+
|
49
|
+
def any(options={})
|
50
|
+
wrap_data(layout.any(options))
|
51
|
+
end
|
52
|
+
|
53
|
+
def all(options={})
|
54
|
+
wrap_data(layout.all(DEFAULT_REQUEST_OPTIONS.merge(options)))
|
55
|
+
end
|
56
|
+
|
57
|
+
def count(*args)
|
58
|
+
compiled_query = compile_query
|
59
|
+
compiled_query ? layout.count(*compiled_query) : layout.total_count
|
60
|
+
end
|
61
|
+
|
62
|
+
def create(args={})
|
63
|
+
get_results(:create, [args]) unless args.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
def update(record_id, args={})
|
67
|
+
get_results(:edit, [record_id, args]) unless args.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete(record_id)
|
71
|
+
get_results(:delete, record_id)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
# Triggers actual fm action.
|
77
|
+
def to_a
|
78
|
+
(data.nil? || data.empty?) ? call.data.to_a : data.to_a
|
79
|
+
end
|
80
|
+
|
81
|
+
# Triggers actual fm action.
|
82
|
+
def each
|
83
|
+
# passes block - if any - to upstream each.
|
84
|
+
to_a.each(&Proc.new)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Combines all queries, sends to FM, returns result in new dataset.
|
88
|
+
def call
|
89
|
+
compiled_query = compile_query
|
90
|
+
wrap_data(compiled_query ? layout.find(*compiled_query) : layout.all(DEFAULT_REQUEST_OPTIONS))
|
91
|
+
end
|
92
|
+
|
93
|
+
# Mixes chained queries together into single query.
|
94
|
+
# Now works with multiple-request queries (using new rfm scope feature).
|
95
|
+
# Other ways: consider mixing multi-request queries with intersection: (result1 & result2),
|
96
|
+
# or with the new scope feature: query1(scope:query2(scope:query3))
|
97
|
+
def compile_query
|
98
|
+
#puts "DATASET COMPILE self #{self}"
|
99
|
+
#puts "DATASET COMPILE queries #{queries}"
|
100
|
+
|
101
|
+
# Old way: works but doesn't handle fmp compound queries.
|
102
|
+
#query.each_with_object([{},{}]){|x,o| o[0].merge!(x[0] || {}); o[1].merge!(x[1] || {})}
|
103
|
+
|
104
|
+
# New way: handles compound queries. Reqires ginjo-rfm 3.0.11.
|
105
|
+
return unless queries # This should help introspecting dataset that results from record deletion. TODO: test this.
|
106
|
+
queries.inject {|new_query,scope| apply_scope(new_query, scope)} ##puts "SCOPE INJECTION scope:#{scope} new_query:#{new_query}";
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns new dataset containing, data, layout, query.
|
110
|
+
def wrap_data(_data=data, _queries=queries, _layout=layout)
|
111
|
+
self.class.new(_layout, _data, _queries)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Send method & query to layout, and wrap results in new dataset.
|
115
|
+
def get_results(_method, query=queries, _layout=layout)
|
116
|
+
wrap_data(_layout.send(_method, *query), query, _layout)
|
117
|
+
end
|
118
|
+
|
119
|
+
end # Dataset
|
120
|
+
end # FMP
|
121
|
+
end # ROM
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rom'
|
2
|
+
require 'rfm'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module FMP
|
6
|
+
|
7
|
+
class Gateway < ROM::Gateway
|
8
|
+
attr_reader :datasets, :database
|
9
|
+
|
10
|
+
def initialize(*options)
|
11
|
+
@database = Rfm.database(options[0].to_h.merge(FMRESULTSET_TEMPLATE).to_h)
|
12
|
+
@datasets = Hash.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def dataset(name)
|
16
|
+
datasets[name.to_s] ||= Dataset.new(@database[name.to_s])
|
17
|
+
end
|
18
|
+
|
19
|
+
# This is required per lint specs
|
20
|
+
alias_method :[], :dataset
|
21
|
+
|
22
|
+
def dataset?(name)
|
23
|
+
datasets.key?(name.to_s)
|
24
|
+
end
|
25
|
+
end # Gateway
|
26
|
+
|
27
|
+
end # FMP
|
28
|
+
end # ROM
|