hanami-model 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/README.md +54 -420
  4. data/hanami-model.gemspec +9 -6
  5. data/lib/hanami/entity.rb +107 -191
  6. data/lib/hanami/entity/schema.rb +236 -0
  7. data/lib/hanami/model.rb +52 -138
  8. data/lib/hanami/model/association.rb +37 -0
  9. data/lib/hanami/model/associations/belongs_to.rb +19 -0
  10. data/lib/hanami/model/associations/dsl.rb +29 -0
  11. data/lib/hanami/model/associations/has_many.rb +200 -0
  12. data/lib/hanami/model/configuration.rb +52 -224
  13. data/lib/hanami/model/configurator.rb +62 -0
  14. data/lib/hanami/model/entity_name.rb +35 -0
  15. data/lib/hanami/model/error.rb +37 -24
  16. data/lib/hanami/model/mapping.rb +29 -35
  17. data/lib/hanami/model/migration.rb +31 -0
  18. data/lib/hanami/model/migrator.rb +111 -88
  19. data/lib/hanami/model/migrator/adapter.rb +39 -16
  20. data/lib/hanami/model/migrator/connection.rb +23 -11
  21. data/lib/hanami/model/migrator/mysql_adapter.rb +38 -17
  22. data/lib/hanami/model/migrator/postgres_adapter.rb +20 -19
  23. data/lib/hanami/model/migrator/sqlite_adapter.rb +9 -8
  24. data/lib/hanami/model/plugins.rb +25 -0
  25. data/lib/hanami/model/plugins/mapping.rb +55 -0
  26. data/lib/hanami/model/plugins/schema.rb +55 -0
  27. data/lib/hanami/model/plugins/timestamps.rb +118 -0
  28. data/lib/hanami/model/relation_name.rb +24 -0
  29. data/lib/hanami/model/sql.rb +161 -0
  30. data/lib/hanami/model/sql/console.rb +41 -0
  31. data/lib/hanami/model/sql/consoles/abstract.rb +33 -0
  32. data/lib/hanami/model/sql/consoles/mysql.rb +63 -0
  33. data/lib/hanami/model/sql/consoles/postgresql.rb +68 -0
  34. data/lib/hanami/model/sql/consoles/sqlite.rb +46 -0
  35. data/lib/hanami/model/sql/entity/schema.rb +125 -0
  36. data/lib/hanami/model/sql/types.rb +95 -0
  37. data/lib/hanami/model/sql/types/schema/coercions.rb +198 -0
  38. data/lib/hanami/model/types.rb +99 -0
  39. data/lib/hanami/model/version.rb +1 -1
  40. data/lib/hanami/repository.rb +287 -723
  41. metadata +77 -40
  42. data/EXAMPLE.md +0 -213
  43. data/lib/hanami/entity/dirty_tracking.rb +0 -74
  44. data/lib/hanami/model/adapters/abstract.rb +0 -281
  45. data/lib/hanami/model/adapters/file_system_adapter.rb +0 -288
  46. data/lib/hanami/model/adapters/implementation.rb +0 -111
  47. data/lib/hanami/model/adapters/memory/collection.rb +0 -132
  48. data/lib/hanami/model/adapters/memory/command.rb +0 -113
  49. data/lib/hanami/model/adapters/memory/query.rb +0 -653
  50. data/lib/hanami/model/adapters/memory_adapter.rb +0 -179
  51. data/lib/hanami/model/adapters/null_adapter.rb +0 -24
  52. data/lib/hanami/model/adapters/sql/collection.rb +0 -287
  53. data/lib/hanami/model/adapters/sql/command.rb +0 -88
  54. data/lib/hanami/model/adapters/sql/console.rb +0 -33
  55. data/lib/hanami/model/adapters/sql/consoles/mysql.rb +0 -49
  56. data/lib/hanami/model/adapters/sql/consoles/postgresql.rb +0 -48
  57. data/lib/hanami/model/adapters/sql/consoles/sqlite.rb +0 -26
  58. data/lib/hanami/model/adapters/sql/query.rb +0 -788
  59. data/lib/hanami/model/adapters/sql_adapter.rb +0 -296
  60. data/lib/hanami/model/coercer.rb +0 -74
  61. data/lib/hanami/model/config/adapter.rb +0 -116
  62. data/lib/hanami/model/config/mapper.rb +0 -45
  63. data/lib/hanami/model/mapper.rb +0 -124
  64. data/lib/hanami/model/mapping/attribute.rb +0 -85
  65. data/lib/hanami/model/mapping/coercers.rb +0 -314
  66. data/lib/hanami/model/mapping/collection.rb +0 -490
  67. data/lib/hanami/model/mapping/collection_coercer.rb +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 29439a9ce3f9aa3cdd0600a6a04a0316209eb91d
