Releases: anykeyh/clear
v0.10
v0.10
This is a maintenance release, which bring support for latest Crystal version (1.17.1)
Added feature
- Add GitHub Actions for running the test under CI/CD.
- Add ENV variables to control the database connection in specs.
- Fix mass assignment on
belongs_torelation (thanks @dukeraphaelng) - Fix issues with json serialization. (thanks @dukenguyenxyz).
- Fix issues with column types with newest version of
pgshard (thanks @lbguilherme and @mloughran)
Breaking changes
ensure_thanmacro is now deprecated, useensure_thatinstead. It was a typo all this time.- Some boolean properties getter have been renamed using
?at the end, to be more idiomatic and might break your code.
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::ConnectionPoolnow returns DB::Connection instead of DB::Database (fix #177) -
Clear::Migration::Directionis 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_operationhas been renamed toCollection#append_operation- Add
Clear::SQL.after_commitmethod
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)) }
endIn 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 }
endNote: 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_bymethod
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
VoidtoNil firstandlaston 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_wherefeature - 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_bymethod - 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::Intervaltype
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|
# ...
endIt 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::TimeInDaycolumns type, which stands for thetimeobject 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:35As 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
endBug 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
pluckandpluck_colmethods to retrieve one or multiple column in a Tuple,
which are super super fast and convenient! -
Add
Clear.with_climethod to allow to use the CLI in your project. Check the documentation ! -
Additional comments in the source code
-
SelectQuerynow inherits fromEnumerable(Hash(String, Clear::SQL::Any)) -
Add optional block on
Enumdefinition. 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 inrawmethod:a = 1 b = 1000 c = 2 where{ raw("generate_series(?, ?, ?)", a, b, c) }
-
[EXPERIMENTAL] Add
<<operation on collection which comes fromhas_manyandhas_many through: -
[EXPERIMENTAL] Add
unlinkmethod on collection which comes fromhas_many through: -
[EXPERIMENTAL] Add possibility to create model from JSON:
Breaking changes
- Migration: use of
table.columninstead 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_pkeytoprimary_key; refactoring of the macro-code allowing to add other type of keys. - Now allow
text,intandbigintprimary key, with still theuuid,serialandbigserialprimary keys available. - Renaming of
Clear::Model::InvalidModelErrortoClear::Model::InvalidErrorandClear::Model::ReadOnlyErrorto
Clear::Model::ReadOnlyto simplify as those classes are already in theClear::Modelnamespace Model#setmethods has been transfered toModel#reset, andModel#setnow 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 joinfeature:Model.query.left_join("other_model", lateral: true){ model.id == other_model.model_id }
- #35 Add methods
importover 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 CONFLICTboth inInsertandModel#saveu = 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#importexplained 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_jsonsupports to model. Please note that some core lib and shardspgobjects 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
insertmethod onInsertQuerytovalues, making API more elegant. - Usage of
varin Expression engine has been changed and is now different from raw:varprovide simple way to construct[schema].table.fieldstructure,
with escaped table, field and schema keywords.rawworks 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") }varfunction, please userawinstead from now.
- Revamping the converter system, allowing to work seemlessly with complexes types like Union and Generic
- Documentation will follow soon.