Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Jennifer::Config.from_uri(db)
| `retry_attempts` | 1 |
| `checkout_timeout` | 5.0 |
| `retry_delay` | 1.0 |
| `local_time_zone_name` | default time zone name for `TimeZone` |
| `local_time_zone_name` | default time zone name |
| `skip_dumping_schema_sql` | `false` |
| `command_shell` | `"bash"` |
| `docker_container` | `""` |
Expand Down
1 change: 1 addition & 0 deletions docs/time.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Time

Any model or view `Time` attribute will be automatically converted from local time zone (which could be set using `Jennifer::Config.local_time_zone_name=`) to UTC and converted back during reading from the DB. Also during querying the db all `Time` arguments will be converted same way as well. Only `Jennifer::Record` time attributes is not automatically converted from UTC to local time during loading from the result set.

Local time could be set using:
Expand Down
16 changes: 6 additions & 10 deletions shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,20 @@ version: 0.5.1
authors:
- Roman Kalnytskyi <moranibaca@gmail.com>

crystal: 0.24.1
crystal: 0.25.0

license: MIT

development_dependencies:
mysql:
github: crystal-lang/crystal-mysql
version: "~> 0.4"
version: "~> 0.5"
pg:
github: will/crystal-pg
version: "~> 0.14.1"
sqlite3:
github: crystal-lang/crystal-sqlite3
version: "~> 0.15.0"
factory:
github: imdrasil/factory
branch: master
version: "~> 0.1.3"
dependencies:
sam:
github: imdrasil/sam.cr
Expand All @@ -30,9 +28,7 @@ dependencies:
ifrit:
github: imdrasil/ifrit
version: "~> 0.1.2"
time_zone:
github: imdrasil/time_zone
version: "~> 0.1"
i18n:
github: TechMagister/i18n.cr
commit: "fc96c6b12547c84da2e76495f9c970acda64976b"
version: "~> 0.2.0"
# commit: "fc96c6b12547c84da2e76495f9c970acda64976b"
2 changes: 1 addition & 1 deletion spec/adapter/base_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe Jennifer::Adapter::Base do
describe "#transaction" do
it "rollbacks if exception was raised" do
void_transaction do
expect_raises(DivisionByZero) do
expect_raises(DivisionByZeroError) do
adapter.transaction do |tx|
Factory.create_contact
1 / 0
Expand Down
4 changes: 2 additions & 2 deletions spec/adapter/sql_generator_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,8 @@ describe "Jennifer::Adapter::SQLGenerator" do
context "with given Time object" do
it do
with_time_zone("Etc/GMT+1") do
time = Time.utc_now
adapter.parse_query("%s", [time] of Jennifer::DBAny)[1][0].as(Time).should be_close(time + 1.hour, 1.second)
adapter.parse_query("%s", [Time.now(local_time_zone)] of Jennifer::DBAny)[1][0].as(Time)
.should be_close(Time.utc_now, 1.second)
end
end
end
Expand Down
9 changes: 4 additions & 5 deletions spec/model/authentication_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ require "../spec_helper"
describe Jennifer::Model::Authentication do
describe "%with_authentication" do
context "with default field names" do
default_user = Factory.build_user

describe "validations" do
it do
user = Factory.build_user
user.password = "1" * 52
user.should validate(:password).with("is too long (maximum is 51 characters)")
user.password = "1" * 72
user.should validate(:password).with("is too long (maximum is 71 characters)")
end

it { Factory.build_user([:with_invalid_password_confirmation]).should validate(:password).with("doesn't match Password") }
Expand All @@ -21,6 +19,7 @@ describe Jennifer::Model::Authentication do
user.password_digest = Crypto::Bcrypt::Password.create("password").to_s
user.should be_valid
end

it do
Factory.create_user([:with_password_digest])
user = User.all.last!
Expand All @@ -47,7 +46,7 @@ describe Jennifer::Model::Authentication do

it do
user = Factory.build_user
user.password = "1" * 53
user.password = "1" * 72
user.password_digest.should eq("")
end