4
- data.tar.gz: fa6bb573a2e58f14cc3b09126168fdc4ed88f72a
3
+ metadata.gz: ac96a7e41555f5a379c12d3fd45c999ea4ce7e93
4
+ data.tar.gz: 59f4d8fe809cd3536bc47fff73830ba37304a6c7
5
5
  SHA512:
6
- metadata.gz: 10a26d11cb59a13d93664a4e9fd29d35f8fa6ce788d95281b7b805ed04cf9a4ca390ace931c3a943ee65fc8a55b138f9931d5c351a50d09b19d8acf8b1845cae
7
- data.tar.gz: 5d5ef5cf0bdee581b7d4d9272fd064c3c8973ea4a6da813fe318f49c24b00a285c6a4b2a410166b19560ddbc6a4a9a69f4a3221d79efc952f3d40e8234599a63
6
+ metadata.gz: f730a23b1a29d426405c1cb79ee2ea74ab73f9baaa73521c7136b3f39913a4a9dcc6cdeed2e45ce4ffd2b334fbb839e637b8ff60edbafa19777d47fb9e5c505d
7
+ data.tar.gz: 382a94c09b68ed787d67643b6128ce1d2f54b0d2c669bc2c0248ad45c8e54fbfe14bd613bb9e9268988d16dcc29f2dd05c30be08c7461ec1a30b11fe34f9f635
data/CHANGELOG.md CHANGED
@@ -1,6 +1,50 @@
1
1
  # Hanami::Model
2
2
  A persistence layer for Hanami
3
3
 
