ginjo-rfm 3.0.9 → 3.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/CHANGELOG.md +61 -40
- data/README.md +8 -8
- data/lib/rfm.rb +66 -67
- data/lib/rfm/VERSION +1 -1
- data/lib/rfm/base.rb +237 -241
- data/lib/rfm/database.rb +38 -24
- data/lib/rfm/error.rb +25 -25
- data/lib/rfm/layout.rb +217 -195
- data/lib/rfm/metadata/datum.rb +37 -37
- data/lib/rfm/metadata/field.rb +42 -39
- data/lib/rfm/metadata/field_control.rb +72 -72
- data/lib/rfm/metadata/layout_meta.rb +32 -32
- data/lib/rfm/metadata/resultset_meta.rb +74 -74
- data/lib/rfm/metadata/script.rb +3 -3
- data/lib/rfm/metadata/value_list_item.rb +30 -30
- data/lib/rfm/record.rb +80 -77
- data/lib/rfm/resultset.rb +63 -65
- data/lib/rfm/server.rb +31 -23
- data/lib/rfm/utilities/case_insensitive_hash.rb +4 -1
- data/lib/rfm/utilities/compound_query.rb +100 -101
- data/lib/rfm/utilities/config.rb +228 -218
- data/lib/rfm/utilities/connection.rb +65 -57
- data/lib/rfm/utilities/core_ext.rb +114 -119
- data/lib/rfm/utilities/factory.rb +122 -126
- data/lib/rfm/utilities/sax_parser.rb +1058 -1046
- data/lib/rfm/utilities/scope.rb +64 -0
- data/lib/rfm/version.rb +23 -7
- metadata +40 -39
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NzE0NWI5NTNjMDVhM2M0ZjZjMzI2Y2FiNDcyOGRiZmIxMGFlMmRiZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MDY0MjgyNjJlYjM5NjBhNmVkYzFiN2NiMjI1ZWI3N2MzNzJiMDIxYQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NTcxNTk2YWQ2NzMwYjJmZDEwZWJmNjA3Nzc2MDIxNTBlMTc2OTY1ZjhkYmQx
|
10
|
+
MmU5ZjA3ZGU1MDY0NTY3NDlkNDA5YTc5MTNjNDNjZTdhZWY4NGQ2NmRmOTY1
|
11
|
+
OTMxNzkwZWVhZjkzZDhjOGViNGYyYjYzZDRmMTc3NzQyNmU2OTM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MjY3MTNmYjBkNGU4YTE4ZTQ0MzA4YWU1NGQxYzY0NDdlZGIxOTQ2MGM4MGE2
|
14
|
+
Yjc2NTVlNDFhMjc5MzU4NDE3MGRlMjQ3NjFhMmY0Yzg1NjkwN2Q0YmJhMzky
|
15
|
+
YWEwZDg3ZTNhYzUwOWU2YWFkYTAwMWY4YTYxMmNjMTI5OTQwZDA=
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## Ginjo-Rfm 3.0.10
|
4
|
+
|
5
|
+
* Fixed bug where missing metadata would cause errors when creating/editing records.
|
6
|
+
|
7
|
+
* Added scoping support
|
8
|
+
|
9
|
+
scope = {:person_id => current_user.person_id}
|
10
|
+
Order.find([{:status => ['open', 'processing']}, {:omit => true, :item_count => "<1"}], :scope => scope)
|
11
|
+
|
12
|
+
class Orders < Rfm::Base
|
13
|
+
SCOPE = Proc.new { {:expired => "=" } }
|
14
|
+
end
|
15
|
+
Order.find({:user_id => '12345'})
|
16
|
+
|
17
|
+
class Orders < Rfm::Base
|
18
|
+
SCOPE = Proc.new {|args| {:user_id => args} }
|
19
|
+
end
|
20
|
+
Order.find([{:status => ['open', 'processing']}, {:omit => true, :item_count => "<1"}], :scope_args => current_user.id)
|
21
|
+
|
22
|
+
* Code cleanup
|
23
|
+
|
3
24
|
## Ginjo-Rfm 3.0.9
|
4
25
|
|
5
26
|
* Fixed bug in parser that was appending each portal array recursively to itself.
|
@@ -14,20 +35,20 @@
|
|
14
35
|
|
15
36
|
* Implemented proxy option for database connections thru a proxy server.
|
16
37
|
|
17
|
-
|
18
|
-
|
38
|
+
config :proxy=>['my.proxy.com', 8888]
|
39
|
+
|
19
40
|
* Implemented erb parsing of config.yml
|
20
|
-
|
41
|
+
|
21
42
|
* Disabled ```:grammar => :auto``` option. The current xml parser cannot yet use the FMPXMLRESULT grammar for general queries.
|
22
43
|
|
23
44
|
|
24
45
|
## Ginjo-Rfm 3.0.7
|
25
46
|
|
26
47
|
* Changed record creation so that generic records created from non-modelized layouts will be instances of Rfm::Record, instead of instances of a transient model class based on the layout. Transient model classes will foul up serialization and any number of other things. Records created from a user-defined model class will continue to be instances of the model class.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
48
|
+
|
49
|
+
my_layout.find(12345).class == Rfm::Record
|
50
|
+
MyModel.find(12345).class == MyModel
|
51
|
+
|
31
52
|
|
32
53
|
## Ginjo-Rfm 3.0.6
|
33
54
|
|
@@ -36,7 +57,7 @@
|
|
36
57
|
|
37
58
|
## Ginjo-Rfm 3.0.5
|
38
59
|
|
39
|
-
* Fixed parser handling of
|
60
|
+
* Fixed parser handling of ```<field>``` element that's missing a ```<data>``` element.
|
40
61
|
* Fixed coercion of repeating field data.
|
41
62
|
* Fixed case where special characters in Filemaker data yielded array instead of string (sax parsing split text).
|
42
63
|
* Fixed various bugs in metadata parsing.
|
@@ -79,10 +100,10 @@
|
|
79
100
|
* Record.new now automatically creats models based on layout name. Should there be an option to disable this?
|
80
101
|
* Removed ActiveSupport requirement (of course, ActiveSupport will load if ActiveModle is used, but that is the users' choice).
|
81
102
|
* Removed XmlMini, XmlParser, and related code & specs.
|
82
|
-
*
|
103
|
+
* Detached resultset from record, so record doesn't drag resultset around with it.
|
83
104
|
* Disabled automatic model creation from a table-name in a new Rfm::Record when initializing.
|
84
|
-
*
|
85
|
-
*
|
105
|
+
* Consolidated Base.new, Base#inititalize into Rfm::Record.
|
106
|
+
* Fixed validation callbacks issue.
|
86
107
|
* Fixed: Resultset will politely return [] when asked for non-existent portal\_names.
|
87
108
|
* Mods to rakefile benchmarking/profiling.
|
88
109
|
* Refactored Resultset metadata methods.
|
@@ -117,7 +138,7 @@
|
|
117
138
|
## Ginjo-Rfm 2.1.2
|
118
139
|
|
119
140
|
* Fixed config.rb so that :file\_path (to user-defined yml config file) can be specified as a single path string
|
120
|
-
|
141
|
+
or as an array of path strings.
|
121
142
|
|
122
143
|
## Ginjo-Rfm 2.1.1
|
123
144
|
|
@@ -127,9 +148,9 @@
|
|
127
148
|
|
128
149
|
## Ginjo-Rfm 2.1.0
|
129
150
|
|
130
|
-
* Removed
|
151
|
+
* Removed ```:include_portals``` query option in favor of ```:ignore_portals```.
|
131
152
|
|
132
|
-
* Added
|
153
|
+
* Added ```:max_portal_rows``` query option.
|
133
154
|
|
134
155
|
* Added field-remapping framework to allow model fields with different names than Filemaker fields.
|
135
156
|
|
@@ -138,11 +159,11 @@
|
|
138
159
|
* Detached new Server objects from Factory.servers hash, so wont reuse or stack-up servers.
|
139
160
|
|
140
161
|
* Added grammar translation layer between xml parser and Rfm, allowing all supported xml grammars to be used with Rfm.
|
141
|
-
|
142
|
-
|
162
|
+
This will also streamline changes/additions to Filemaker's xml grammar(s).
|
163
|
+
|
143
164
|
* Fixed case statement for ruby 1.9
|
144
165
|
|
145
|
-
* Configuration
|
166
|
+
* Configuration ```:use``` option now works for all Rfm objects that respond to ```config```.
|
146
167
|
|
147
168
|
## Ginjo-Rfm 2.0.2
|
148
169
|
|
@@ -192,47 +213,47 @@
|
|
192
213
|
|
193
214
|
* Re-implemented:
|
194
215
|
|
195
|
-
|
216
|
+
Layout#field\_controls
|
196
217
|
|
197
|
-
|
218
|
+
Layout#value\_lists
|
198
219
|
|
199
220
|
* Enhanced:
|
200
221
|
|
201
|
-
|
222
|
+
ValueListItem handles both display & data items now.
|
202
223
|
|
203
|
-
|
224
|
+
Timeout feature from timting (github/timting/rfm).
|
204
225
|
|
205
|
-
|
226
|
+
Added specs for Record#save.
|
206
227
|
|
207
228
|
* Fixed:
|
208
229
|
|
209
|
-
|
230
|
+
[Bug] Getting & setting fields with symbol-based keys was producing error.
|
210
231
|
|
211
|
-
|
232
|
+
[Bug] Setting fields would not update main record hash.
|
212
233
|
|
213
|
-
|
234
|
+
[Bug] Record#save wasn't merging back into self.
|
214
235
|
|
215
236
|
* Partial Fix:
|
216
237
|
|
217
|
-
|
218
|
-
|
219
|
-
|
238
|
+
server.db.all
|
239
|
+
db.layout.all
|
240
|
+
db.script.all
|
220
241
|
|
221
|
-
|
242
|
+
Note: the "#all" method returns object names (as keys) only. The receiver of the method maintains the full object collection.
|
222
243
|
|
223
|
-
|
244
|
+
Example:
|
224
245
|
|
225
|
-
|
226
|
-
|
246
|
+
server.db.all #=> ['dbname1', 'dbname2', ...]
|
247
|
+
server.db #=> a DbFactory object (descendant of Hash), containing 0 or more Database objects
|
227
248
|
|
228
249
|
## Lardawge-Rfm 1.4.2 (unreleased)
|
229
250
|
|
230
251
|
* Made nil default on fields with no value.
|
231
252
|
|
232
|
-
|
253
|
+
Example:
|
233
254
|
|
234
|
-
|
235
|
-
|
255
|
+
Old: record.john #=> ""
|
256
|
+
New: record.john #=> nil
|
236
257
|
|
237
258
|
## Lardawge-Rfm 1.4.1.2
|
238
259
|
|
@@ -252,13 +273,13 @@
|
|
252
273
|
|
253
274
|
* Added an option to load portal records which defaults to false. This significantly speeds up load time when portals are present on the layout.
|
254
275
|
|
255
|
-
|
276
|
+
Example:
|
256
277
|
|
257
|
-
|
258
|
-
|
278
|
+
result = fm_server('layout').find({:username => "==#{username}"}, {:include_portals => true})
|
279
|
+
# => This will fetch all records with portal records attached.
|
259
280
|
|
260
|
-
|
261
|
-
|
281
|
+
result.first.portals
|
282
|
+
# => would return an empty hash by default.
|
262
283
|
|
263
284
|
* Internal file restructuring. Some classes have changed but it should be nothing a developer would use API wise. Please let me know if it is.
|
264
285
|
|
data/README.md
CHANGED
@@ -24,12 +24,12 @@ Ginjo-rfm version 3 has been tested successfully on Ruby 1.8.7 thru 2.1.3.
|
|
24
24
|
|
25
25
|
## Documentation & Links
|
26
26
|
|
27
|
-
*
|
28
|
-
* Rdoc
|
27
|
+
* Gem <https://rubygems.org/gems/ginjo-rfm>
|
28
|
+
* Rdoc <http://rubydoc.info/github/ginjo/rfm>
|
29
|
+
* Github <https://github.com/ginjo/rfm>
|
29
30
|
* Discussion <http://groups.google.com/group/rfmcommunity>
|
30
|
-
*
|
31
|
-
*
|
32
|
-
* Lardawge at github <https://github.com/lardawge/rfm>
|
31
|
+
* Original <http://sixfriedrice.com/wp/products/rfm/>
|
32
|
+
* Lardawge <https://github.com/lardawge/rfm>
|
33
33
|
|
34
34
|
## Requirements
|
35
35
|
|
@@ -183,7 +183,7 @@ You can also include or extend the Rfm::Config module in any object in your proj
|
|
183
183
|
# using :parent to set where this object inherits config settings from
|
184
184
|
end
|
185
185
|
|
186
|
-
Use `get_config` to view the compiled configuration settings for any object. Configuration compilation will start at the top (rfm.yml), then work down the
|
186
|
+
Use `get_config` to view the compiled configuration settings for any object. Configuration compilation will start at the top (rfm.yml), then work down the hierarchy of objects to wherever you call the `get_config` method, merging in all global settings along the way. Subgroupings of settings will also be merged, if they are specified in a subgroup filter. A subgroup filter occurs any time you put `:use => :subgroup` in your configuration setting. You can have multiple subgroup filters, and when configuration compilation occurs, all subgroup filters are stacked up into an array and processed in order (as if you typed `:use=>[:subgroup1, :subgroup2, subgroup3, ...]` which is also allowed). `get_config` returns a compiled configuration hash, leaving all configuration settings in all modules and classes un-touched.
|
187
187
|
|
188
188
|
Person.get_config
|
189
189
|
|
@@ -512,7 +512,7 @@ All of these methods return an Rfm::Resultset object (see below), and every one
|
|
512
512
|
|
513
513
|
my_layout.find({:state => "AZ"}, {:max_records => 10, :skip_records => 100})
|
514
514
|
|
515
|
-
For a complete list of the available options, see the "
|
515
|
+
For a complete list of the available options, see the "Common Options" section in the layout.rb file.
|
516
516
|
|
517
517
|
Finally, if filemaker returns an error when executing any of these methods, an error will be raised in your Ruby script. There is one exception to this, though. If a find results in no records being found (FileMaker error # 401) I just ignore it and return you a Resultset with zero records in it. If you prefer an error in this case, add :raise_on_401 => true to the options you pass the Rfm::Server when you create it.
|
518
518
|
|
@@ -864,6 +864,6 @@ Other lead contributors:
|
|
864
864
|
* Atsushi Matsuo was an early Rfm tester, and provided outstanding feedback, critical code fixes, and a lot of web exposure.
|
865
865
|
* Jesse Antunes helped ensure that Rfm is stable and functional.
|
866
866
|
* Larry Sprock added ssl support, switched the xml parser to a much faster Nokogiri, added the rspec testing framework, and refined code architecture.
|
867
|
-
* William Richardson is the current maintainer of the ginjo-rfm fork and added support for multiple xml parsers, ActiveModel integration, field mapping, compound queries, logging, and a configuration framework.
|
867
|
+
* William Richardson is the current maintainer of the ginjo-rfm fork and added support for multiple xml parsers, ActiveModel integration, field mapping, compound queries, logging, scoping, and a configuration framework.
|
868
868
|
|
869
869
|
|
data/lib/rfm.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Rfm
|
2
|
-
|
3
|
-
|
2
|
+
PATH = File.expand_path(File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift(PATH) unless $LOAD_PATH.include?(PATH)
|
4
4
|
end
|
5
5
|
|
6
6
|
#require 'thread' # some versions of ActiveSupport will raise error about Mutex unless 'thread' is loaded.
|
@@ -10,7 +10,7 @@ require 'rfm/utilities/core_ext'
|
|
10
10
|
require 'rfm/utilities/case_insensitive_hash'
|
11
11
|
|
12
12
|
module Rfm
|
13
|
-
|
13
|
+
|
14
14
|
class CommunicationError < StandardError; end
|
15
15
|
class ParameterError < StandardError; end
|
16
16
|
class AuthenticationError < StandardError; end
|
@@ -28,18 +28,19 @@ module Rfm
|
|
28
28
|
autoload :CompoundQuery,'rfm/utilities/compound_query'
|
29
29
|
autoload :VERSION, 'rfm/version'
|
30
30
|
autoload :Connection, 'rfm/utilities/connection.rb'
|
31
|
+
autoload :Scope, 'rfm/utilities/scope.rb'
|
32
|
+
|
33
|
+
module Metadata
|
34
|
+
autoload :Script, 'rfm/metadata/script'
|
35
|
+
autoload :Field, 'rfm/metadata/field'
|
36
|
+
autoload :FieldControl, 'rfm/metadata/field_control'
|
37
|
+
autoload :ValueListItem, 'rfm/metadata/value_list_item'
|
38
|
+
autoload :Datum, 'rfm/metadata/datum'
|
39
|
+
autoload :ResultsetMeta, 'rfm/metadata/resultset_meta'
|
40
|
+
autoload :LayoutMeta, 'rfm/metadata/layout_meta'
|
41
|
+
end
|
31
42
|
|
32
|
-
|
33
|
-
autoload :Script, 'rfm/metadata/script'
|
34
|
-
autoload :Field, 'rfm/metadata/field'
|
35
|
-
autoload :FieldControl, 'rfm/metadata/field_control'
|
36
|
-
autoload :ValueListItem, 'rfm/metadata/value_list_item'
|
37
|
-
autoload :Datum, 'rfm/metadata/datum'
|
38
|
-
autoload :ResultsetMeta, 'rfm/metadata/resultset_meta'
|
39
|
-
autoload :LayoutMeta, 'rfm/metadata/layout_meta'
|
40
|
-
end
|
41
|
-
|
42
|
-
def info
|
43
|
+
def info
|
43
44
|
rslt = <<-EEOOFF
|
44
45
|
Gem name: ginjo-rfm
|
45
46
|
Version: #{VERSION}
|
@@ -51,57 +52,55 @@ module Rfm
|
|
51
52
|
rslt.gsub!(/^[ \t]*/, '')
|
52
53
|
rslt
|
53
54
|
rescue
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
extend self
|
106
|
-
|
55
|
+
"Could not retrieve info: #{$!}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def info_short
|
59
|
+
"Using ginjo-rfm version #{::Rfm::VERSION} with #{SaxParser::Handler.get_backend}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def_delegators 'Rfm::Factory', :servers, :server, :db, :database, :layout
|
63
|
+
def_delegators 'Rfm::Config', :config, :get_config, :config_clear
|
64
|
+
def_delegators 'Rfm::Resultset', :load_data
|
65
|
+
|
66
|
+
def models(*args)
|
67
|
+
Rfm::Factory.models(*args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def modelize(*args)
|
71
|
+
Rfm::Factory.modelize(*args)
|
72
|
+
end
|
73
|
+
|
74
|
+
def logger
|
75
|
+
@@logger ||= get_config[:logger] || Logger.new(STDOUT).tap {|l| l.formatter = proc {|severity, datetime, progname, msg| "#{datetime}: Rfm-#{severity} #{msg}\n"}}
|
76
|
+
end
|
77
|
+
|
78
|
+
alias_method :log, :logger
|
79
|
+
|
80
|
+
def logger=(obj)
|
81
|
+
@@logger = obj
|
82
|
+
end
|
83
|
+
|
84
|
+
# DEFAULT_CLASS = CaseInsensitiveHash
|
85
|
+
# TEMPLATE_PREFIX = File.join(File.dirname(__FILE__), 'rfm/utilities/sax/')
|
86
|
+
# TEMPLATES = {
|
87
|
+
# :fmpxmllayout => 'fmpxmllayout.yml',
|
88
|
+
# :fmresultset => 'fmresultset.yml',
|
89
|
+
# :fmpxmlresult => 'fmpxmlresult.yml',
|
90
|
+
# :none => nil
|
91
|
+
# }
|
92
|
+
|
93
|
+
PARSER_DEFAULTS = {
|
94
|
+
:default_class => CaseInsensitiveHash,
|
95
|
+
:template_prefix => File.join(File.dirname(__FILE__), 'rfm/utilities/sax/'),
|
96
|
+
:templates => {
|
97
|
+
:fmpxmllayout => 'fmpxmllayout.yml',
|
98
|
+
:fmresultset => 'fmresultset.yml',
|
99
|
+
:fmpxmlresult => 'fmpxmlresult.yml',
|
100
|
+
:none => nil
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
extend self
|
105
|
+
|
107
106
|
end # Rfm
|
data/lib/rfm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0.
|
1
|
+
3.0.10
|
data/lib/rfm/base.rb
CHANGED
@@ -1,201 +1,197 @@
|
|
1
1
|
module Rfm
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
2
|
+
|
3
|
+
# Adds ability to create Rfm::Base model classes that behave similar to ActiveRecord::Base models.
|
4
|
+
# If you set your Rfm.config (or RFM_CONFIG) with your host, database, account, password, and
|
5
|
+
# any other server/database options, you can provide your models with nothing more than a layout.
|
6
|
+
#
|
7
|
+
# class Person < Rfm::Base
|
8
|
+
# config :layout => 'mylayout'
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# And similar to ActiveRecord, you can define callbacks, validations, attributes, and methods on your model.
|
12
|
+
# (if you have ActiveModel loaded).
|
13
|
+
#
|
14
|
+
# class Account < Rfm::Base
|
15
|
+
# config :layout=>'account_xml'
|
16
|
+
# before_create :encrypt_password
|
17
|
+
# validates :email, :presence => true
|
18
|
+
# validates :username, :presence => true
|
19
|
+
# attr_accessor :password
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# Then in your project, you can use these models just like ActiveRecord models.
|
23
|
+
# The query syntax and options are still Rfm under the hood. Treat your model
|
24
|
+
# classes like Rfm::Layout objects, with a few enhancements.
|
25
|
+
#
|
26
|
+
# @account = Account.new :username => 'bill', :password => 'pass'
|
27
|
+
# @account.email = 'my@email.com'
|
28
|
+
# @account.save!
|
29
|
+
#
|
30
|
+
# @person = Person.find({:name => 'mike'}, :max_records => 50)[0]
|
31
|
+
# @person.update_attributes(:name => 'Michael', :title => "Senior Partner")
|
32
|
+
# @person.save
|
33
|
+
|
34
34
|
class Base < Rfm::Record #Hash
|
35
35
|
extend Config
|
36
36
|
config :parent => 'Rfm::Config'
|
37
|
-
|
37
|
+
|
38
38
|
begin
|
39
|
-
|
39
|
+
require 'active_model'
|
40
40
|
include ActiveModel::Validations
|
41
41
|
include ActiveModel::Serialization
|
42
42
|
extend ActiveModel::Callbacks
|
43
43
|
include ActiveModel::Validations::Callbacks
|
44
44
|
define_model_callbacks(:create, :update, :destroy)
|
45
45
|
rescue LoadError, StandardError
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
def run_callbacks(*args)
|
47
|
+
yield
|
48
|
+
end
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
def to_partial_path(object = self) #@object)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
@partial_names[object.class.name] ||= begin
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
52
|
+
return 'some/partial/path'
|
53
|
+
##### DISABLED HERE - ActiveModel Lint only needs a string #####
|
54
|
+
##### TODO: implement to_partial_path to return meaningful string.
|
55
|
+
# @partial_names[object.class.name] ||= begin
|
56
|
+
# object = object.to_model if object.respond_to?(:to_model)
|
57
|
+
|
58
|
+
# object.class.model_name.partial_path.dup.tap do |partial|
|
59
|
+
# path = @view.controller_path
|
60
|
+
# partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
end
|
64
|
+
|
65
|
+
class << self
|
66
|
+
|
67
|
+
# Access layout functions from base model
|
68
|
+
def_delegators :layout, :db, :server, :field_controls, :field_names, :value_lists, :total_count,
|
69
|
+
:query, :all, :delete, :portal_meta, :portal_names, :database, :table, :count, :ignore_bad_data
|
70
|
+
|
71
|
+
def inherited(model)
|
72
|
+
(Rfm::Factory.models << model).uniq unless Rfm::Factory.models.include? model
|
73
|
+
model.config :parent=>'Rfm::Base'
|
74
|
+
end
|
75
|
+
|
76
|
+
def config(*args)
|
77
|
+
super(*args){|options| @config.merge!(:layout=>options[:strings][0]) if options[:strings] && options[:strings][0]}
|
78
|
+
end
|
79
|
+
|
80
|
+
# Access/create the layout object associated with this model
|
81
|
+
def layout
|
82
|
+
return @layout if @layout
|
83
|
+
# cnf = get_config
|
84
|
+
# raise "Could not get :layout from get_config in Base.layout method" unless cnf[:layout] #return unless cnf[:layout]
|
85
|
+
# @layout = Rfm::Factory.layout(cnf).sublayout
|
86
|
+
name = get_config[:layout] || 'test' # The 'test' was added to help active-model-lint tests pass.
|
87
|
+
@layout = Rfm::Factory.layout(name, self) #.sublayout
|
88
|
+
|
89
|
+
# Added by wbr to give config hierarchy: layout -> model -> sublayout
|
90
|
+
#config :parent=>'parent_layout'
|
91
|
+
#config :parent=>'Rfm::Config'
|
92
|
+
#@layout.config model
|
93
|
+
#@layout.config :parent=>self
|
94
|
+
|
95
|
+
@layout.model = self
|
96
|
+
@layout
|
97
|
+
end
|
98
|
+
|
99
|
+
# # Access the parent layout of this model
|
100
|
+
# def parent_layout
|
101
|
+
# layout #.parent_layout
|
102
|
+
# end
|
103
|
+
|
104
|
+
# Just like Layout#find, but searching by record_id will return a record, not a resultset.
|
105
|
+
def find(find_criteria, options={})
|
106
|
+
#puts "base.find-#{layout.object_id}"
|
107
|
+
r = layout.find(find_criteria, options)
|
108
|
+
if ![Hash,Array].include?(find_criteria.class) and r.size == 1
|
109
|
+
r[0]
|
110
|
+
else
|
111
|
+
r
|
61
112
|
end
|
113
|
+
rescue Rfm::Error::RecordMissingError
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
# Layout#any, returns single record, not resultset
|
118
|
+
def any(*args)
|
119
|
+
layout.any(*args)[0]
|
120
|
+
end
|
121
|
+
|
122
|
+
# New record, save, (with callbacks & validations if ActiveModel is loaded)
|
123
|
+
def create(*args)
|
124
|
+
new(*args).send :create
|
125
|
+
end
|
126
|
+
|
127
|
+
# Using this method will skip callbacks. Use instance method +#update+ instead
|
128
|
+
def edit(*args)
|
129
|
+
layout.edit(*args)[0]
|
62
130
|
end
|
131
|
+
|
132
|
+
end # class << self
|
133
|
+
|
134
|
+
|
135
|
+
# Is this a newly created record, not saved yet?
|
136
|
+
def new_record?
|
137
|
+
return true if (self.record_id.nil? || self.record_id.empty?)
|
63
138
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
(Rfm::Factory.models << model).uniq unless Rfm::Factory.models.include? model
|
73
|
-
model.config :parent=>'Rfm::Base'
|
74
|
-
end
|
75
|
-
|
76
|
-
def config(*args)
|
77
|
-
super(*args){|options| @config.merge!(:layout=>options[:strings][0]) if options[:strings] && options[:strings][0]}
|
78
|
-
end
|
79
|
-
|
80
|
-
# Access/create the layout object associated with this model
|
81
|
-
def layout
|
82
|
-
return @layout if @layout
|
83
|
-
# cnf = get_config
|
84
|
-
# raise "Could not get :layout from get_config in Base.layout method" unless cnf[:layout] #return unless cnf[:layout]
|
85
|
-
# @layout = Rfm::Factory.layout(cnf).sublayout
|
86
|
-
name = get_config[:layout] || 'test' # The 'test' was added to help active-model-lint tests pass.
|
87
|
-
@layout = Rfm::Factory.layout(name, self) #.sublayout
|
88
|
-
|
89
|
-
# Added by wbr to give config heirarchy: layout -> model -> sublayout
|
90
|
-
#config :parent=>'parent_layout'
|
91
|
-
#config :parent=>'Rfm::Config'
|
92
|
-
#@layout.config model
|
93
|
-
#@layout.config :parent=>self
|
94
|
-
|
95
|
-
@layout.model = self
|
96
|
-
@layout
|
97
|
-
end
|
98
|
-
|
99
|
-
# # Access the parent layout of this model
|
100
|
-
# def parent_layout
|
101
|
-
# layout #.parent_layout
|
102
|
-
# end
|
103
|
-
|
104
|
-
# Just like Layout#find, but searching by record_id will return a record, not a resultset.
|
105
|
-
def find(find_criteria, options={})
|
106
|
-
#puts "base.find-#{layout.object_id}"
|
107
|
-
r = layout.find(find_criteria, options)
|
108
|
-
if ![Hash,Array].include?(find_criteria.class) and r.size == 1
|
109
|
-
r[0]
|
110
|
-
else
|
111
|
-
r
|
112
|
-
end
|
113
|
-
rescue Rfm::Error::RecordMissingError
|
114
|
-
nil
|
115
|
-
end
|
116
|
-
|
117
|
-
# Layout#any, returns single record, not resultset
|
118
|
-
def any(*args)
|
119
|
-
layout.any(*args)[0]
|
120
|
-
end
|
121
|
-
|
122
|
-
# New record, save, (with callbacks & validations if ActiveModel is loaded)
|
123
|
-
def create(*args)
|
124
|
-
new(*args).send :create
|
125
|
-
end
|
126
|
-
|
127
|
-
# Using this method will skip callbacks. Use instance method +#update+ instead
|
128
|
-
def edit(*args)
|
129
|
-
layout.edit(*args)[0]
|
130
|
-
end
|
131
|
-
|
132
|
-
end # class << self
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
# Is this a newly created record, not saved yet?
|
140
|
-
def new_record?
|
141
|
-
return true if (self.record_id.nil? || self.record_id.empty?)
|
142
|
-
end
|
143
|
-
|
144
|
-
# Reload record from database
|
145
|
-
# TODO: handle error when record has been deleted
|
146
|
-
# TODO: Move this to Rfm::Record.
|
147
|
-
def reload(force=false)
|
148
|
-
if (@mods.empty? or force) and record_id
|
149
|
-
@mods.clear
|
150
|
-
self.replace_with_fresh_data layout.find(self.record_id)[0] #self.class.find(self.record_id)
|
139
|
+
|
140
|
+
# Reload record from database
|
141
|
+
# TODO: handle error when record has been deleted
|
142
|
+
# TODO: Move this to Rfm::Record.
|
143
|
+
def reload(force=false)
|
144
|
+
if (@mods.empty? or force) and record_id
|
145
|
+
@mods.clear
|
146
|
+
self.replace_with_fresh_data layout.find(self.record_id)[0] #self.class.find(self.record_id)
|
151
147
|
end
|
152
148
|
end
|
153
|
-
|
149
|
+
|
154
150
|
# Mass update of record attributes, without saving.
|
155
151
|
def update_attributes(new_attr)
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
152
|
+
new_attr.each do |k,v|
|
153
|
+
k = k.to_s.downcase
|
154
|
+
if keys.include?(k)
|
155
|
+
@mods[k] = v
|
156
|
+
self[k] = v
|
157
|
+
else
|
158
|
+
instance_variable_set("@#{k}", v)
|
159
|
+
end
|
160
|
+
end
|
165
161
|
end
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
162
|
+
# # Mass update of record attributes, without saving.
|
163
|
+
# # TODO: return error or nil if input hash contains no recognizable keys.
|
164
|
+
# def update_attributes(new_attr)
|
165
|
+
# # creates new special hash
|
166
|
+
# input_hash = Rfm::CaseInsensitiveHash.new
|
167
|
+
# # populate new hash with input, coercing keys to strings
|
168
|
+
# #new_attr.each{|k,v| input_hash.merge! k.to_s=>v}
|
169
|
+
# new_attr.each{|k,v| input_hash[k.to_s] = v}
|
170
|
+
# # loop thru each layout field, adding data to @mods
|
171
|
+
# self.class.field_controls.keys.each do |field|
|
172
|
+
# field_name = field.to_s
|
173
|
+
# if input_hash.has_key?(field_name)
|
174
|
+
# #@mods.merge! field_name=>(input_hash[field_name] || '')
|
175
|
+
# @mods[field_name] = (input_hash[field_name] || '')
|
176
|
+
# end
|
177
|
+
# end
|
178
|
+
# # loop thru each input key-value,
|
179
|
+
# # creating new attribute if key doesn't exist in model.
|
180
|
+
# input_hash.each do |k,v|
|
181
|
+
# if !self.class.field_controls.keys.include?(k) and self.respond_to?(k)
|
182
|
+
# self.instance_variable_set("@#{k}", v)
|
183
|
+
# end
|
184
|
+
# end
|
185
|
+
# self.merge!(@mods) unless @mods == {}
|
186
|
+
# #self.merge!(@mods) unless @mods == Rfm::CaseInsensitiveHash.new
|
187
|
+
# end
|
188
|
+
|
193
189
|
# Mass update of record attributes, with saving.
|
194
190
|
def update_attributes!(new_attr)
|
195
191
|
self.update_attributes(new_attr)
|
196
192
|
self.save!
|
197
193
|
end
|
198
|
-
|
194
|
+
|
199
195
|
# Save record modifications to database (with callbacks & validations). If record cannot be saved will raise error.
|
200
196
|
def save!
|
201
197
|
#return unless @mods.size > 0
|
@@ -206,95 +202,95 @@ module Rfm
|
|
206
202
|
self.create
|
207
203
|
end
|
208
204
|
end
|
209
|
-
|
210
|
-
|
205
|
+
|
206
|
+
# Same as save!, but will not raise error.
|
211
207
|
def save
|
212
208
|
save!
|
213
209
|
rescue
|
214
210
|
(self.errors[:base] rescue []) << $!
|
215
211
|
return nil
|
216
212
|
end
|
217
|
-
|
218
|
-
|
213
|
+
|
214
|
+
# Just like Layout#save_if_not_modified, but with callbacks & validations.
|
219
215
|
def save_if_not_modified
|
220
216
|
update(@mod_id) if @mods.size > 0
|
221
|
-
end
|
222
|
-
|
217
|
+
end
|
218
|
+
|
223
219
|
# Delete record from database, with callbacks & validations.
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
220
|
+
def destroy
|
221
|
+
return unless record_id
|
222
|
+
run_callbacks :destroy do
|
223
|
+
self.class.delete(record_id)
|
224
|
+
@destroyed = true
|
225
|
+
@mods.clear
|
226
|
+
end
|
227
|
+
self.freeze
|
228
|
+
#self
|
229
|
+
end
|
230
|
+
|
231
|
+
def destroyed?
|
232
|
+
@destroyed
|
233
|
+
end
|
234
|
+
|
235
|
+
# For ActiveModel compatibility
|
236
|
+
def to_model
|
237
|
+
self
|
238
|
+
end
|
239
|
+
|
240
|
+
def persisted?
|
241
|
+
record_id ? true : false
|
242
|
+
end
|
243
|
+
|
244
|
+
def to_key
|
245
|
+
record_id ? [record_id] : nil
|
246
|
+
end
|
247
|
+
|
248
|
+
def to_param
|
249
|
+
record_id
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
protected # Base
|
254
|
+
|
255
|
+
def self.create_from_new(*args)
|
256
|
+
layout.create(*args)[0]
|
257
|
+
end
|
258
|
+
|
263
259
|
# shunt for callbacks when not using ActiveModel
|
264
260
|
def callback_deadend (*args)
|
265
261
|
yield #(*args)
|
266
262
|
end
|
267
|
-
|
263
|
+
|
268
264
|
def create
|
269
265
|
raise "Record not valid" if (defined?(ActiveModel::Validations) && !valid?)
|
270
266
|
run_callbacks :create do
|
271
267
|
return unless @mods.size > 0
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
268
|
+
# merge_rfm_result self.class.create_from_new(@mods)
|
269
|
+
replace_with_fresh_data self.class.create_from_new(@mods)
|
270
|
+
end
|
271
|
+
self
|
272
|
+
end
|
273
|
+
|
278
274
|
def update(mod_id=nil)
|
279
275
|
raise "Record not valid" if (defined?(ActiveModel::Validations) && !valid?)
|
280
276
|
return false unless record_id
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
277
|
+
run_callbacks :update do
|
278
|
+
return unless @mods.size > 0
|
279
|
+
unless mod_id
|
280
|
+
# regular save
|
281
|
+
# merge_rfm_result self.class.send :edit, record_id, @mods
|
282
|
+
replace_with_fresh_data self.class.send :edit, record_id, @mods
|
283
|
+
else
|
284
|
+
# save_if_not_modified
|
285
|
+
# merge_rfm_result self.class.send :edit, record_id, @mods, :modification_id=>mod_id
|
286
|
+
replace_with_fresh_data self.class.send :edit, record_id, @mods, :modification_id=>mod_id
|
291
287
|
end
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
288
|
+
end
|
289
|
+
self
|
290
|
+
end
|
291
|
+
|
292
|
+
# Deprecated in favor of Record#replace_with_fresh_data
|
293
|
+
def merge_rfm_result(result_record)
|
298
294
|
return unless @mods.size > 0
|
299
295
|
@record_id ||= result_record.record_id
|
300
296
|
self.merge! result_record
|
@@ -302,7 +298,7 @@ module Rfm
|
|
302
298
|
self || {}
|
303
299
|
#self || Rfm::CaseInsensitiveHash.new
|
304
300
|
end
|
305
|
-
|
301
|
+
|
306
302
|
end # Base
|
307
303
|
|
308
304
|
end # Rfm
|