Jennifer.cr Versions Save

Crystal ORM using ActiveRecord pattern with flexible query DSL

v0.13.0

10 months ago

Release notes

General

  • address "positional parameter of the overridden method, which has a different name and may affect named argument passing" crystal 1.5.0 warning (for full list see MR)
  • upgrade supported mysql driver version to 0.14.0
  • upgrade supported pg driver version to 0.26.0
  • add jsonb type support for model and migration generators
  • replace phoffer/inflector.cr inflector library with luckyframework/wordsmith

QueryBuilder

  • Query.count returns Int64 instead of Int32
  • Query.offset accepts Int32 or Int64 as input instead of only Int32
  • #group, #select raise an ArgumentError if block returns anything except array
  • #having, #reorder, #order raises ExpressionBuilder as a block argument
  • #where accepts hash with string keys as well
  • added #find_or_create_by, #find_or_create_by!, #find_or_initialize_by to create/instantiate a record if nothing was found in the DB
  • added #find_by and #find_by! as a shortcut for .where().first and .where().first!

Model

  • change default id attribute type from Int32 to Int64
  • generate a setter method for a Int64 attribute that calls #to_i64 on Int32 value
  • in mapping automatically assign null: true if null property is missing and field is primary
  • enhance type coercion on initializing instance from hash
  • change return type of .ids to Array(Int64)
  • Model.count returns Int64 instead of Int32
  • add support for JSON::PullParser attribute class
  • model class responds to: #here, #select, #from, #union, #distinct, #order, #group, #with, #merge, #limit, #offset, #lock, #reorder, #count, #max, #min, #sum, #avg, #group_count, #group_max, #group_min, #group_sum, #group_avg, #with_relation, #includes, #preload, #eager_load, #last, #last!, #first, #first!, #find_by, #find_by!, #pluck, #exists?, #increment, #decrement, #ids, #find_records_by_sql, #find_in_batches, #find_each, #join, #right_join, #left_join, #lateral_join
  • adds .create and .create! that accepts block
  • fix a bug when password digest wasn't generated when password was set by passing it to a constructor
  • add missing STI initialization to constructors accepting a hash or named tuple
  • use Adapter::Base.coerce_database_value in constructors accepting a hash or named tuple to coerce values for columns that don't have specified converter
  • Fix the issue of Jennifer::Model::OptimisticLocking#reset_lock_version decreasing lock version column when it's not changed

Validation

  • add jennifer.errors.messages.required message in locale yaml file
  • Jennifer::Model::Errors#add accepts String | Symbol | Proc(Translation, String, String) for message argument
  • validates_inclusion, validates_exclusion, validates_format, validates_length, validates_uniqueness, validates_presence, validates_absence, validates_numericality, validates_acceptance & validates_confirmation validation macros accepts message option that allows to specify validation message

Relation

  • belongs_to relation macro accepts required option to validate existence of relation on save

View

  • model class responds to: #here, #select, #from, #union, #distinct, #order, #group, #with, #merge, #limit, #offset, #lock, #reorder, #count, #max, #min, #sum, #avg, #group_count, #group_max, #group_min, #group_sum, #group_avg, #with_relation, #includes, #preload, #eager_load, #last, #last!, #first, #first!, #find_by, #find_by!, #pluck, #exists?, #increment, #decrement, #ids, #find_records_by_sql, #find_in_batches, #find_each, #join, #right_join, #left_join, #lateral_join

Adapter

  • remove id column from the versions table schema
  • change default id column type from int to bigint
  • add Base.coerce_database_value method to provide interface to perform coercing from default database type used to read column to hash to a desired model attribute type

SqlGenerator

  • fix invalid SQL generating for FROM clause when string value is given for #from

Record

  • now calling a #foo_bar method on a record that is missing raises ArgumentError instead of KeyError

v0.12.0

2 years ago

In memory of my father - even with very low understanding in programming, he was always interested in the current state of Jennifer and whether it was useful to people

Changelog

General

  • add crystal 1.2.0 support

QueryBuilder

  • #pluck accepts splatted named tuple of desired attribute-type pairs and returns array of such tuples as a records
  • #upsert passes expression builder as a block argument

Model

  • add Optimistic locking support via macro with_optimistic_lock(column_name = lock_version)
  • updated_at and created_at fields aren't override on save if have been set manually
  • add upsert class method to insert multiple models while ignoring conflicts on specified unique fields