4
+ ## v0.7.0 - 2016-11-15
5
+ ### Added
6
+ - [Luca Guidi] `Hanami::Entity` defines an automatic schema for SQL databases
7
+ – [Luca Guidi] `Hanami::Entity` attributes schema
8
+ - [Luca Guidi] Experimental support for One-To-Many association (aka `has_many`)
9
+ - [Luca Guidi] Native support for PostgreSQL types like UUID, Array, JSON(B) and Money
10
+ - [Luca Guidi] Repositories instances can access all the relations (eg. `BookRepository` can access `users` relation via `#users`)
11
+ - [Luca Guidi] Automapping for SQL databases
12
+ - [Luca Guidi] Added `Hanami::Model::DatabaseError`
13
+
14
+ ### Changed
15
+ - [Luca Guidi] Entities are immutable
16
+ - [Luca Guidi] Removed support for Memory and File System adapters
17
+ - [Luca Guidi] Removed support for _dirty tracking_
18
+ - [Luca Guidi] `Hanami::Entity.attributes` method no longer accepts a list of attributes, but a block to optionally define typed attributes
19
+ - [Luca Guidi] Removed `#fetch`, `#execute` and `#transaction` from repository
20
+ - [Luca Guidi] Removed `mapping` block from `Hanami::Model.configure`
21
+ - [Luca Guidi] Changed `adapter` signature in `Hanami::Model.configure` (use `adapter :sql, ENV['DATABASE_URL']`)
22
+ - [Luca Guidi] Repositories must inherit from `Hanami::Repository` instead of including it
23
+ - [Luca Guidi] Entities must inherit from `Hanami::Entity` instead of including it
24
+ - [Pascal Betz] Repositories use instance level interface (eg. `BookRepository.new.find` instead of `BookRepository.find`)
25
+ - [Luca Guidi] Repositories now accept hashes for CRUD operations
26
+ - [Luca Guidi] `Hanami::Repository#create` now accepts: hash (or entity)
27
+ - [Luca Guidi] `Hanami::Repository#update` now accepts two arguments: primary key (`id`) and data (or entity)
28
+ - [Luca Guidi] `Hanami::Repository#delete` now accepts: primary key (`id`)
29
+ - [Luca Guidi] Drop `Hanami::Model::NonPersistedEntityError`, `Hanami::Model::InvalidMappingError`, `Hanami::Model::InvalidCommandError`, `Hanami::Model::InvalidQueryError`
30
+ - [Luca Guidi] Official support for Ruby 2.3 and JRuby 9.0.5.0
31
+ - [Luca Guidi] Drop support for Ruby 2.0, 2.1, 2.2, and JRuby 9.0.0.0
32
+ - [Luca Guidi] Drop support for `mysql` gem in favor of `mysql2`
33
+
34
+ ### Fixed
35
+ - [Luca Guidi] Ensure booleans to be correctly dumped in database
36
+ - [Luca Guidi] Ensure to respect default database schema values
37
+ - [Luca Guidi] Ensure SQL UPDATE to not override non-default primary key
38
+ - [James Hamilton] Print appropriate error message when trying to create a PostgreSQL database that is already existing
39
+
40
+ ## v0.6.2 - 2016-06-01
41
+ ### Changed
42
+ - [Kjell-Magne Øierud] Ensure inherited entities to expose attributes from base class
43
+
44
+ ## v0.6.1 - 2016-02-05
45
+ ### Changed
46
+ - [Hélio Costa e Silva & Pascal Betz] Mapping SQL Adapter's errors as `Hanami::Model` errors
47
+
4
48
  ## v0.6.1 - 2016-02-05
5
49
  ### Changed
6
50
  - [Hélio Costa e Silva & Pascal Betz] Mapping SQL Adapter's errors as `Hanami::Model` errors
data/README.md CHANGED
@@ -7,11 +7,8 @@ The architecture eases keeping the business logic (entities) separated from deta
7
7
 
8
8
  It implements the following concepts:
9
9
 
