jade-sql 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06d76c59dbdec2e461a437690981df433898e1366503d665d2b10778fa504d48
4
- data.tar.gz: 2c608f51a766507d5f9f13df7acce793177d6241e742edf05968eacff8482525
3
+ metadata.gz: 203373e2a0789908e800cda5e81acee5883edb436205ec016f281549451218cd
4
+ data.tar.gz: 3338e7d2e8132fdf4ed4de0e8b53658d8a2cce64ee8f13394cc7dca53413906c
5
5
  SHA512:
6
- metadata.gz: f87fac54b24c1a530fa20ee750168654ec6c7a20d60dc5f09afb556862a132ae71e3a8c5e1683e041ccd749ca6fd51db3615a04ac6bfc631c03e61fc533fba68
7
- data.tar.gz: 8e6fe0954616823a3cf8b44da971562a4cf8fbefa50cad20d66604e77c8c016a6b0c12a5c8e3bbfb5e5c72afe10961db4a4b996bf1b08d22ae4a4b4682068faf
6
+ metadata.gz: f5e68d85a8d74169946d1368aa91ef46fe99c05a6b3250a5f11b44cc0ee13f75c66b3e1a3bbe59febb9c63ebee3f32ea419396444dffb699fa239a211f943821
7
+ data.tar.gz: 4ad41bf4b0b780a78b599bba48bd90f7b14a35dfd2564a38b8153bbeec8dd8a7abd77a08d6c43db9888df49096429702aa31d2a6ceebd606446023d729988dd7
data/docs/building.md CHANGED
@@ -28,22 +28,18 @@ bundle exec rake jade:schema \
28
28
  ```
29
29
 
30
30
  Type map: `bigint`/`integer`/`smallint` → `Int`, `numeric`/`decimal` →
31
- `Decimal` (from `Sql.Decimal`), `double precision`/`real` → `Float`,
32
- `varchar`/`text`/`char` → `String`, `boolean` → `Bool`, `jsonb`/`json` →
33
- `Decode.Value`, `date` → `Calendar.Date`, `timestamp` → `Clock.Instant`,
34
- `uuid` → `Uuid` (from `Sql.Uuid`). Unknown types fail loudly with the
35
- table+column name.
36
-
37
- `numeric`/`decimal` map to `Sql.Decimal` — an exact base-10 value
38
- (`mantissa * 10^exponent`), never `Float`, so no precision is lost. Genuine
39
- floating-point columns (`double precision`/`real`) map to `Float`. `bytea`
40
- isn't mapped yet, though jade's `Bytes` is the natural target.
41
-
42
- `Sql.Decimal` implements `Numeric`, so `+`/`-`/`*` work and stay exact.
43
- Mirroring `BigDecimal`, `/` can't represent a repeating quotient, so it
44
- rounds half-up to a default scale (use `div(a, b, scale)` for explicit
45
- control, `round(d, scale)` to round); division by zero raises. `to_i`
46
- truncates toward zero and `to_float` converts (lossily, on purpose).
31
+ `Decimal` (jade's stdlib exact decimal), `double precision`/`real` →
32
+ `Float`, `varchar`/`text`/`char` → `String`, `boolean` → `Bool`,
33
+ `jsonb`/`json` → `Decode.Value`, `date` → `Calendar.Date`, `timestamp` →
34
+ `Clock.Instant`, `uuid` → `Uuid` (from `Sql.Uuid`). Unknown types fail
35
+ loudly with the table+column name.
36
+
37
+ `numeric`/`decimal` map to the stdlib `Decimal` — an exact base-10 value
38
+ (`coefficient * 10^exponent`), never `Float`, so no precision is lost.
39
+ Genuine floating-point columns (`double precision`/`real`) map to `Float`.
40
+ `bytea` isn't mapped yet, though jade's `Bytes` is the natural target. See
41
+ jade-lang's `Decimal` for the full API (`of`/`scaled`/`parse`, arithmetic,
42
+ `round`, `to_i`/`to_float`).
47
43
 
48
44
  For each table, the generator emits:
49
45
 
@@ -53,7 +53,7 @@ module JadeSql
53
53
  "Clock.Instant" => "import Clock",
54
54
  "Decode.Value" => "import Decode",
55
55
  "Uuid" => "import Sql.Uuid exposing(Uuid)",
56
- "Decimal" => "import Sql.Decimal exposing(Decimal)",
56
+ "Decimal" => "import Decimal exposing(Decimal)",
57
57
  }.freeze
58
58
 
59
59
  # Column names that collide with Jade keywords get a trailing underscore
@@ -82,8 +82,8 @@ module JadeSql
82
82
  # other List(a) column.
83
83
  #
84
84
  # numeric/decimal columns come back as ::BigDecimal; the schema generator
85
- # maps them to Sql.Decimal, whose decoder reads the exact
86
- # "<mantissa>e<exponent>" wire form. Float would lose precision, so don't.
85
+ # maps them to jade's stdlib Decimal, whose decoder reads the exact
86
+ # "<coefficient>e<exponent>" wire form. Float would lose precision, so don't.
87
87
  def self.coerce_row(row)
88
88
  row.transform_values { |v| coerce_value(v) }
89
89
  end
@@ -99,19 +99,19 @@ module JadeSql
99
99
  end
100
100
  end
101
101
 
102
- # ::BigDecimal -> "<mantissa>e<exponent>" with value = mantissa * 10^exp,
102
+ # ::BigDecimal -> "<coefficient>e<exponent>" with value = coeff * 10^exp,
103
103
  # exactly (BigDecimal#split gives sign, significant digits, and a base-10
104
- # exponent). Matches the wire form Sql.Decimal's decoder parses.
104
+ # exponent). Matches the wire form jade's stdlib Decimal decoder parses.
105
105
  #
106
- # 'NaN'/'Infinity'::numeric are legal Postgres values that Sql.Decimal
107
- # can't represent; fail loudly rather than silently decode them as 0.
106
+ # 'NaN'/'Infinity'::numeric are legal Postgres values that Decimal can't
107
+ # represent; fail loudly rather than silently decode them as 0.
108
108
  def self.decimal_wire(v)
109
109
  raise ArgumentError, "non-finite numeric: #{v}" unless v.finite?
110
110
 
111
111
  sign, digits, _base, exp = v.split
112
- mantissa = sign * digits.to_i
112
+ coefficient = sign * digits.to_i
113
113
  exponent = exp - digits.length
114
- "#{mantissa}e#{exponent}"
114
+ "#{coefficient}e#{exponent}"
115
115
  end
116
116
 
117
117
  # PG arrays render as `{}`, `{a,b,c}`, `{"a,b","c"}`, with NULL as
@@ -1,3 +1,3 @@
1
1
  module JadeSql
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jade-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - agustin
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 0.1.0
18
+ version: 0.2.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 0.1.0
25
+ version: 0.2.0
26
26
  description: Query and mutation builders, schema generation from db/structure.sql,
27
27
  and an ActiveRecord-backed runtime for the Jade language. Renders typed queries
28
28
  to (String, List(Value)) and decodes rows into Jade structs.
@@ -42,7 +42,6 @@ files:
42
42
  - lib/jade-sql/bin/generate_schema.rb
43
43
  - lib/jade-sql/runtime.rb
44
44
  - lib/jade-sql/sql.jd
45
- - lib/jade-sql/sql/decimal.jd
46
45
  - lib/jade-sql/sql/loader.jd
47
46
  - lib/jade-sql/sql/mutation.jd
48
47
  - lib/jade-sql/sql/query.jd
@@ -1,193 +0,0 @@
1
- module Sql.Decimal exposing (
2
- Decimal,
3
- decimal,
4
- div,
5
- exponent,
6
- mantissa,
7
- parse,
8
- round,
9
- to_float,
10
- to_i,
11
- )
12
-
13
- import Decode exposing (Decodable, Decoder)
14
- import Encode exposing (Encodable)
15
-
16
-
17
- # An exact base-10 decimal: value = mantissa * 10 ^ exponent. Lossless for
18
- # Postgres numeric/decimal — no Float rounding. The wire form is
19
- # "<mantissa>e<exponent>" (e.g. "175e-3"), which Postgres casts to numeric
20
- # exactly and which decodes back without parsing a decimal point.
21
- type Decimal = Decimal(Int, Int)
22
-
23
-
24
- def decimal(m: Int, e: Int) -> Decimal
25
- Decimal(m, e)
26
- end
27
-
28
-
29
- def mantissa(d: Decimal) -> Int
30
- Decimal(m, _) = d
31
-
32
- m
33
- end
34
-
35
-
36
- def exponent(d: Decimal) -> Int
37
- Decimal(_, e) = d
38
-
39
- e
40
- end
41
-
42
-
43
- def pow10(n: Int) -> Int
44
- n <= 0 ? 1 : 10 * pow10(n - 1)
45
- end
46
-
47
-
48
- def abs(x: Int) -> Int
49
- x < 0 ? 0 - x : x
50
- end
51
-
52
-
53
- # Integer division, rounding ties half away from zero — i.e. Java's HALF_UP
54
- # and Ruby BigDecimal's default mode (2.5 -> 3, -2.5 -> -3). Rounds the
55
- # magnitude, then re-applies the sign. A zero `den` raises through Int `/`.
56
- def round_div(num: Int, den: Int) -> Int
57
- negative = not ((num < 0) == (den < 0))
58
- n = abs(num)
59
- d = abs(den)
60
- q = n / d
61
- r = n - q * d
62
- rounded = (2 * r) >= d ? q + 1 : q
63
-
64
- negative ? 0 - rounded : rounded
65
- end
66
-
67
-
68
- def trunc_div(num: Int, den: Int) -> Int
69
- q = abs(num) / abs(den)
70
-
71
- num < 0 ? 0 - q : q
72
- end
73
-
74
-
75
- # `+` `-` `*` are exact. `+`/`-` line the operands up on the smaller exponent
76
- # first; `*` adds exponents.
77
- def add(a: Decimal, b: Decimal) -> Decimal
78
- Decimal(ma, ea) = a
79
- Decimal(mb, eb) = b
80
- e = ea < eb ? ea : eb
81
-
82
- Decimal((ma * pow10(ea - e)) + (mb * pow10(eb - e)), e)
83
- end
84
-
85
-
86
- def sub(a: Decimal, b: Decimal) -> Decimal
87
- Decimal(ma, ea) = a
88
- Decimal(mb, eb) = b
89
- e = ea < eb ? ea : eb
90
-
91
- Decimal((ma * pow10(ea - e)) - (mb * pow10(eb - e)), e)
92
- end
93
-
94
-
95
- def mul(a: Decimal, b: Decimal) -> Decimal
96
- Decimal(ma, ea) = a
97
- Decimal(mb, eb) = b
98
-
99
- Decimal(ma * mb, ea + eb)
100
- end
101
-
102
-
103
- # a / b rounded half-up to `scale` decimal places — like Java's
104
- # BigDecimal.divide(divisor, scale, HALF_UP). Division by zero raises.
105
- def div(a: Decimal, b: Decimal, scale: Int) -> Decimal
106
- Decimal(ma, ea) = a
107
- Decimal(mb, eb) = b
108
- k = (ea - eb) + scale
109
- num = k >= 0 ? ma * pow10(k) : ma
110
- den = k >= 0 ? mb : mb * pow10(0 - k)
111
-
112
- Decimal(round_div(num, den), 0 - scale)
113
- end
114
-
115
-
116
- # The Numeric `/`: like Ruby's BigDecimal `/`, it can't be exact for a
117
- # repeating quotient (1/3), so it rounds half-up to a generous default scale.
118
- def divide(a: Decimal, b: Decimal) -> Decimal
119
- div(a, b, 32)
120
- end
121
-
122
-
123
- implements Numeric(Decimal) with
124
- (+): add,
125
- (-): sub,
126
- (*): mul,
127
- (/): divide
128
- end
129
-
130
-
131
- # Round to `scale` decimal places, half-up. Fewer places than the value
132
- # already has are left untouched.
133
- def round(d: Decimal, scale: Int) -> Decimal
134
- Decimal(m, e) = d
135
- shift = e + scale
136
-
137
- shift >= 0 ? d : Decimal(round_div(m, pow10(0 - shift)), 0 - scale)
138
- end
139
-
140
-
141
- # Truncates toward zero (drops the fractional part).
142
- def to_i(d: Decimal) -> Int
143
- Decimal(m, e) = d
144
-
145
- e >= 0 ? m * pow10(e) : trunc_div(m, pow10(0 - e))
146
- end
147
-
148
-
149
- def to_float(d: Decimal) -> Float
150
- Decimal(m, e) = d
151
-
152
- e >= 0
153
- ? Basics.to_float(m * pow10(e))
154
- : Basics.to_float(m) / Basics.to_float(pow10(0 - e))
155
- end
156
-
157
-
158
- def to_wire(d: Decimal) -> String
159
- Decimal(m, e) = d
160
-
161
- String.from_int(m) ++ "e" ++ String.from_int(e)
162
- end
163
-
164
-
165
- def parse(s: String) -> Maybe(Decimal)
166
- case String.split(s, "e")
167
- in [m, e]
168
- String.to_int(m)
169
- |> Maybe.and_then((mi) -> {
170
- String.to_int(e) |> Maybe.map((ei) -> { Decimal(mi, ei) })
171
- })
172
-
173
- else Nothing
174
- end
175
- end
176
-
177
-
178
- def decimal_decoder(s: String) -> Decoder(Decimal)
179
- case parse(s)
180
- in Just(d) then Decode.succeed(d)
181
- in Nothing then Decode.fail("invalid decimal: " ++ s)
182
- end
183
- end
184
-
185
-
186
- implements Decodable(Decimal) with
187
- decoder: -> { Decode.string |> Decode.and_then(decimal_decoder) }
188
- end
189
-
190
-
191
- implements Encodable(Decimal) with
192
- encoder: (d) -> { Encode.string(to_wire(d)) }
193
- end