pod4 0.10.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.bugs/bugs +2 -1
  3. data/.bugs/details/b5368c7ef19065fc597b5692314da71772660963.txt +53 -0
  4. data/.hgtags +1 -0
  5. data/Gemfile +5 -5
  6. data/README.md +157 -46
  7. data/lib/pod4/basic_model.rb +9 -22
  8. data/lib/pod4/connection.rb +67 -0
  9. data/lib/pod4/connection_pool.rb +154 -0
  10. data/lib/pod4/errors.rb +20 -0
  11. data/lib/pod4/interface.rb +34 -12
  12. data/lib/pod4/model.rb +32 -27
  13. data/lib/pod4/nebulous_interface.rb +25 -30
  14. data/lib/pod4/null_interface.rb +22 -16
  15. data/lib/pod4/pg_interface.rb +84 -104
  16. data/lib/pod4/sequel_interface.rb +138 -82
  17. data/lib/pod4/tds_interface.rb +83 -70
  18. data/lib/pod4/tweaking.rb +105 -0
  19. data/lib/pod4/version.rb +1 -1
  20. data/md/breaking_changes.md +80 -0
  21. data/spec/common/basic_model_spec.rb +67 -70
  22. data/spec/common/connection_pool_parallelism_spec.rb +154 -0
  23. data/spec/common/connection_pool_spec.rb +246 -0
  24. data/spec/common/connection_spec.rb +129 -0
  25. data/spec/common/model_ai_missing_id_spec.rb +256 -0
  26. data/spec/common/model_plus_encrypting_spec.rb +16 -4
  27. data/spec/common/model_plus_tweaking_spec.rb +128 -0
  28. data/spec/common/model_plus_typecasting_spec.rb +10 -4
  29. data/spec/common/model_spec.rb +283 -363
  30. data/spec/common/nebulous_interface_spec.rb +159 -108
  31. data/spec/common/null_interface_spec.rb +88 -65
  32. data/spec/common/sequel_interface_pg_spec.rb +217 -161
  33. data/spec/common/shared_examples_for_interface.rb +50 -50
  34. data/spec/jruby/sequel_encrypting_jdbc_pg_spec.rb +1 -1
  35. data/spec/jruby/sequel_interface_jdbc_ms_spec.rb +3 -3
  36. data/spec/jruby/sequel_interface_jdbc_pg_spec.rb +3 -23
  37. data/spec/mri/pg_encrypting_spec.rb +1 -1
  38. data/spec/mri/pg_interface_spec.rb +311 -223
  39. data/spec/mri/sequel_encrypting_spec.rb +1 -1
  40. data/spec/mri/sequel_interface_spec.rb +177 -180
  41. data/spec/mri/tds_encrypting_spec.rb +1 -1
  42. data/spec/mri/tds_interface_spec.rb +296 -212
  43. data/tags +340 -174
  44. metadata +19 -11
  45. data/md/fixme.md +0 -3
  46. data/md/roadmap.md +0 -125
  47. data/md/typecasting.md +0 -80
  48. data/spec/common/model_new_validate_spec.rb +0 -204
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d97fb4e3c277219cfee34b8d28bf0464f5ab6424
4
- data.tar.gz: a49c67b03b4dab994775eb5774b41cb897a494d4
2
+ SHA256:
3
+ metadata.gz: 1e0f3ba3e1a6e876874296334e8df4a789c6757403a6f72697e7f2a5506ac05f
4
+ data.tar.gz: db35eb4f442ebe83b30de9ce394354143586142144af531171f35afffa8e504c
5
5
  SHA512:
6
- metadata.gz: e567aa380a11cbbe270c89bd69a2b60d2c69583cb8d5dd71208d15e9b9b8511bb766be5a164f6cc57b23ad3e1cd63cb17f80e4e27373356f4b48527df8c79d55
7
- data.tar.gz: d62957ca14ceb46dfd33face48e48da8674fe5a82023ff7a259a048b45031be49ef09a114986f37c7b76dc2402c5041d8059e0ef6f0e2947d7621fb66f72902a
6
+ metadata.gz: 4d033f6947e0e2fcf421f764746f647c68e1158b65568a215c57db2508773aecc700815bc4ad318bb844f5252e89c64fe16eca6a1b20c60fb15730835d411b90
7
+ data.tar.gz: 68908e5107c5f1160b42b4948514554c57e53dcc337e413fd5b6282e6edc22ecf4dd51680a4a11f59103c6fb2ee1f8cb1d138f01b0f9007c5be41f4e2b984b1f
data/.bugs/bugs CHANGED
@@ -1,2 +1,3 @@
1
1
  Fixnum is Deprecated | owner:Andy Jones <andy.jones@jameshall.co.uk>, open:True, id:274eb8828bd4e7d879e4f7a99317c75eb2a2e2b0, time:1533817328.12