10
- * [Entity](#entities) - An object defined by its identity.
10
+ * [Entity](#entities) - A model domain object defined by its identity.
11
11
  * [Repository](#repositories) - An object that mediates between the entities and the persistence layer.
12
- * [Data Mapper](#data-mapper) - A persistence mapper that keep entities independent from database details.
13
- * [Adapter](#adapter) – A database adapter.
14
- * [Query](#query) - An object that represents a database query.
15
12
 
16
13
  Like all the other Hanami components, it can be used as a standalone framework or within a full Hanami application.
17
14
 
@@ -35,7 +32,7 @@ Like all the other Hanami components, it can be used as a standalone framework o
35
32
 
36
33
  ## Rubies
37
34
 
38
- __Hanami::Model__ supports Ruby (MRI) 2.2+ and JRuby 9000+
35
+ __Hanami::Model__ supports Ruby (MRI) 2.3+ and JRuby 9.1.5.0+
39
36
 
40
37
  ## Installation
41
38
 
@@ -55,51 +52,40 @@ Or install it yourself as:
55
52
 
56
53
  ## Usage
57
54
 
58
- This class provides a DSL to configure adapter, mapping and collection.
55
+ This class provides a DSL to configure the connection.
59
56
 
60
57
  ```ruby
61
58
  require 'hanami/model'
62
59
 
63
- class User
64
- include Hanami::Entity
65
- attributes :name, :age
60
+ class User < Hanami::Entity
66
61
  end
67
62
 
68
- class UserRepository
69
- include Hanami::Repository
63
+ class UserRepository < Hanami::Repository
70
64
  end
71
65
 
72
66
  Hanami::Model.configure do
73
- adapter type: :sql, uri: 'postgres://localhost/database'
67
+ adapter :sql, 'postgres://username:password@localhost/bookshelf'
68
+ end.load!
74
69
 
75
- mapping do
76
- collection :users do
77
- entity User
78
- repository UserRepository
79
-
80
- attribute :id, Integer
81
- attribute :name, String
82
- attribute :age, Integer
83
- end
84
- end
85
- end
70
+ repository = UserRepository.new
71
+ user = repository.create(name: 'Luca')
86
72
 
87
- Hanami::Model.load!
73
+ puts user.id # => 1
88
74
 
89
- user = User.new(name: 'Luca', age: 32)
90
- user = UserRepository.create(user)
75
+ found = repository.find(user.id)
76
+ found == user # => true
91
77
 
92
- puts user.id # => 1
78
+ updated = repository.update(user.id, age: 34)
79
+ updated.age # => 34
93
80
 
94
- u = UserRepository.find(user.id)
95
- u == user # => true
81
+ repository.delete(user.id)
96
82
  ```
97
83
 
98
84
  ## Concepts
99
85
 
100
86
  ### Entities
101
87
 
102
- An object that is defined by its identity.
88
+ A model domain object that is defined by its identity.
103
89
  See "Domain Driven Design" by Eric Evans.
104
90
 
105
91
  An entity is the core of an application, where the part of the domain logic is implemented.
@@ -115,59 +101,10 @@ message passing if you will, which is the quintessence of Object Oriented Progra
115
101
  ```ruby
116
102
  require 'hanami/model'
117
103
 
118
- class Person
119
- include Hanami::Entity
120
- attributes :name, :age
104
+ class Person < Hanami::Entity
121
105
  end
122
106
  ```
123
107
 
124
- When a class includes `Hanami::Entity` it receives the following interface:
125
-
126
- * `#id`
127
- * `#id=`
128
- * `#initialize(attributes = {})`
129
-
130
- `Hanami::Entity` also provides the `.attributes` for defining attribute accessors for the given names.
131
-
132
- If we expand the code above in **pure Ruby**, it would be:
133
-
134
- ```ruby
135
- class Person
136
- attr_accessor :id, :name, :age
137
-
138
- def initialize(attributes = {})
139
- @id, @name, @age = attributes.values_at(:id, :name, :age)
140
- end
141
- end
142
- ```
143
-
144
- **Hanami::Model** ships `Hanami::Entity` for developers's convenience.
145
-
146
- **Hanami::Model** depends on a narrow and well-defined interface for an Entity - `#id`, `#id=`, `#initialize(attributes={})`.
147
- If your object implements that interface then that object can be used as an Entity in the **Hanami::Model** framework.
148
-
149
- However, we suggest to implement this interface by including `Hanami::Entity`, in case that future versions of the framework will expand it.
150
-
151
- See [Dependency Inversion Principle](http://en.wikipedia.org/wiki/Dependency_inversion_principle) for more on interfaces.
152
-
153
- When a class extends a `Hanami::Entity` class, it will also *inherit* its mother's attributes.
154
-
155
- ```ruby
156
- require 'hanami/model'
157
-
158
- class Article
159
- include Hanami::Entity
160
- attributes :name
161
- end
162
-
163
- class RareArticle < Article
164
- attributes :price
165
- end
166
- ```
167
-
168
- That is, `RareArticle`'s attributes carry over `:name` attribute from `Article`,
169
- thus is `:id, :name, :price`.
170
-
171
108
  ### Repositories
172
109
 
173
110
  An object that mediates between entities and the persistence layer.
@@ -192,18 +129,16 @@ This architecture has several advantages:
192
129
 
193
130
  When a class includes `Hanami::Repository`, it will receive the following interface:
194
131
 
195
- * `.persist(entity)` – Create or update an entity
196
- * `.create(entity)` Create a record for the given entity
197
- * `.update(entity)` Update the record corresponding to the given entity
198
- * `.delete(entity)` Delete the record corresponding to the given entity
199
- * `.all` - Fetch all the entities from the collection
200
- * `.find` - Fetch an entity from the collection by its ID
201
- * `.first` - Fetch the first entity from the collection
202
- * `.last` - Fetch the last entity from the collection
203
- * `.clear` - Delete all the records from the collection
204
- * `.query` - Fabricates a query object
205
-
206
- **A collection is a homogenous set of records.**
132
+ * `#create(data)` – Create a record for the given data (or entity)
133
+ * `#update(id, data)` Update the record corresponding to the given id by setting the given data (or entity)
134
+ * `#delete(id)` Delete the record corresponding to the given id
135
+ * `#all` - Fetch all the entities from the relation
136
+ * `#find` - Fetch an entity from the relation by primary key
137
+ * `#first` - Fetch the first entity from the relation
138
+ * `#last` - Fetch the last entity from the relation
139
+ * `#clear` - Delete all the records from the relation
140
+
141
+ **A relation is a homogenous set of records.**
207
142
  It corresponds to a table for a SQL database or to a MongoDB collection.
208
143
 
209
144
  **All the queries are private**.
@@ -212,7 +147,7 @@ This decision forces developers to define intention revealing API, instead of le
212
147
  Look at the following code:
213
148
 
214
149
  ```ruby
215
- ArticleRepository.where(author_id: 23).order(:published_at).limit(8)
150
+ ArticleRepository.new.where(author_id: 23).order(:published_at).limit(8)
216
151
  ```
217
152
 
218
153
  This is **bad** for a variety of reasons:
@@ -232,14 +167,11 @@ There is a better way:
232
167
  ```ruby
233
168
  require 'hanami/model'
234
169
 
235
- class ArticleRepository
236
- include Hanami::Repository
237
-
238
- def self.most_recent_by_author(author, limit = 8)
239
- query do
240
- where(author_id: author.id).
241
- order(:published_at)
242
- end.limit(limit)
170
+ class ArticleRepository < Hanami::Repository
171
+ def most_recent_by_author(author, limit: 8)
172
+ articles.where(author_id: author.id).
173
+ order(:published_at).
174
+ limit(limit)
243
175
  end
244
176
  end
245
177
  ```
@@ -256,253 +188,31 @@ This is a **huge improvement**, because:
256
188
 
257
189
  * If we change the storage, the callers aren't affected.
258
190
 
259
- Here is an extended example of a repository that uses the SQL adapter.
260
-
261
- ```ruby
262
- class ArticleRepository
263
- include Hanami::Repository
264
-
265
- def self.most_recent_by_author(author, limit = 8)
266
- query do
267
- where(author_id: author.id).
268
- desc(:id).
269
- limit(limit)
270
- end
271
- end
272
-
273
- def self.most_recent_published_by_author(author, limit = 8)
274
- most_recent_by_author(author, limit).published
275
- end
276
-
277
- def self.published
278
- query do
279
- where(published: true)
280
- end
281
- end
282
-
283
- def self.drafts
284
- exclude published
285
- end
286
-
287
- def self.rank
288
- published.desc(:comments_count)
289
- end
191
+ ### Mapping
290
192
 
291
- def self.best_article_ever
292
- rank.limit(1)
293
- end
193
+ Hanami::Model can **_automap_** columns from relations and entities attributes.
294
194
 
295
- def self.comments_average
296
- query.average(:comments_count)
297
- end
298
- end
299
- ```
300
-
301
- You can also extract the common logic from your repository into a module to reuse it in other repositories. Here is a pagination example:
302
-
303
- ```ruby
304
- module RepositoryHelpers
305
- module Pagination
306
- def paginate(limit: 10, offset: 0)
307
- query do
308
- limit(limit).offset(offset)
309
- end
310
- end
311
- end
312
- end
313
-
314
- class ArticleRepository
315
- include Hanami::Repository
316
- extend RepositoryHelpers::Pagination
317
-
318
- def self.published
319
- query do
320
- where(published: true)
321
- end
322
- end
323
-
324
- # other repository-specific methods here
325
- end
326
- ```
327
-
328
- That will allow `.paginate` usage on `ArticleRepository`, for example:
329
- `ArticleRepository.published.paginate(15, 0)`
330
-
331
- **Your models and repositories have to be in the same namespace.** Otherwise `Hanami::Model::Mapper#load!`
332
- will not initialize your repositories correctly.
333
-
334
- ```ruby
335
- class MyHanamiApp::Model::User
336
- include Hanami::Entity
337
- # your code here
338
- end
339
-
340
- # This repository will work...
341
- class MyHanamiApp::Model::UserRepository
342
- include Hanami::Repository
343
- # your code here
344
- end
345
-
346
- # ...this will not!
347
- class MyHanamiApp::Repository::UserRepository
348
- include Hanami::Repository
349
- # your code here
350
- end
351
- ```
352
-
353
- ### Data Mapper
354
-
355
- A persistence mapper that keeps entities independent from database details.
356
- It is database independent, it can work with SQL, document, and even with key/value stores.
357
-
358
- The role of a data mapper is to translate database columns into the corresponding attribute of an entity.
195
+ However, there are cases where columns and attribute names do not match (mainly **legacy databases**).
359
196
 
360
197
  ```ruby
361
198
  require 'hanami/model'
362
199
 
363
- mapper = Hanami::Model::Mapper.new do
364
- collection :users do
365
- entity User
200
+ class UserRepository < Hanami::Repository
201
+ self.relation = :t_user_archive
366
202
 
367
- attribute :id, Integer
368
- attribute :name, String
369
- attribute :age, Integer
370
- end
371
- end
372
- ```
373
-
374
- For simplicity's sake, imagine that the mapper above is used with a SQL database.
375
- We use `#collection` to indicate the name of the table that we want to map, `#entity` to indicate the class that we want to associate.
376
- In the end, each call to `#attribute` associates the specified column with a corresponding Ruby type.
377
-
378
- For advanced mapping and legacy databases, please have a look at the API doc.
379
-
380
- **Known limitations**
381
-
382
- Note there are limitations with inherited entities:
383
-
384
- ```ruby
385
- require 'hanami/model'
386
-
387
- class Article
388
- include Hanami::Entity
389
- attributes :name
390
- end
391
-
392
- class RareArticle < Article
393
- attributes :price
394
- end
395
-
396
- mapper = Hanami::Model::Mapper.new do
397
- collection :articles do
398
- entity Article
399
-
400
- attribute :id, Integer
401
- attribute :name, String
402
- attribute :price, Integer
203
+ mapping do
204
+ attribute :id, from: :i_user_id
205
+ attribute :name, from: :s_name
206
+ attribute :age, from: :i_age
403
207
  end
404
208
  end
405
209
  ```
406
-
407
- In the example above, there are a few problems:
408
-
409
- * `Article` could not be fetched because mapping could not map `price`.
410
- * Finding a persisted `RareArticle` record, for eg. `ArticleRepository.find(123)`,
411
- the result is an `Article` not `RareArticle`.
412
-
413
- ### Adapter
414
-
415
- An adapter is a concrete implementation of persistence logic for a specific database.
416
- **Hanami::Model** is shipped with three adapters:
417
-
418
- * SqlAdapter
419
- * MemoryAdapter
420
- * FileSystemAdapter
421
-
422
- An adapter can be associated with one or multiple repositories.
423
-
424
- ```ruby
425
- require 'pg'
426
- require 'hanami/model'
427
- require 'hanami/model/adapters/sql_adapter'
428
-
429
- mapper = Hanami::Model::Mapper.new do
430
- # ...
431
- end
432
-
433
- adapter = Hanami::Model::Adapters::SqlAdapter.new(mapper, 'postgres://host:port/database')
434
-
435
- PersonRepository.adapter = adapter
436
- ArticleRepository.adapter = adapter
437
- ```
438
-
439
- In the example above, we reuse the adapter because the target tables (`people` and `articles`) are defined in the same database.
440
- **As rule of thumb, one adapter instance per database.**
441
-
442
- ### Query
443
-
444
- An object that implements an interface for querying the database.
445
- This interface may vary, according to the adapter's specifications.
446
-
447
- Here is common interface for existing class:
448
-
449
- * `.all` - Resolves the query by fetching records from the database and translating them into entities
450
- * `.where`, `.and` - Adds a condition that behaves like SQL `WHERE`
451
- * `.or` - Adds a condition that behaves like SQL `OR`
452
- * `.exclude`, `.not` - Logical negation of a #where condition
453
- * `.select` - Selects only the specified columns
454
- * `.order`, `.asc` - Specify the ascending order of the records, sorted by the given columns
455
- * `.reverse_order`, `.desc` - Specify the descending order of the records, sorted by the given columns
456
- * `.limit` - Limit the number of records to return
457
- * `.offset` - Specify an `OFFSET` clause. Due to SQL syntax restriction, offset MUST be used with `#limit`
458
- * `.sum` - Returns the sum of the values for the given column
459
- * `.average`, `.avg` - Returns the average of the values for the given column
460
- * `.max` - Returns the maximum value for the given column
461
- * `.min` - Returns the minimum value for the given column
462
- * `.interval` - Returns the difference between the MAX and MIN for the given column
463
- * `.range` - Returns a range of values between the MAX and the MIN for the given column
464
- * `.exist?` - Checks if at least one record exists for the current conditions
465
- * `.count` - Returns a count of the records for the current conditions
466
- * `.join` - Adds an inner join with a table (only SQL)
467
- * `.left_join` - Adds a left join with a table (only SQL)
468
-
469
- If you need more information regarding those methods, you can use comments from [memory](https://github.com/hanami/model/blob/master/lib/hanami/model/adapters/memory/query.rb#L29) or [sql](https://github.com/hanami/model/blob/master/lib/hanami/model/adapters/sql/query.rb#L28) adapters interface.
470
-
471
- Think of an adapter for Redis, it will probably employ different strategies to filter records than an SQL query object.
472
-
473
- ### Model Error Coercions
474
-
475
- All adapters' errors are encapsulated into Hanami error classes.
476
-
477
- Hanami Model may raise the following exceptions:
478
-
479
- * `Hanami::Model::UniqueConstraintViolationError`
480
- * `Hanami::Model::ForeignKeyConstraintViolationError`
481
- * `Hanami::Model::NotNullConstraintViolationError`
482
- * `Hanami::Model::CheckConstraintViolationError`
483
-
484
- For any other adapter's errors, Hanami will raise the `Hanami::Model::InvalidCommandError` object.
485
- All errors contains the root cause and the full error message thrown by sql adapter.
210
+ **NOTE:** This feature should be used only when **_automapping_** fails because the naming mismatch.
486
211
 
487
212
  ### Conventions
488
213
 
489
214
  * A repository must be named after an entity, by appending `"Repository"` to the entity class name (eg. `Article` => `ArticleRepository`).
490
215
 
491
- ### Configurations
492
-
493
- * Non-standard repository can be configured for an entity, by setting `repository` on the collection.
494
-
495
- ```ruby
496
- require 'hanami/model'
497
-
498
- mapper = Hanami::Model::Mapper.new do
499
- collection :users do
500
- entity User
501
- repository EmployeeRepository
502
- end
503
- end
504
- ```
505
-
506
216
  ### Thread safety
507
217
 
508
218
  **Hanami::Model**'s is thread safe during the runtime, but it isn't during the loading process.
@@ -525,105 +235,29 @@ If an entity has the following accessors: `:created_at` and `:updated_at`, they
525
235
  ```ruby
526
236
  require 'hanami/model'
527
237
 
528
- class User
529
- include Hanami::Entity
530
- attributes :name, :created_at, :updated_at
531
- end
532
-
533
- class UserRepository
534
- include Hanami::Repository
535
- end
536
-
537
- Hanami::Model.configure do
538
- adapter type: :memory, uri: 'memory://localhost/timestamps'
539
-
540
- mapping do
541
- collection :users do
542
- entity User
543
- repository UserRepository
544
-
545
- attribute :id, Integer
546
- attribute :name, String
547
- attribute :created_at, DateTime
548
- attribute :updated_at, DateTime
549
- end
550
- end
551
- end.load!
552
-
553
- user = User.new(name: 'L')
554
- puts user.created_at # => nil
555
- puts user.updated_at # => nil
556
-
557
- user = UserRepository.create(user)
558
- puts user.created_at.to_s # => "2015-05-15T10:12:20+00:00"
559
- puts user.updated_at.to_s # => "2015-05-15T10:12:20+00:00"
560
-
561
- sleep 3
562
- user.name = "Luca"
563
- user = UserRepository.update(user)
564
- puts user.created_at.to_s # => "2015-05-15T10:12:20+00:00"
565
- puts user.updated_at.to_s # => "2015-05-15T10:12:23+00:00"
566
- ```
567
-
568
- ### Dirty Tracking
569
-
570
- Entities are able to track changes of their data, if `Hanami::Entity::DirtyTracking` is included.
571
-
572
- ```ruby
573
- require 'hanami/model'
574
-
575
- class User
576
- include Hanami::Entity
577
- include Hanami::Entity::DirtyTracking
578
- attributes :name, :age
238
+ class User < Hanami::Entity
579
239
  end
580
240
 
581
- class UserRepository
582
- include Hanami::Repository
241
+ class UserRepository < Hanami::Repository
583
242
  end
584
243
 
585
244
  Hanami::Model.configure do
586
- adapter type: :memory, uri: 'memory://localhost/dirty_tracking'
587
-
588
- mapping do
589
- collection :users do
590
- entity User
591
- repository UserRepository
592
-
593
- attribute :id, Integer
594
- attribute :name, String
595
- attribute :age, String
596
- end
597
- end
245
+ adapter :sql, uri: 'postgresql://localhost/bookshelf'
598
246
  end.load!
599
247
 
600
- user = User.new(name: 'L')
601
- user.changed? # => false
602
-
603
- user.age = 33
604
- user.changed? # => true
605
- user.changed_attributes # => {:age=>33}
248
+ repository = UserRepository.new
606
249
 
607
- user = UserRepository.create(user)
608
- user.changed? # => false
250
+ user = repository.create(name: 'Luca')
609
251
 
610
- user.update(name: 'Luca')
611
- user.changed? # => true
612
- user.changed_attributes # => {:name=>"Luca"}
252
+ puts user.created_at.to_s # => "2016-09-19 13:40:13 UTC"
253
+ puts user.updated_at.to_s # => "2016-09-19 13:40:13 UTC"
613
254
 
614
- user = UserRepository.update(user)
615
- user.changed? # => false
616
-
617
- result = UserRepository.find(user.id)
618
- result.changed? # => false
255
+ sleep 3
256
+ user = repository.update(user.id, age: 34)
257
+ puts user.created_at.to_s # => "2016-09-19 13:40:13 UTC"
258
+ puts user.updated_at.to_s # => "2016-09-19 13:40:16 UTC"
619
259
  ```
620
260
 
621
- ## Example
622
-
623
- For a full working example, have a look at [EXAMPLE.md](https://github.com/hanami/model/blob/master/EXAMPLE.md).
624
- Please remember that the setup code is only required for the standalone usage of **Hanami::Model**.
625
- A **Hanami** application will handle that configurations for you.
626
-
627
261
  ## Versioning
628
262
 
629
263
  __Hanami::Model__ uses [Semantic Versioning 2.0.0](http://semver.org)