Expand Down
6 changes: 3 additions & 3 deletions spec/model/base_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ describe Jennifer::Model::Base do
describe "#with_lock" do
it "starts transaction" do
void_transaction do
expect_raises(DivisionByZero) do
expect_raises(DivisionByZeroError) do
Factory.create_contact.with_lock do
Factory.create_contact
Contact.all.count.should eq(2)
Expand All @@ -452,7 +452,7 @@ describe Jennifer::Model::Base do
describe "::transaction" do
it "allow to start transaction" do
void_transaction do
expect_raises(DivisionByZero) do
expect_raises(DivisionByZeroError) do
Contact.transaction do
Factory.create_contact
1 / 0
Expand Down Expand Up @@ -511,7 +511,7 @@ describe Jennifer::Model::Base do
describe "::models" do
it "returns all model classes" do
models = Jennifer::Model::Base.models
models.is_a?(Array(Jennifer::Model::Base.class)).should be_true
models.is_a?(Array).should be_true
# I tired from modifying this each time new model is added
(models.size > 6).should be_true
end
Expand Down
9 changes: 5 additions & 4 deletions spec/model/mapping_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -327,19 +327,20 @@ describe Jennifer::Model::Mapping do

describe Time do
it "stores to db time converted to UTC" do
contact = Factory.create_contact
new_time = Time.now(local_time_zone)
with_time_zone("Etc/GMT+1") do
contact = Factory.create_contact
Contact.all.update(created_at: Time.utc_now)
Contact.all.update(created_at: new_time)
Contact.all.select { [_created_at] }.each_result_set do |rs|
rs.read(Time).should be_close(Time.utc_now + 1.hour, 2.seconds)
rs.read(Time).should be_close(new_time, 1.second)
end
end
end

it "converts values from utc to local" do
contact = Factory.create_contact
with_time_zone("Etc/GMT+1") do
contact.reload.created_at!.should be_close(Time.utc_now - 1.hour, 2.seconds)
contact.reload.created_at!.should be_close(Time.now(local_time_zone), 1.second)
end
end
end
Expand Down
18 changes: 18 additions & 0 deletions spec/model/parameter_converter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,30 @@ describe Jennifer::Model::ParameterConverter do
it { converter.to_numeric("-1").to_s.should eq("-1") }
it { converter.to_numeric("1.12345").to_s.should eq("1.12345") }
it { converter.to_numeric("-1.12345").to_s.should eq("-1.12345") }

# NOTE: some cases from the will/crystal-pg

it { converter.to_numeric("0").to_s.should eq("0") }
it { converter.to_numeric("0.0").to_s.should eq("0.0") }
it { converter.to_numeric("1.30").to_s.should eq("1.30") }
it { converter.to_numeric("-0.00009").to_s.should eq("-0.00009") }
it { converter.to_numeric("-0.00000009").to_s.should eq("-0.00000009") }
it { converter.to_numeric("50093").to_s.should eq("50093") }
it { converter.to_numeric("500000093").to_s.should eq("500000093") }
it { converter.to_numeric("0.0000006000000").to_s.should eq("0.0000006000000") }
it { converter.to_numeric("0.3").to_s.should eq("0.3") }
it { converter.to_numeric("0.03").to_s.should eq("0.03") }
it { converter.to_numeric("0.003").to_s.should eq("0.003") }
it { converter.to_numeric("0.000300003").to_s.should eq("0.000300003") }
end
end

describe "#to_time" do
it { converter.to_time("2010-10-10").should eq(Time.new(2010, 10, 10)) }
it { converter.to_time("2010-10-10 20:10:10").should eq(Time.new(2010, 10, 10, 20, 10, 10)) }
it "ignores given time zone" do
converter.to_time("2010-10-10 20:10:10 +01:00").should eq(Time.new(2010, 10, 10, 20, 10, 10, location: local_time_zone))
end
end

describe "#to_b" do
Expand Down
74 changes: 1 addition & 73 deletions spec/models.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require "./views"
require "../src/jennifer/model/authentication"

struct JohnyQuery < Jennifer::QueryBuilder::QueryObject
Expand Down Expand Up @@ -431,76 +432,3 @@ class ContactWithFloatMapping < Jennifer::Model::Base
mapping({id: Primary32}, false)
{% end %}
end

# ===========
# views
# ===========

class FemaleContact < Jennifer::View::Materialized
mapping({
id: Primary32,
name: String?,
}, false)
end

class MaleContact < Jennifer::View::Base
mapping({
id: Primary32,
name: String,
gender: String,
age: Int32,
created_at: Time?,
}, false)

scope :main { where { _age < 50 } }
scope :older { |age| where { _age >= age } }
scope :johny, JohnyQuery
end