Adapter

  • fix database connection query arguments building
  • each adapter includes RequestMethods instead of including it by base adapter class
  • add a new upsert overload that allows passing a collection of Jennifer::Model::Base
  • fix insert_on_duplicates sql generation for postgres adapter if no unique fields given

SqlGenerator

  • any INSERT, UPDATE, SELECT and DELETE requests uses quoted tables/columns if they are created by QueryBuilder::ExpressionBuilder (raw SQL is placed as-is)
  • .select_clause uses specified query attributes to select and fall back to custom fields only if there is no custom attribute to select

Migration

  • TableBuilder::ChangeTable now performs DropForeignKey, DropIndex & DropReference before any column manipulation
  • fix TableBuilder::ChangeEnum to use specified adapter to query effected tables

v0.11.1

2 years ago

Changelog

General

  • switch to the crimson-knight/i18n.cr ~> 0.4.1

Migration

  • fix TableBuilder::ChangeTable#drop_reference dropping column before reference
  • add TableBuilder::DropReference

v0.11.0

2 years ago

I'm happy to announce that the release supporting crystal >= 1.0.0 is published. Summarizing changelog below I'd like to emphasize next points:

  • now models/views/record have #to_json method to simplify serialization
  • Jennifer supports latest mysql/postgresl/sqlite driver versions
  • now string-based values can be used much easily with new coercing logic
  • time zone related logic become more flexible and configurable
  • now each pull request merge and release automatically rebuilds documentation so you always can check fresh version here
  • currently i18n shard has a bit unstable support so may cause some issues (feel free to open issue if you face one)

Changelog

General

  • add crystal >= 1.0.0 support
  • fix inconsistent method signatures in multiple places
  • add #to_json to the following structs: PG::Numeric, PG::Geo::Point, PG::Geo::Line, PG::Geo::Circle, PG::Geo::LineSegment, PG::Geo::Box, PG::Geo::Path, PG::Geo::Polygon, Char, Time::Span, Slice, UUID
  • add crystal-mysql: 0.13.0 support
  • add crystal-pg: 0.23.2 support

QueryBuilder

  • add custom #to_json to serialize retrieved collection
  • add #where accepting Hash(Symbol, _)
  • add Criteria#equal and Criteria#not_equal as original implementation of Criteria#== and Criteria#!=
  • add ExpressionBuilder #and, #or and #xor methods that accepts array of conditions