2
- BigDecimal.new is deprecated | owner:Andy Jones <andy.jones@jameshall.co.uk>, open:True, id:3979ce1679bc4f8c4aef8436e344e10e3773480d, time:1533817342.0
2
+ BigDecimal.new is deprecated | owner:Andy Jones <andy.jones@jameshall.co.uk>, open:False, id:3979ce1679bc4f8c4aef8436e344e10e3773480d, time:1533817342.0
3
+ Connection Pool Parallelism tests crash rspec when run as part of the suite | owner:Andy Jones <andy.jones@jameshall.co.uk>, open:False, id:b5368c7ef19065fc597b5692314da71772660963, time:1554280671.5
@@ -0,0 +1,53 @@
1
+ # Lines starting with '#' and sections without content
2
+ # are not displayed by a call to 'details'
3
+ #
4
+ [paths]
5
+ # Paths related to this bug.
6
+ # suggested format: REPO_PATH:LINENUMBERS
7
+
8
+
9
+ [details]
10
+ # Additional details
11
+ You can duplicate this by running the following tests in this order, in Jruby:
12
+
13
+ * common/connection_pool_parallelism_spec
14
+ * common/nebulous_interface_spec
15
+ * common/sequel_pg_interface_spec
16
+
17
+ Here's the error:
18
+
19
+ ```
20
+ Stomp::Error::ReceiveTimeout: Stomp::Error::ReceiveTimeout
21
+ block in _receive at /home/jonea/.rbenv/versions/jruby-9.1.17.0/lib/ruby/gems/shared/gems/stomp-1.4.4/lib/connec
22
+ tion/netio.rb:66
23
+ synchronize at org/jruby/ext/thread/Mutex.java:158
24
+ _receive at /home/jonea/.rbenv/versions/jruby-9.1.17.0/lib/ruby/gems/shared/gems/stomp-1.4.4/lib/connec
25
+ tion/netio.rb:31
26
+ __old_receive at /home/jonea/.rbenv/versions/jruby-9.1.17.0/lib/ruby/gems/shared/gems/stomp-1.4.4/lib/connec
27
+ tion/utils.rb:250
28
+ receive at /home/jonea/.rbenv/versions/jruby-9.1.17.0/lib/ruby/gems/shared/gems/stomp-1.4.4/lib/stomp/
29
+ connection.rb:481
30
+ block in start_listeners at /home/jonea/.rbenv/versions/jruby-9.1.17.0/lib/ruby/gems/shared/gems/stomp-1.4.4/lib/client
31
+ /utils.rb:194
32
+ loop at org/jruby/RubyKernel.java:1316
33
+ block in start_listeners at /home/jonea/.rbenv/versions/jruby-9.1.17.0/lib/ruby/gems/shared/gems/stomp-1.4.4/lib/client
34
+ /utils.rb:193
35
+ r
36
+ ```
37
+
38
+ This might be related to this jRuby bug: https://github.com/jruby/jruby/issues/5476.
39
+
40
+ [expected]
41
+ # The expected result
42
+
43
+
44
+ [actual]
45
+ # What happened instead
46
+
47
+
48
+ [reproduce]
49
+ # Reproduction steps
50
+
51
+
52
+ [comments]
53
+ # Comments and updates - leave your name
data/.hgtags CHANGED
@@ -32,3 +32,4 @@ f1b3a814e7a8c7d818b57f6d35ce7ed749573d76 0.9.1
32
32
  e98232cdc6cbe61e2bf0513fcf578001a2931018 0.10.4
33
33
  daae29a48272b5c3029b6509354bd3c3e6bf339a 0.10.5
34
34
  48d204fe041672e6df5845ad552497df8db3029a 0.10.6
35
+ 82ec1031be4084a673f2f979f9bbf9950e21d71a 1.0.0
data/Gemfile CHANGED
@@ -6,19 +6,19 @@ gemspec
6
6
  group :development, :test do
7
7
 
8
8
  # for bundler, management, etc etc
9
- gem "bundler", "~> 1.15"
9
+ gem "bundler", "~> 2"
10
10
  gem "rake", "~> 12"
