Releases: anykeyh/clear
v0.9
#v0.9
I'm pleased to announce the version 0.9 of Crystal Clear ORM !
This version is probably the biggest version released since Clear is born.
Under the hood, it simplifies a lot of code, push testing to another level with
a great amount of new specs.
On the feature part, it add full support for serializing to and from json, with mass assignment secure check, big decimal
type, PostgreSQL view management at migration, new callbacks methods, support for postgres interval object and so on...
Finally, the code has been tweaked to be compatible with release of Crystal 1.0.
With that in mind, Clear starts to mature, with only CLI, polymorphic relations and model inheritance still lacking.
Note of warning: some changes will break your code. However everything can be fixed in matter of minutes (hopefully)
Special thanks to all contributors of this version:
@007lva @anykeyh @GabFitzgerald @dukeraphaelng @mamantoha @watzon @yujiri8
(hopefully I did not forget someone)
Breaking changes
-
Clear::SQL::ConnectionPool
now returns DB::Connection instead of DB::Database (fix #177) -
Clear::Migration::Direction
is now an enum instead of a struct. -
where and having clauses use splat and named tuple always. This is breaking change.
- Before you had to do:
where("a = ?", [1])
Now you can do much more easy:
where("a = ?", 1)
Same apply for the named parameters version:
# Instead of where("a = :a", { a: 1 } ) # Do where("a = :a", a: 1)
Features
- PR #187 Add methods to import from and to
json
, with mass_assignment security
(thanks @dukeraphaelng and Caspian Baska for this awesome work!) - PR #191 Add Big Decimal support (@dukeraphaelng)
Collection#add_operation
has been renamed toCollection#append_operation
- Add
Clear::SQL.after_commit
method
Register a callback function which will be fired once when SQL COMMIT
operation is called
This can be used for example to send email, or perform others tasks
when you want to be sure the data is secured in the database.
transaction do
@user = User.find(1)
@user.subscribe!
Clear::SQL.after_commit{ Email.deliver(ConfirmationMail.new(@user)) }
end
In case the transaction fail and eventually rollback, the code won't be called.
Same method exists now on the model level, using before and after hooks:
class User
include Clear::Model
after(:commit){ |mdl| WelcomeEmail.new(mdl.as(User)).deliver_now }
end
Note: before(:commit)
and after(:commit)
are both called after the transaction has been commited.
Before hook always call before after hook.
- Add possibility to name and rollback to a specific savepoint:
Clear::SQL.with_savepoint("a") do
Clear::SQL.with_savepoint("b") do
Clear::SQL.rollback("a") # < Exit to "a"
end
puts "This won't be called"
end
puts "This will be called"
- Add
Clear.json_serializable_converter(CustomType)
This macro help setting a converter transparently for any CustomType
.
Your CustomType
must be JSON::Serializable
, and the database column
must be of type jsonb
, json
or text
.
class Color
include JSON::Serializable
@[JSON::Field]; property r: Int8
@[JSON::Field]; property g: Int8
@[JSON::Field]; property b: Int8
@[JSON::Field]; property a: Int8
end
Clear.json_serializable_converter(Color)
# Now you can use Color in your models:
class MyModel
include Clear::Model
column color : Color
end
- Add
jsonb().contains?(...)
method
This allow usage of Postgres ?
operator over jsonb
fields:
# SELECT * FROM actors WHERE "jsonb_column"->'movies' ? 'Top Gun' LIMIT 1;
Actor.query.where{ var("jsonb_column").jsonb("movies").contains?("Top Gun") }.first!.name # << Tom Cruise
- Add
SelectQuery#reverse_order_by
method
A convenient method to reverse all the order by clauses,
turning each ASC
to DESC
direction, and each NULLS FIRST
to NULLS LAST
Bugfixes
- Prepare the code to make it compatible with crystal 1.0. Change
Void
toNil
first
andlast
on collection object does not change the collection anymore (previously would add limit/offset and change order_by clauses)- Dozen of other bugs not tracked here have been fixed, by usage or new test sets.
v0.8
This version fix a lot of small things:
- Fix TimeInDay issues
- Fix issue when using DISTINCT with JOIN in models with custom SELECT
clause defined AFTER the joins. - Fix mistake in spec and add specs
- Add changelog; update shard
- Add seed command in the CLI
- add
or_where
feature - Fix FTS to remove ambiguous clauses
- Fix issue with nilable belongs_to which cannot be saved when set to nil
- Add RFC3339 support while converting string to time
- Fix caching with belongs_to
- Add colorize parameter to Clear::SQL::Logger module
- Migration: Add datatype conversion in add_column and alter_column methods
- Migration: Update migration add_column operation to allow contraints, nullable
and default value - Update to latest version of pg gem
- Fix ambigous column name in with_xxx method for belongs_to relation
- Add possibility to have nulls first and nulls last in
order_by
method - WIP on a SQL parser
- Add the possibility to convert from Array(JSON:Any)
- Fix misc typos
v0.7.2
v0.7.1
v0.7
v0.7
Features
- Add
Clear::Interval
type
This type is related to the type Clear::Interval
of PostgreSQL. It stores month
, days
and microseconds
and can be used
with Time
(Postgres' datetime
) by adding or substracting it.
Examples:
Usage in Expression engine:
interval = Clear::Interval.new(months: 1, days: 1)
MyModel.query.where{ created_at - interval > updated_at }.each do |model|
# ...
end
It might be used as column definition, and added / removed to crystal Time
object
class MyModel
include Clear::Model
column i : Clear::Interval
end
puts "Expected time: #{Time.now + MyModel.first!.i}"
- Add
Clear::TimeInDay
columns type, which stands for thetime
object in PostgreSQL.
Examples:
Usage as stand alone:
time = Clear::TimeInDay.parse("12:33")
puts time.hour # 12
puts time.minutes # 0
Time.now.at(time) # Today at 12:33:00
time.to_s # 12:33:00
time.to_s(false) # don't show seconds => 12:33
time = time + 2.minutes #12:35
As with Interval, you might wanna use it as a column (use underlying time
type in PostgreSQL):
class MyModel
include Clear::Model
column i : Clear::TimeInDay
end
Bug fixes
- Fix #115 (Thanks @pynixwang)
- Fix #118 (Thanks @russ)
- Fix #108
v0.6
v0.6
v0.6 should have shipped polymorphic relations, spec rework and improvement in
documentation. That's a lot of work (honestly the biggest improvement since v0)
and since already a lot of stuff have been integrated, I think it's better to
ship now and prepare it for the next release.
Since few weeks I'm using Clear in a full-time project, so I can see and correct
many bugs. Clear should now be more stable in term of compilation and should not
crash the compiler (which happened in some borderline cases).
Features
-
Release of a guide and documentation to use Clear: https://clear.gitbook.io/project/
json = JSON.parse(%({"first_name": "John", "last_name": "Doe", "tags": ["customer", "medical"] })) User.new(json)
-
Add of
pluck
andpluck_col
methods to retrieve one or multiple column in a Tuple,
which are super super fast and convenient! -
Add
Clear.with_cli
method to allow to use the CLI in your project. Check the documentation ! -
Additional comments in the source code
-
SelectQuery
now inherits fromEnumerable(Hash(String, Clear::SQL::Any))
-
Add optional block on
Enum
definition. This allow you to define custom methods for the enum:Clear.enum ClientType, "company", "non_profit", "personnal" do def pay_vat? self == Personnal end end
-
Add
?
support inraw
method:a = 1 b = 1000 c = 2 where{ raw("generate_series(?, ?, ?)", a, b, c) }
-
[EXPERIMENTAL] Add
<<
operation on collection which comes fromhas_many
andhas_many through:
-
[EXPERIMENTAL] Add
unlink
method on collection which comes fromhas_many through:
-
[EXPERIMENTAL] Add possibility to create model from JSON:
Breaking changes
- Migration: use of
table.column
instead oftable.${type}
(remove the method missing method); this was causing headache
in some case where the syntax wasn't exactly followed, as the error output from the compiler was unclear. - Renaming of
with_serial_pkey
toprimary_key
; refactoring of the macro-code allowing to add other type of keys. - Now allow
text
,int
andbigint
primary key, with still theuuid
,serial
andbigserial
primary keys available. - Renaming of
Clear::Model::InvalidModelError
toClear::Model::InvalidError
andClear::Model::ReadOnlyError
to
Clear::Model::ReadOnly
to simplify as those classes are already in theClear::Model
namespace Model#set
methods has been transfered toModel#reset
, andModel#set
now change the status of the column to dirty. (see #81)
Bug fixes
v0.5
v0.5: Merry christmas 🎄
Features
Connection pool
Clear wasn't fiber-proof since it lacks of connection pool system. It's now fixed, the connection pooling is done
completely transparently without any boilerplate on your application side.
Each fiber may require a specific connection; then the connection is binded to the fiber. In the case of transaction
and with_savepoint
, the connection is kept until the end of the block happens.
On the case of normal execution or cursors, we store the connection until the execution is done.
The connection pool is using Channel so in case of pool shortage, the fiber requesting the connection is put in
waiting state.
This is a huge step forward:
- Clear can be used in framework with
spawn
-based server and other event machine system. - I'll work on performance improvement or other subtilities in some specific cases, where multiple requests can be
parallelized over different connections.
Bug fixes
- Fix #53
- Update ameba to latest version
- Large refactoring on relations
- Many bugfixes
v0.4
Features
- #48 Add
lateral join
feature:Model.query.left_join("other_model", lateral: true){ model.id == other_model.model_id }
- #35 Add methods
import
over collection, to be able to insert multiple objects with one query:user_array = 10.times.map{ |x| User.new(name: "user#{x}") } Model.import(user_array)
- #42 Add support of
ON CONFLICT
both inInsert
andModel#save
u = User.new(id: 1, first_name: "me") u.save! &.on_conflict("(id)").do_update(&.set("first_name = excluded.first_name").where { model_users.id == excluded.id })
- Note: Method
Model#import
explained above can use the same syntax to resolve conflict.
This will helps to use Clear for import, CSV and batch processing.
- Note: Method
- #26 Add
to_json
supports to model. Please note that some core lib and shardspg
objects got
extended to allow this support:- By default, undefined fields are not exported. To export all columns even thoses which are not fetched in SQL, use
full: true
. For example:
User.query.first!.to_json # => {"id":1, "first_name":"Louis", "last_name": "XVI"} User.query.select("id, first_name").first!.to_json # => {"id":1, "first_name":"Louis"} User.query.select("id, first_name").first!.to_json(full: true) # => {"id":1, "first_name":"Louis", "last_name": null}
- By default, undefined fields are not exported. To export all columns even thoses which are not fetched in SQL, use
Bug fixes
- Escaping table, columns and schema name to allow Clear to works on any SQL restricted names.
- This is very demanding work as it turns out table and columns naming are used everywhere
in the ORM. Please give me feedback in case of any issues !
- This is very demanding work as it turns out table and columns naming are used everywhere
- Fix #31, #36, #38, #37
- Fix issue with polymorphic table
Breaking changes
- Renaming
insert
method onInsertQuery
tovalues
, making API more elegant. - Usage of
var
in Expression engine has been changed and is now different from raw:var
provide simple way to construct[schema].table.field
structure,
with escaped table, field and schema keywords.raw
works as usual, printing the raw string fragment to you condition.- Therefore:
TL;DR, if you currently use
where{ var("a.b") == 1 } # Wrong now! => WHERE "a.b" = 1 # Must be changed by: where{ var("a", "b") == 1 } # OR where{ raw("a.b") }
var
function, please useraw
instead from now.
- Revamping the converter system, allowing to work seemlessly with complexes types like Union and Generic
- Documentation will follow soon.
v0.3.1
v0.3
Features
- Add support to pg Enum
- Add support for UUID primary key, with uuid autogeneration
- Add support for BCrypt fields, like passwords
- Finalization of CLI !
- Add
Clear.seed(&block)
Clear.seed
goes in pair withbin/clear migrate seed
which will call the seed blocks. - Add possibility to use has_many through without having to declare the model doing the relation
For example, if A belongs to B, B belongs to C, then A has_many C through B. You can
perform this now without declaring any class for B; see the guide about relations for
more informations. - Add error messages so cool you want your code to crash 😉
Bug fixes
- Fix #23 bug with
has_many through:
and select - Add support for
DISTINCT ON
feature. - Array(String), Array(Int64) columns type are working now works
Breaking changes
Model#save
on read only model do not throw exception anymore but return false (save! still throw error)with_serial_pkey
use Int32 (type:serial
) and Int64 (type:longserial
) pkey instead of UInt32 and UInt64. This would prevent issue with defaultbelongs_to
behavior and simplify static number assignation.