Model

  • Authentication#password returns given unecrypted value
  • add Coercer module with static methods to localize all coercing logic for different types
  • add .coercer method to return object responding to all coercing methods described in Coercer
  • mapping generates .coerce_{{attribute}(value : String) methods for every field to coerce string value to attribute's type
  • mapping generates for every non-string attribute with a setter additional #{{attribute}}=(value : String) setter
  • allow all build methods (.new, .create and .update) to receive Hash(String, String) and coerce values to expected types
  • add .column_name to return all field names
  • fix .field_names from returning child's properties for parent class
  • add custom #to_json
  • change converter interface to .from_db(DB::ResultSet, NamedTuple), .from_db(DB::ResultSet, NamedTuple) and .from_hash(Hash, String | Symbol, NamedTuple)
  • change all existing converters to support new required interface
  • add BigDecimalConverter(T) converter
  • add Coercer.coerce(String, (BigDecimal?).class)
  • add time_zone_aware option for TimeZoneConverter to specify whether field should respect time zone converting logic
  • add support of date only and time only string formats for TimeZoneConverter
  • add time_format, date_time_format and date_format to customize time, date time and date formats respectively
  • fix a bug where updated_at is not set in the generated sql query from model.save
  • NumericToFloat64Converter, BigDecimalConverter, TimeZoneConverter #from_hash accepts string as field value
  • BigDecimalConverter#from_hash accepts integer and float values as field value
  • fix Errors#inspect bug using old UInt64#to_s signature
  • introduce Timestamp module that now includes with_timestamps macro
  • reworked how updated_at and created_at fields are set before save - now they are set explicitly without utilizing callbacks
  • add Resource.where accepting Hash(Symbol, _)

Validation

  • change Validator#validate abstract interface to #validate(record, **opts)
  • update all built-in validators to reflect new Validator interface
  • make Validator.with_blank_validation macro to accept arguments to reference record, field name, value and blank value acceptance

Relation

  • remove abstract #condition_clause & #condition_clause(a) declarations from IRelation

View

  • add custom #to_json

Adapter

  • remove abstract #update declaration from Base
  • BaseSQLGenerator#parse_query converts Time arguments to UTC only if Config.time_zone_aware_attributes set to true
  • Mysql#read_column calls super if column isn't a tiny int
  • ResultParser#read_column convert time to Config.local_time_zone if Config.time_zone_aware_attributes set to true or just change time zone to it otherwise

Config

  • .reset_config creates new instance instead of executing #initialize on existing object
  • add Config.time_zone_aware_attributes to specify whether time zone converting logic should be globally disabled

Migration

  • add precision and scale options support for decimal data type

Record

  • add custom #to_json
  • add custom #inspect

v0.10.0

3 years ago

Changes

General

  • add crystal 0.35.0 support and drop 0.34.0 support

QueryBuilder

  • allow arbitrary Query instances as nested queries for CTEs
  • fix failed nil assertion when eager load relations sequence with missing intermediate relation records
  • add IModelQuery#find(id) to retrieve record by primary key
  • add IModelQuery#find!(id) to retrieve record by primary key or raise Jennifer::RecordNotFound exception
  • ModelQuery(T)#to_a and ModelQuery(T)#find_by_sql ensure T has loaded actual table field count before making a request

Model

  • change model constructor hash argument type from Hash(String, Jennifer::DBany) to Hash(String, AttrType) (same for Symbol keys)
  • CommonMapping#attribute uses attribute getter
  • CommonMapping#attribute raises Jennifer::UnknownAttribute exception if model has no requested attribute and raise_exception = true
  • add CommonMapping#attribute_before_typecast which returns given attribute in database format using attribute converter
  • add Mapping#{{attribute}}_will_change! to mark {{attribute}} as changed one
  • #primary uses getter
  • any Mapping.mapping invocation creates AttrType alias to represent union of Jennifer::DBAny and any arbitrary type from fields definition
  • change Mapping#update_columns argument type to Hash(String | Symbol, AttrType)
  • Mapping#update_columns raises Jennifer::UnknownAttribute if key-value pairs include unknown attribute
  • Mapping#set_attribute to accept AttrType
  • Mapping#set_attribute raises Jennifer::UnknownAttribute exception if model has no requested attribute
  • Mapping#update_columns raises Jennifer::UnknownAttribute if key-value pairs include unknown field
  • Mapping#arguments_to_insert and Mapping#arguments_to_save use #attribute_before_typecast to collect attributes to store in a database
  • #add_{{relation}} accepts AttrType as a hash value type
  • rename EnumConverter to PgEnumConverter
  • add EnumConverter(T) to convert string to crystal enum
  • add JSONSerializableConverter(T) to convert JSON field to objects of T that support .from_json and #to_json methods
  • add TimeZoneConverter to convert time attributes from UTC to Jennife::Config.local_time_zone

Adapter

  • add DBFormater as a proposed default logger formatter
  • change logging - emit Log::Metadata with query, args and time keys (as new crystal-db does)
  • fix missing reconnect to database on connection lost

SqlGenerator

  • fix a potential compilation issue that appears in certain edge cases for postgres adapter (#329) Adapter::BaseSQLGenerator now produces valid SQL when using multiple recursive CTEs (.with(..., true)) in a single query

Migration

  • rename TableBuilder::DB_OPTIONS to TableBuilder::DbOptions

Exceptions

  • add Jennifer::UnknownAttribute to represent case when unknown attribute is tried to be read/written

v0.9.0

3 years ago

Changelog

General

  • add Crystal 0.34.0 support
  • add Jennifer::Presentable with abstract methods declarations (#attribute, #errors, #human_attribute_name, #attribute_metadata, #class_name)

QueryBuilder

  • Query#initialize now accept Adapter::Base as a second (optional) argument
  • OrderItem is renamed to OrderExpression to avoid possible name collisions

Model

  • fix an issue with rendering new_record and destroyed system variables by #to_json
  • Resource.table_prefix now returns underscored namespace name (if any) by default
  • Base includes Jennifer::Presentable
  • add Translation#class_name method to return underscored class name
  • add Mapping#attribute_metadata to return attribute metadata by it's name
  • remove Base::primary_field_type
  • Prevent compile time error with models named Model or Record

View

  • fix an issue with rendering new_record and destroyed system variables by #to_json
  • remove Base::primary_field_type

Adapter

  • db connection is established on the first request no on adapter initialization
  • Adapter.adapter_class raises BaseException if no valid Config.adapter is specified
  • .command_interface, .create_database, .drop_database, .generate_schema, .load_schema, .db_connection, .connection_string, .database_exists? now are instance methods
  • Base#initialize now excepts Config instance
  • respect host in Jennifer::Postgres::CommandInterface#database_exists?
  • escape connection URI segments
  • Config#logger now is Log instead of Logger
  • add read/write adapter segregation
  • deprecate .adapter & .adapter_class
  • remove .query, .exec & .scalar

Config

  • .reset_config invokes #initialize instead of creating new instance

Migration

  • Base#schema_processor is no more public api
  • Runner.create and Runner.drop now accept option Adapter::Base instance
  • pass to_table in TableBuilder::DropForeignKey#process
  • fix TableBuilder::CreateTable#reference - now it takes into account given SQL type for the foreign key column
  • add #add_reference, #drop_reference, #add_timestamps to TableBuilder::CHangeTable
  • TableBuilder::CHangeTable#drop_index also accepts single column name
  • remove deprecated TableBuilder::CreateTable#index overrides

Record

  • #initialize(DB::ResultSet) is removed

v0.8.4

4 years ago

Changes

QueryBuilder

  • use adapter's #read_column in NestedRelationTree
  • add RelationTree#adapter
  • fix Ordering#order(Hash(String | Symbol, String | Symbol))

Adapter

  • fix issue with treating tinyint mysql field as boolean
  • remove ResultParser#result_to_array
  • add Mysql#read_column
  • add Base.default_max_bind_vars_count which returns default maximum count of bind variables that can be used in Base#bulk_insert (default is 32766)
  • Mysql.default_max_bind_vars_count and Postgres.default_max_bind_vars_count returns 32766
  • Base#bulk_insert doesn't do table lock no more
  • if variables that should be inserted by Base#bulk_insert exceed Base.max_bind_vars all of them are quoted and put into a query
  • remove BaseSQLGenerator::ARRAY_ESCAPE
  • move BaseSQLGenerator::ARGUMENT_ESCAPE_STRING to Quoting
  • move BaseSQLGenerator .quote, .escape_string, .filter_out to Quoting
  • add correct values quoting for postgres adapter
  • add correct values quoting for mysql adapter
  • now logger writes BEGIN instead of TRANSACTION START, COMMIT instead of TRANSACTION COMMIT and ROLLBACK instead of TRANSACTION ROLLBACK on corresponding transaction commands
  • add SchemaProcessor::FkEventActions enum to validate on_delete and on_update action values; String | Symbol still should be used as an argument type everywhere

Config

  • add max_bind_vars_count property to present maximum allowed count of bind variables to be used in bulk insert operation
  • add MigrationFailureHandler enum to validate migration_failure_handler_method value; Symbol | MigrationFailureHandler should be used as an argument for it
  • fix migration_failure_handler_method config - make it instance property

v0.8.3

4 years ago

Changes

General

  • add crystal 0.31.1 compatibility
  • add [email protected] support
  • remove sam from mandatory dependencies

Model

  • fix bug with primary field presence assertion

View

  • fix bug with primary field presence assertion

Adapter

  • add date SQL data type
  • date_time field type maps to timestamp SQL data type (postgres only)

Migration

  • add Runner.pending_migration? to return whether there is pending (not invoked) migration
  • add Base.with_transaction method to disable automatic transaction wrapping around migration methods
  • add Base.with_transaction? to check whether migration is run under a transaction
  • remove var_string field type
  • remove blob field type for postgres
  • fix wrong explanation message for TableBuilder::CreateIndex
  • add new TableBuilder::CreateTable#index signatures (old ones are deprecated):
    • #index(fields : Array(Symbol), type : Symbol | ::Nil = nil, name : String | ::Nil = nil, lengths : Hash(Symbol, Int32) = {} of Symbol => Int32, orders : Hash(Symbol, Symbol) = {} of Symbol => Symbol)
    • #index(field : Symbol, type : Symbol | ::Nil = nil, name : String | ::Nil = nil, length : Int32 | ::Nil = nil, order : Symbol | ::Nil = nil)
  • make default varchar length 254 (mysql only)
  • add foreign key ON UPDATE and ON DELETE support
  • Base#add_foreign_key accepts on_delete and on_update keyword arguments to specify corresponding actions
  • TableBuilder::ChangeTable#add_foreign_key accepts on_delete and on_update keyword arguments to specify corresponding actions
  • TableBuilder::CreateTable#reference accepts on_delete and on_update options to specify corresponding actions
  • TableBuilder::CreateTable#foreign_key accepts on_delete and on_update keyword arguments to specify corresponding actions

v0.8.2

4 years ago

Tiny release to update dependencies

v0.8.1

4 years ago