11
- gem "rspec", "~> 3.7"
11
+ gem "rspec", "~> 3.8"
12
12
  gem 'pry'
13
13
  gem "pry-doc"
14
14
 
15
15
  # For testing
16
- gem "sequel", "~> 5.3"
16
+ gem "sequel", "~> 5.20"
17
17
  gem "nebulous_stomp", "~> 3"
18
18
 
19
19
  platforms :ruby do
20
- gem "sqlite3", "~> 1.3"
21
- gem "tiny_tds", "~> 1.0" # currently pinned to 1.0 because of FreeBDS 0.95 (Centos)
20
+ gem "sqlite3", "~> 1.4"
21
+ gem "tiny_tds", "~> 2.1"
22
22
  gem "pg"
23
23
  end
24
24
 
data/README.md CHANGED
@@ -49,7 +49,8 @@ I don't want the people who maintain my code to have to know the differences bet
49
49
  or so methods you need to worry about, and six of those are pretty much self-explanatory. Or, you
50
50
  can inherit from Pod4::BasicModel instead, and do without even that.
51
51
 
52
- I honestly don't think of it as an Object Relational Manager. I think of it as a Way To Have Nice Models.
52
+ I honestly don't think of it as an Object Relational Manager. I think of it as a Way To Have Nice
53
+ Models.
53
54
 
54
55
  If you are looking for something with all the features of, say, ActiveRecord, then this isn't for
55
56
  you. I provide basic access to and maintenance of records, with validation. For anything more, you
@@ -168,7 +169,7 @@ Here is the model and interface definition that goes with the above example:
168
169
 
169
170
  class ExampleInterface < Pod4::PgInterface
170
171
  set_table :example
171
- set_id_fld :id
172
+ set_id_fld :id, autoincrement: true
172
173
  end
173
174
 
174
175
  set_interface ExampleInterface.new($pg_conn)
@@ -199,11 +200,12 @@ the data. What they are depends on the interface, but the ones for PgInterface a
199
200
 
200
201
  Actually, _every_ interface defines `set_id_fld`. Instances of a model _must_ be represented by a
201
202
  single ID field that provides a unique identifier. Pod4 does not care what it's called or what data
202
- type it is -- if you say that's what makes it unique, that's good enough.
203
+ type it is -- if you say that's what makes it unique, that's good enough. Additionally you can
204
+ specify whether your key autoincrements or not. If you don't say, we assume that it does.
203
205
 
204
206
  Internally, Interfaces talk the same basic language of list / create / read / update / delete that
205
- models do. But I'm not finding the need to subclass these much. So that's probably going to be it
206
- for your Interface definition.
207
+ models do. Sometimes you might want to add a special interface method for a specific database
208
+ operation, but otherwise that's probably going to be it for your Interface definition.
207
209
 
208
210
  ### Model ###
209
211
 
@@ -214,14 +216,16 @@ Models have two of their own DSLish methods:
214
216
 
215
217
  You can see that interfaces are instantiated when the model is required. Exactly what you need to
216
218
  pass to the interface to instantiate it depends on the interface. SequelInterface wants the Sequel
217
- DB object (which means you have to require sequel, connect, and *then* require your models); the
218
- other interfaces only want connection hashes.
219
+ DB object; the other interfaces only want connection hashes.
220
+
221
+ (If you are finding it annoying to have to, for example, figure out your database connection hash
222
+ or get a Sequel DB object _before_ requiring your models, then see "Connections", below.)
219
223
 
220
224
  Any attributes you define using `attr_columns` are treated specially by Pod4::Model. You get all
221
225
  the effect of the standard Ruby `attr_accessor` call, but in addition, the attribute will be passed
222
226
  to and from the interface, and to and from your external code, by the standard model methods.
223
227
 
224
- In addition to the ones above, we have:
228
+ In addition to the methods above, we have:
225
229
 
226
230
  * `validate` -- override this to provide validation
227
231
  * `map_to_model` -- controls how the interface sets attributes on the model
@@ -231,7 +235,7 @@ In addition to the ones above, we have:
231
235
  A model also has some built-in attributes of its own:
232
236
 
233
237
  * `model_id` -- this is the value of the ID column you set in the interface.
234
- * `model_status` -- one of :error :warning :okay :deleted :empty
238
+ * `model_status` -- one of :error :warning :okay :deleted :unknown
235
239
 
236
240
  We'll deal with all these below.
237
241
 
@@ -240,11 +244,12 @@ Adding Validation
240
244
  -----------------
241
245
 
242
246
  Built into the model is an array of alerts (Pod4::Alert) which are messages that have been raised