# ==================
# synthetic views
# ==================

class FakeFemaleContact < Jennifer::View::Base
view_name "female_contacs"

mapping({
id: Primary32,
name: String,
gender: String,
age: Int32,
created_at: Time?,
}, false)
end

class FakeContactView < Jennifer::View::Base
view_name "male_contacs"

mapping({
id: Primary32,
}, false)
end

class StrinctBrokenMaleContact < Jennifer::View::Base
view_name "male_contacts"
mapping({
id: Primary32,
name: String,
})
end

class StrictMaleContactWithExtraField < Jennifer::View::Base
view_name "male_contacts"
mapping({
id: Primary64,
missing_field: String,
})
end

class MaleContactWithDescription < Jennifer::View::Base
view_name "male_contacts"
mapping({
id: Primary32,
description: String,
}, false)
end
4 changes: 4 additions & 0 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ def read_to_end(rs)
end
end

def local_time_zone
Jennifer::Config.local_time_zone
end

def with_time_zone(zone_name : String)
old_zone = Jennifer::Config.local_time_zone_name
begin
Expand Down
4 changes: 2 additions & 2 deletions spec/view/base_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe Jennifer::View::Base do
Factory.create_contact(gender: "male")
view = MaleContact.all.first!
view.inspect.should eq("#<MaleContact:0x#{view.object_id.to_s(16)} id: #{view.id}, name: \"Deepthi\", "\
"gender: \"male\", age: 28, created_at: #{view.created_at.to_s}>")
"gender: \"male\", age: 28, created_at: #{view.created_at.inspect}>")
}
end

Expand Down Expand Up @@ -140,7 +140,7 @@ describe Jennifer::View::Base do
describe "::views" do
it "returns all model classes" do
views = Jennifer::View::Base.views
views.is_a?(Array(Jennifer::View::Base.class)).should be_true
views.is_a?(Array).should be_true
# I tired from modifing this each time new view is added
(views.size > 0).should be_true
end
Expand Down
10 changes: 6 additions & 4 deletions spec/view/experimental_mapping_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,21 @@ describe Jennifer::View::ExperimentalMapping do

describe Time do
it "stores to db time converted to UTC" do
contact = Factory.create_contact
new_time = Time.now(local_time_zone)

with_time_zone("Etc/GMT+1") do
contact = Factory.create_contact
Contact.all.update(created_at: Time.utc_now)
Contact.all.update(created_at: new_time)
MaleContact.all.select { [_created_at] }.each_result_set do |rs|
rs.read(Time).should be_close(Time.utc_now + 1.hour, 2.seconds)
rs.read(Time).should be_close(new_time, 1.second)
end
end
end

it "converts values from utc to local" do
contact = Factory.create_contact
with_time_zone("Etc/GMT+1") do
MaleContact.all.first!.created_at!.should be_close(Time.utc_now - 1.hour, 2.seconds)
MaleContact.all.first!.created_at!.should be_close(Time.now(local_time_zone), 2.seconds)
end
end
end
Expand Down
68 changes: 68 additions & 0 deletions spec/views.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
class FemaleContact < Jennifer::View::Materialized
mapping({
id: Primary32,
name: String?,
}, false)
end

class MaleContact < Jennifer::View::Base
mapping({
id: Primary32,
name: String,
gender: String,
age: Int32,
created_at: Time?,
}, false)

scope :main { where { _age < 50 } }
scope :older { |age| where { _age >= age } }
scope :johny, JohnyQuery
end

# ==================
# synthetic views
# ==================

class FakeFemaleContact < Jennifer::View::Base
view_name "female_contacs"

mapping({
id: Primary32,
name: String,
gender: String,
age: Int32,
created_at: Time?,
}, false)
end

class FakeContactView < Jennifer::View::Base
view_name "male_contacs"

mapping({
id: Primary32,
}, false)
end

class StrinctBrokenMaleContact < Jennifer::View::Base
view_name "male_contacts"
mapping({
id: Primary32,
name: String,
})
end

class StrictMaleContactWithExtraField < Jennifer::View::Base
view_name "male_contacts"
mapping({
id: Primary64,
missing_field: String,
})
end

class MaleContactWithDescription < Jennifer::View::Base
view_name "male_contacts"
mapping({
id: Primary32,
description: String,
}, false)
end
Loading