243
- against the instance of the model class. Each alert can have a status of :error, :warning, :info or
244
- :success. If any alert has a status of :error :warning or :success then that is reflected in the
245
- model's `model_status` attribute.
247
+ against the instance of the model class. Each alert can have a type of :error, :warning, :info or
248
+ :success. If any alert has a type of :error or :warning, then that is reflected in the model's
249
+ `model_status` attribute. A model that has passed validation with no :error or :warning alerts is
250
+ status :okay.
246
251
 
247
- (In fact, there are two other possible statuses -- models are :empty when first created
252
+ (There are two other possible statuses -- models are :unknown when validation has yet to be run,
248
253
  and :deleted after a call to delete.)
249
254
 
250
255
  You can raise alerts yourself, and you normally do so by overriding `validate`. This method is
@@ -259,7 +264,7 @@ Here's a model with some validation:
259
264
  class CustomerInterface < Pod4::PgInterface
260
265
  set_schema :pod4example
261
266
  set_table :customer
262
- set_id_fld :id
267
+ set_id_fld :id, autoincrement: true
263
268
  end
264
269
 
265
270
  set_interface CustomerInterface.new($pg_conn)
@@ -287,7 +292,7 @@ the validation will fail. (Probably you do not want this on delete; test the pa
287
292
  to validate as in the example above).
288
293
 
289
294
  In passing I should note that validation is _not_ run on list: every record that list returns
290
- should be complete, but the `model_status` will be :empty because validation has not been run.
295
+ should be complete, but the `model_status` will be :unknown because validation has not been run.
291
296
  (This is partly for the sake of speed.)
292
297
 
293
298
  You should be aware that validation is not called on `set`, either. Because of that, it's entirely
@@ -304,16 +309,16 @@ datatimes should all end up as the right type in the model. (It depends on the
304
309
  going to get tired of me saying that, aren't you?) But maybe you want more than that.
305
310
 
306
311
  Let's imagine you have a database table in PostreSQL with a column called cost that uses the money
307
- type. And you want it to be a `BigDecimal` in the model. Well, Pod4 won't do that for you -- for
308
- all I know someone might have a problem with my requiring BigDecimal -- but it's not hard to do
309
- yourself.
312
+ type. (This is a terrible idea, by the way.) And you want it to be a `BigDecimal` in the model.
313
+ Well, Pod4 won't do that for you -- for all I know someone might have a problem with my requiring
314
+ BigDecimal -- but it's not hard to do yourself.
310
315
 
311
316
  class Product < Pod4::Model
312
317
 
313
318
  class ProductInterface < Pod4::PgInterface
314
319
  set_schema :pod4example
315
320
  set_table :product
316
- set_id_fld :product_id
321
+ set_id_fld :product_id, autoincrement: true
317
322
  end
318
323
 
319
324
  set_interface ProductInterface.new($pg_conn)
@@ -340,7 +345,8 @@ interface from the model. It _returns_ an Octothorpe to the interface. By defaul
340
345
  You might also want to ensure that your data types are honoured when your application updates a
341
346
  model object; in which case you will need to override `set` as well.
342
347
 
343
- At some point in the future, the Pod4::TypeCasting mixin will do most of this for you.
348
+ If this seems like a lot of work, take a look at the Pod4::TypeCasting mixin. it will handle it for
349
+ you.
344
350
 
345
351
 
346
352
  Relations
@@ -352,7 +358,7 @@ Pod4 does not provide relations. But, I'm not sure that it needs to. Look:
352
358
 
353
359
  class BlogPostInterface < Pod4::PgInterface
354
360
  set_table :blogpost
355
- set_id_fld :id
361
+ set_id_fld :id, autoincrement: true
356
362
  end
357
363
 
358
364
  set_interface BlogPostInterface.new($conn)
@@ -396,7 +402,7 @@ If your interface is connected to a SQL database, it should provide two more met
396
402
 
397
403
  class BlogPostInterface < Pod4::PgInterface
398
404
  set_table :blogpost
399
- set_id_fld :id
405
+ set_id_fld :id, autoincrement: true
400
406
  end
401
407
 
402
408
  set_interface BlogPostInterface.new($conn)
@@ -434,12 +440,110 @@ certainly need revisiting if you change database. But how often does that happen
434
440
  it ever does, you are likely to need to revisit the effected models anyway...
435
441
 
436
442
 
443
+ Connections
444
+ -----------
445
+
446
+ ### A Couple Of Wrinkles ###
447
+
448
+ There are a couple of weird wrinkles in all this which you may or may not have missed. Wrinkle
449
+ one:
450
+
451
+ class MyModel < Pod4::Model
452
+
453
+ class Interface < Pod4::PgInterface
454
+ set_id_fld :id, autoincrement: true
455
+ set_table :my_table
456
+ end
457
+
458
+ set_interface Interface.new($pgconn) # <- wrinkle
459
+ attr_columns :one, two, three
460
+ end
461
+
462
+ You _instantiate_ the interface at the point where you _define_ the model. I admit this is a
463
+ little odd. It wasn't a specific design decision on my part; rather it was the cleanest way to get
464
+ where I was going.
465
+
466
+ In practice this means you need to get your DB connection details from somewhere, maybe create your
467
+ Sequel DB object; and only then can you require your models.
468
+
469
+ Leading on from this, wrinkle two: except when using Sequel (which behaves differently) each
470
+ interface has its own connection to the database. This means that your application has (simplifying
471
+ a bit here) one database connection for each model class. So if you have a Customer model and a
472
+ Orders model, your application will hold two connections to the database. All customer enquiries
473
+ share a single connection, and all order enquiries share a single connection.
474
+
475
+ I'm finding that, generally, this scales about right. But if you have a lot of different models and a
476
+ database that runs out of connections easily, it might be problematic.
477
+
478
+ ### The Connection Object ###
479
+
480
+ The solution to both of these wrinkles, if you need one, is to use a Pod4::Connection object:
481
+
482
+ #
483
+ # init.rb -- bootup for my project
484
+ #
485
+
486
+ # Require libraries
487
+ require "sequel"
488
+ require "pod4"
489
+ require "pod4/sequel_interface"
490
+ require "pod4/connection"
491
+
492
+ # require models
493
+ $conn = Pod4::Connection.new(interface: Pod4::SequelInterface)
494
+ require_relative "models/customer"
495
+
496
+ # set up database connection
497
+ hash = get_sequel_params
498
+ $conn.data_layer_options = Sequel.connect(hash)
499
+
500
+ #############
501
+
502
+ #
503
+ # models/customer.rb ( only a part shown)
504
+ #
505
+
506
+ class Foo < Pod4::Model
507
+ class Interface < Pod4::SequelInterface
508
+ set_table :foo
509
+ set_id_field :id, autoincrement: true
510
+ end
511
+
512
+ set_interface Interface.new($conn)
513
+
514
+ TL;DR: the only code that needs to go in the middle of your requires is the line defining a
515
+ Connection object; you pass that to the interface instead. You can set up the parameters
516
+ the interface needs and pass them to the connection object afterwards.
517
+
518
+ With TdsInterface and PgInterface you can pass the same connection to multiple models and they will
519
+ share it. These interfaces take a Pod4::ConnectionPool instead, but otherwise the code looks
520
+ exactly the same as the above example.
521
+
522
+ (Technical note: the ConnectionPool object will actually provide multiple connections, one per
523
+ thread that uses it. This satisfies the requirement of the underlying libraries that connections
524
+ are not shared between threads, and therefore ensures that Pod4 is more or less thread safe. You
525
+ get this functionality automatically -- if you don't define a ConnectionPool, then the interface
526
+ will create one internally. Sequel uses its own thread pool, of course, and we only use the one
527
+ Sequel DB object for the whole of Pod4, so it doesn't need any of that. That's why it uses
528
+ Pod4::Connection, instead.)
529
+
530
+
437
531
  BasicModel
438
532
  ----------
439
533
 
440
534
  Sometimes your model needs to represent data in a way which is so radically different from the data
441
535
  source that the whole list, create, read, update, delete thing that Pod4::Model gives you is no
442
- use. Enter Pod4::BasicModel.
536
+ use.
537
+
538
+ Pod4::BasicModel gives you:
539
+
540
+ * `set_interface`
541
+ * the `model_id`, `model_status` and `alerts` attributes
542
+ * `add_alert`
543
+
544
+ ...and nothing else. But that's enough to make a model, your way, using the methods on the
545
+ interface. These are the same CRUDL methods that Pod4::Model provides -- except that on the
546
+ interface, the CRUD methods take a record id as a key.
443
547
 
444
548
  A real world example: at James Hall my intranet system has a User model, where each attribute is a
445
549
  parameter that controls how the system behaves for that user -- email address, security settings,
@@ -450,16 +554,6 @@ add a user parameter. The logical place to change the parameter is in the User m
450
554
  database, and certainly not both. So on the database, I have a settings table where the key runs:
451
555
  userid, setting name.
452
556
 
453
- Pod4::BasicModel gives you:
454
-
455
- * `set_interface`
456
- * the `model_id`, `model_status` and `alerts` attributes
457
- * `add_alert`
458
-
459
- ...and nothing else. But that's enough to make a model, your way, using the methods on the
460
- interface. These are the same CRUDL methods that Pod4::Model provides -- except that the CRUD
461
- methods take a record id as a key.
462
-
463
557
  Here's a simplified version of my User model. This one is read only, but it's hopefully enough to
464
558
  get the idea:
465
559
 
@@ -467,10 +561,9 @@ get the idea:
467
561
 
468
562
  class UserInterface < ::Pod4::SequelInterface
469
563
  set_table :settings
470
- set_id_fld :id
564
+ set_id_fld :id, autoincrement: false
471
565
  end
472
566
 
473
-
474
567
  # Here we set what settings always exist for a user
475
568
  Setting = Struct.new(:setName, :default)
476
569
 
@@ -485,19 +578,14 @@ get the idea:
485
578
  set_interface UserInterface.new($db)
486
579
  attr_reader :userid, :depot, :store, :menu, :roles, :name, :nick, :mail
487
580
 
488
-
489
581
  class << self
490
-
491
582
  def keys; DefaultSettings.map{|x| x.setName }; end
492
583
 
493
584
  def list
494
585
  array = interface.select(%Q|select distinct userid from settings;|)
495
586
  array.map {|r| self.new( r[:userid] ).read }
496
587
  end
497
-
498
- end
499
- ##
500
-
588
+ end # of class << self
501
589
 
502
590
  def initialize(userid=nil)
503
591
  super(userid)
@@ -507,7 +595,6 @@ get the idea:
507
595
  end
508
596
  end
509
597
 
510
-
511
598
  def read
512
599
  lst = interface.list(userid: @model_id)
513
600
 
@@ -517,12 +604,11 @@ get the idea:
517
604
 
518
605
  @userid = @model_id
519
606
  set_merge( Octothorpe.new(data) )
520
- validate; @model_status = :okay unless @model_status != :empty
607
+ validate; @model_status = :okay unless @model_status != :unknown
521
608
 
522
609
  self
523
610
  end
524
611
 
525
-
526
612
  def to_ot
527
613
  hash = self.class.keys.each_with_object({}) do |k,m|
528
614
  m[k] = instance_variable_get("@#{k}".to_sym)
@@ -531,7 +617,6 @@ get the idea:
531
617
  Octothorpe.new(hash)
532
618
  end
533
619
 
534
-
535
620
  def set_merge(hash)
536
621
  self.class.keys.each do |key|
537
622
  value = hash[key]
@@ -541,11 +626,37 @@ get the idea:
541
626
 
542
627
  end
543
628
 
629
+
544
630
  Extensions
545
631
  ----------
546
632
 
547
- There are now some mixins that you can use to extend the functionality of Pod4 models. Have a look
633
+ There are some mixins that you can use to extend the functionality of Pod4 models. Have a look
548
634
  at the comments at the top of the mixin in question if you want details.
549
635
 
550
636
  * typecasting -- force columns to be a specific ruby type, validation helpers, some encoding stuff
551
637
  * encrypting -- encrypt text columns
638
+ * tweaking -- adds DSL commands to support custom methods on the interface.
639
+
640
+
641
+ Gotchas
642
+ -------
643
+
644
+ Some hopefully-not-too-unexpected behaviour:
645
+
646
+ * If you change attributes on a record, then call #delete or #read on it, we don't warn you that
647
+ your changes are lost.
648
+
649
+ * If you attempt to update an autoincrement ID field, we don't write that change to the database
650
+ and we don't warn you about that.
651
+
652
+ * As mentioned above, we only run validate on #create, #read, #update and #delete. So you can
653
+ change the attributes of a record to something invalid without it immediately warning you. (You
654
+ can always run #validate yourself, though.)
655
+
656
+ * Again, as mentioned above, the array of model instances returned by #list will all be status
657
+ :unknown. This is because we have run neither #read nor #validate against them.
658
+
659
+ * I can't stop you writing to `@model_id` or `@model_status` in your model. I have no idea what
660
+ might happen if you do, but I doubt that it would ever be a good idea. (In a non-autoincrement
661
+ model, write to your ID field directly instead; `@model_id` will be updated when you call
662
+ #create, #update or of course #read.)