primate-run 0.6.1 → 0.7.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: 0f83843c5b0fa350eec8aa6c38b3d2bd3277707d8c8dbeff6c4d1f1a655ae31c
4
- data.tar.gz: 25bb52f15b1d6bb0afa45098d230c132a5c61a7da54b717d88bf7771ed8694b0
3
+ metadata.gz: fd5815a95c686288ead6c2af8a81f56f36ad8c20a9a93939579626987680f160
4
+ data.tar.gz: cda12f8c3d71c1e12e27054228fa2306869d0bcbbf0d3afd36f5c1740710af0e
5
5
  SHA512:
6
- metadata.gz: 94a76367d2d7d059a4e1bd7403228d339900bb663843b94746549b66fe7ce46bc5671926da67348e6fa1ca407aecc231be9da15a1681d76ea072e6c3fb4a3825
7
- data.tar.gz: e6dbcc74893720e392afd29f82b50ed8790e00b96e81de31862c54a70404f99e5830e0cde2e0501a5d80c8709c864b6080b1dfa63633fc28895395f396556275
6
+ metadata.gz: 6801700e003b7dbd31bf8f7322e2388742c4093b769ded377d1f864130415b40b963e4673545d3c21b7427ad573b7ca7d8ce0a305fd58bffa34c8a47eee1b321
7
+ data.tar.gz: dbc173ba18b451c287747b067624a64cf68758268d3ea94f6925006841aaaf52ec6df05085bc5a3ac791ed27236da15728e86ebb42cf7d2aafcb37e7d57219df
data/lib/primate/i18n.rb CHANGED
@@ -21,11 +21,7 @@ module I18N
21
21
  def t(key, params = nil)
22
22
  return key unless @current
23
23
 
24
- if params.nil?
25
- @current.t(key)
26
- else
27
- @current.t(key, params.to_json)
28
- end
24
+ params.nil? ? @current.t(key) : @current.t(key, params.to_json)
29
25
  end
30
26
 
31
27
  # Locale accessor
@@ -43,7 +39,7 @@ module I18N
43
39
  #
44
40
  # @return [String] Current locale code
45
41
  def get
46
- I18N.instance_variable_get(:@current)&.[]('locale')
42
+ I18N.instance_variable_get(:@current)&.[]("locale")
47
43
  end
48
44
 
49
45
  # Set the current locale
@@ -2,22 +2,25 @@
2
2
 
3
3
  module Primate
4
4
  class Readable
5
- attr_reader :content_type
5
+ attr_reader :type
6
6
 
7
- def initialize(typed_array, content_type = nil)
7
+ def initialize(typed_array, type = nil)
8
8
  @ta = typed_array
9
- @content_type = content_type
9
+ @type = type
10
10
  @pos = 0
11
11
  @size = @ta[:length].to_i
12
12
  end
13
13
 
14
14
  def size = @size
15
15
  def eof? = @pos >= @size
16
- def rewind; @pos = 0; self; end
16
+ def rewind
17
+ @pos = 0
18
+ self
19
+ end
17
20
 
18
21
  # return a binary string (ASCII-8BIT), advance position
19
22
  def read(n = nil)
20
- return ''.b if eof?
23
+ return "".b if eof?
21
24
  n = n ? [n, @size - @pos].min : (@size - @pos)
22
25
  str = pack_range(@pos, n)
23
26
  @pos += n
@@ -60,7 +63,7 @@ module Primate
60
63
  private
61
64
 
62
65
  def pack_range(off, n)
63
- Array.new(n) { |i| @ta[off + i].to_i }.pack('C*')
66
+ Array.new(n) { |i| @ta[off + i].to_i }.pack("C*")
64
67
  end
65
68
  end
66
69
  end
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'url'
4
- require_relative 'request_bag'
5
- require_relative 'request_body'
3
+ require_relative "url"
4
+ require_relative "request_bag"
5
+ require_relative "request_body"
6
6
 
7
7
  class Request
8
8
  attr_reader :url, :body, :path, :query, :headers, :cookies
9
9
 
10
10
  def initialize(request, helpers)
11
- @url = Primate::URL.new(request['url'])
12
- @body = RequestBody.new(request['body'], helpers)
13
- @path = RequestBag.new(request['path'], helpers)
14
- @query = RequestBag.new(request['query'], helpers)
15
- @headers = RequestBag.new(request['headers'], helpers)
16
- @cookies = RequestBag.new(request['cookies'], helpers)
11
+ @url = Primate::URL.new(request["url"])
12
+ @body = RequestBody.new(request["body"], helpers)
13
+ @path = RequestBag.new(request["path"], helpers)
14
+ @query = RequestBag.new(request["query"], helpers)
15
+ @headers = RequestBag.new(request["headers"], helpers)
16
+ @cookies = RequestBag.new(request["cookies"], helpers)
17
17
  end
18
18
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'type'
3
+ require_relative "type"
4
4
 
5
5
  class RequestBag
6
6
  # initialize with JavaScript object data
@@ -41,15 +41,6 @@ class RequestBag
41
41
  @data.key?(key) && !@data[key].nil?
42
42
  end
43
43
 
44
- # Parse the entire bag with a schema
45
- #
46
- # @param schema [Object] Object with parse method
47
- # @param coerce [Boolean] Whether to coerce types
48
- # @return [Object] Parsed result
49
- def parse(schema, coerce = false)
50
- schema.parse(@data, coerce)
51
- end
52
-
53
44
  # Convert to hash (Ruby standard conversion)
54
45
  #
55
46
  # @return [Hash] The underlying data
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
- require_relative 'readable'
5
- require_relative 'uploaded_file'
3
+ require "json"
4
+ require_relative "readable"
5
+ require_relative "uploaded_file"
6
6
 
7
7
  class Multipart
8
8
  attr_reader :form, :files
@@ -34,16 +34,17 @@ class RequestBody
34
34
  def multipart
35
35
  form = JSON.parse(@body.formSync.to_s)
36
36
  files_js = @body.filesSync
37
- files = Array.new(files_js[:length].to_i) do |i|
38
- f = files_js[i]
39
- Primate::UploadedFile.new(
40
- field: f['field'].to_s,
41
- name: f['name'].to_s,
42
- type: f['type'].to_s,
43
- size: f['size'].to_i,
44
- bytes: f['bytes']
45
- )
46
- end
37
+ files =
38
+ Array.new(files_js[:length].to_i) do |i|
39
+ f = files_js[i]
40
+ Primate::UploadedFile.new(
41
+ field: f["field"].to_s,
42
+ name: f["name"].to_s,
43
+ type: f["type"].to_s,
44
+ size: f["size"].to_i,
45
+ bytes: f["bytes"]
46
+ )
47
+ end
47
48
  Multipart.new(form, files)
48
49
  end
49
50
 
@@ -1,6 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Response
4
+ def self.page(props = {}, options = {})
5
+ { __PRMT__: "page", props: props, options: options }
6
+ end
7
+
8
+ def self.abort(options)
9
+ { __PRMT__: "abort", options: options }
10
+ end
11
+
12
+ def self.binary(body, options = {})
13
+ { __PRMT__: "binary", body: body, options: options }
14
+ end
15
+
16
+ def self.json(body, options = {})
17
+ { __PRMT__: "json", body: body, options: options }
18
+ end
19
+
20
+ def self.null
21
+ { __PRMT__: "null" }
22
+ end
23
+
24
+ def self.text(body, options = {})
25
+ { __PRMT__: "text", body: body, options: options }
26
+ end
27
+
28
+ def self.sse(options = {}, &body)
29
+ raise ArgumentError, "block required" unless body
30
+
31
+ { __PRMT__: "sse", body: body, options: options }
32
+ end
33
+
34
+ def self.ws(actions = {})
35
+ { __PRMT__: "ws", actions: actions }
36
+ end
37
+
4
38
  # Create a view response
5
39
  #
6
40
  # @param name [String] The view template name
@@ -8,7 +42,7 @@ module Response
8
42
  # @param options [Hash] Additional view options
9
43
  # @return [Hash] Response object for the Primate framework
10
44
  def self.view(name, props = {}, options = {})
11
- { __PRMT__: 'view', name: name, props: props, options: options }
45
+ { __PRMT__: "view", name: name, props: props, options: options }
12
46
  end
13
47
 
14
48
  # Create a redirect response
@@ -17,7 +51,7 @@ module Response
17
51
  # @param options [Hash] Redirect options (status code, etc.)
18
52
  # @return [Hash] Response object for the Primate framework
19
53
  def self.redirect(location, status = nil)
20
- result = { __PRMT__: 'redirect', location: location }
54
+ result = { __PRMT__: "redirect", location: location }
21
55
  result[:status] = status if status
22
56
  result
23
57
  end
@@ -27,6 +61,6 @@ module Response
27
61
  # @param options [Hash] Error options
28
62
  # @return [Hash] Response object for the Primate framework
29
63
  def self.error(options = {})
30
- { __PRMT__: 'error', options: options }
64
+ { __PRMT__: "error", options: options }
31
65
  end
32
66
  end
data/lib/primate/route.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'request'
4
- require_relative 'response'
5
- require_relative 'i18n'
6
- require_relative '../primate'
3
+ require_relative "request"
4
+ require_relative "response"
5
+ require_relative "i18n"
6
+ require_relative "../primate"
7
7
 
8
8
  module Route
9
9
  @registry = Hash.new { |h, k| h[k] = {} }
@@ -3,14 +3,16 @@
3
3
  # Session management
4
4
  module Session
5
5
  class << self
6
- attr_accessor :current
7
-
8
6
  # Set the current session instance (called by framework)
9
7
  #
10
8
  # @param session_instance [SessionInstance] Session instance from JavaScript runtime
11
9
  # @return [void]
12
10
  def set_current(session_instance)
13
- self.current = session_instance
11
+ Fiber[:primate_session] = session_instance
12
+ end
13
+
14
+ def current
15
+ Fiber[:primate_session]
14
16
  end
15
17
 
16
18
  # Get the session ID
@@ -81,14 +83,14 @@ class SessionInstance
81
83
  #
82
84
  # @return [String] Session identifier
83
85
  def id
84
- @session['id']
86
+ @session["id"]
85
87
  end
86
88
 
87
89
  # Check if session exists
88
90
  #
89
91
  # @return [Boolean] True if session exists
90
92
  def exists
91
- @session['exists']
93
+ @session["exists"]
92
94
  end
93
95
 
94
96
  # Create a new session with data
@@ -96,21 +98,21 @@ class SessionInstance
96
98
  # @param data [Hash] Initial session data
97
99
  # @return [void]
98
100
  def create(data)
99
- @session.create(data)
101
+ @session.create(data).await
100
102
  end
101
103
 
102
104
  # Get session data (raises if no session)
103
105
  #
104
106
  # @return [Hash] Session data
105
107
  def get
106
- @session.get
108
+ @session.get.await
107
109
  end
108
110
 
109
111
  # Try to get session data (returns empty if no session)
110
112
  #
111
113
  # @return [Hash] Session data or empty hash
112
114
  def try
113
- @session.try
115
+ @session.try.await
114
116
  end
115
117
 
116
118
  # Set session data
@@ -118,13 +120,13 @@ class SessionInstance
118
120
  # @param data [Hash] Data to store in session
119
121
  # @return [void]
120
122
  def set(data)
121
- @session.set(data)
123
+ @session.set(data).await
122
124
  end
123
125
 
124
126
  # Destroy the session
125
127
  #
126
128
  # @return [void]
127
129
  def destroy
128
- @session.destroy
130
+ @session.destroy.await
129
131
  end
130
132
  end
data/lib/primate/type.rb CHANGED
@@ -1,43 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
3
+ require "json"
4
4
 
5
5
  def type(value, helpers)
6
6
  type = helpers.type(value).to_s
7
7
 
8
- if type == 'integer'
9
- return value.to_i
10
- end
8
+ return value.to_i if type == "integer"
11
9
 
12
- if type == 'float'
13
- return value.to_f
14
- end
10
+ return value.to_f if type == "float"
15
11
 
16
- if type == 'boolean'
17
- return value == JS::True
18
- end
12
+ return value == JS::True if type == "boolean"
19
13
 
20
- if type == 'string'
21
- return value.to_s
22
- end
14
+ return value.to_s if type == "string"
23
15
 
24
- if type == 'nil'
25
- return nil
26
- end
16
+ return nil if type == "nil"
27
17
 
28
- if type == 'array'
18
+ if type == "array"
29
19
  as_array = JS.global[:Array].from(value)
30
- return Array.new(as_array[:length].to_i) {
31
- type(as_array[_1], helpers)
32
- }
20
+ return Array.new(as_array[:length].to_i) { type(as_array[_1], helpers) }
33
21
  end
34
22
 
35
- if type == 'object'
23
+ if type == "object"
36
24
  as_entries = JS.global[:Object].entries(value)
37
- return Hash[Array.new(as_entries[:length].to_i) {[
38
- as_entries[_1][0].to_s,
39
- type(as_entries[_1][1], helpers)
40
- ]}]
25
+ return(
26
+ Hash[
27
+ Array.new(as_entries[:length].to_i) do
28
+ [as_entries[_1][0].to_s, type(as_entries[_1][1], helpers)]
29
+ end
30
+ ]
31
+ )
41
32
  end
42
33
 
43
34
  value
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'readable'
3
+ require_relative "readable"
4
4
 
5
5
  module Primate
6
6
  class UploadedFile
data/lib/primate/url.rb CHANGED
@@ -2,23 +2,32 @@
2
2
 
3
3
  module Primate
4
4
  class URL
5
- attr_reader :href, :origin, :protocol, :username, :password, :host,
6
- :hostname, :port, :pathname, :search, :hash
5
+ attr_reader :href,
6
+ :origin,
7
+ :protocol,
8
+ :username,
9
+ :password,
10
+ :host,
11
+ :hostname,
12
+ :port,
13
+ :pathname,
14
+ :search,
15
+ :hash
7
16
 
8
17
  # initialize URL from JavaScript URL object
9
18
  # @param url [Object] JavaScript URL object from the runtime
10
19
  def initialize(url)
11
- @href = url['href'].to_s
12
- @origin = url['origin'].to_s
13
- @protocol = url['protocol'].to_s
14
- @username = url['username'].to_s
15
- @password = url['password'].to_s
16
- @host = url['host'].to_s
17
- @hostname = url['hostname'].to_s
18
- @port = url['port'].to_s
19
- @pathname = url['pathname'].to_s
20
- @search = url['search'].to_s
21
- @hash = url['hash'].to_s
20
+ @href = url["href"].to_s
21
+ @origin = url["origin"].to_s
22
+ @protocol = url["protocol"].to_s
23
+ @username = url["username"].to_s
24
+ @password = url["password"].to_s
25
+ @host = url["host"].to_s
26
+ @hostname = url["hostname"].to_s
27
+ @port = url["port"].to_s
28
+ @pathname = url["pathname"].to_s
29
+ @search = url["search"].to_s
30
+ @hash = url["hash"].to_s
22
31
  end
23
32
  end
24
33
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primate
4
- VERSION = '0.6.1'
4
+ VERSION = "0.7.0"
5
5
  end
data/sig/primate.rbs CHANGED
@@ -60,7 +60,6 @@ class RequestBag
60
60
  def []: (String key) -> String
61
61
  def try: (String key) -> String?
62
62
  def has?: (String key) -> bool
63
- def parse: (untyped schema, ?bool coerce) -> untyped
64
63
  def to_h: () -> Hash[String, untyped]
65
64
  def size: () -> Integer
66
65
  end
@@ -99,8 +98,16 @@ module Session
99
98
  end
100
99
 
101
100
  module Response
101
+ def self.page: (?Hash[Symbol | String, untyped] props, ?Hash[Symbol | String, untyped] options) -> Hash[Symbol, untyped]
102
+ def self.abort: (Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
103
+ def self.binary: (Array[Integer] body, ?Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
104
+ def self.json: (untyped body, ?Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
105
+ def self.null: () -> Hash[Symbol, untyped]
106
+ def self.text: (String body, ?Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
107
+ def self.sse: (?Hash[Symbol, untyped] options) { (untyped) -> Proc? } -> Hash[Symbol, untyped]
108
+ def self.ws: (?Hash[Symbol, Proc] actions) -> Hash[Symbol, untyped]
102
109
  def self.view: (String name, ?Hash[String, untyped] props, ?Hash[String, untyped] options) -> Hash[Symbol, untyped]
103
- def self.redirect: (String location, ?Hash[String, untyped] options) -> Hash[Symbol, untyped]
110
+ def self.redirect: (String location, ?Integer status) -> Hash[Symbol, untyped]
104
111
  def self.error: (?Hash[String, untyped] options) -> Hash[Symbol, untyped]
105
112
  end
106
113
 
@@ -121,44 +128,12 @@ module Route
121
128
  def self.call_route: (String method, Request request) -> untyped
122
129
  end
123
130
 
124
- module Pema
125
- class ValidationError < StandardError
126
- end
127
-
128
- class Field
129
- def parse: (untyped value, ?bool coerce) -> untyped
130
- end
131
-
132
- class StringType < Field
133
- def parse: (untyped value, ?bool coerce) -> String
134
- end
135
-
136
- class BooleanType < Field
137
- def parse: (untyped value, ?bool coerce) -> bool
138
- end
139
-
140
- class IntType < Field
141
- def parse: (untyped value, ?bool coerce) -> Integer
142
- end
143
-
144
- class FloatType < Field
145
- def parse: (untyped value, ?bool coerce) -> Float
146
- end
147
-
148
- class Schema
149
- def initialize: (Hash[String, Field] fields) -> void
150
- def parse: (Hash[String, untyped] data, ?bool coerce) -> Hash[String, untyped]
151
- end
152
-
153
- def self.string: () -> StringType
154
- def self.boolean: () -> BooleanType
155
- def self.int: () -> IntType
156
- def self.float: () -> FloatType
157
- def self.schema: (Hash[String, Field] fields) -> Schema
158
- end
159
-
160
131
  module PrimateInternal
161
132
  def self.set_session: (untyped session, untyped helpers) -> void
162
133
  end
163
134
 
164
- def type: (untyped value, untyped helpers) -> untyped
135
+ class Object
136
+ private
137
+
138
+ def type: (untyped value, untyped helpers) -> untyped
139
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: primate-run
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Primate Team
@@ -37,6 +37,20 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '13.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rbs
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.10'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.10'
40
54
  - !ruby/object:Gem::Dependency
41
55
  name: ruby-lsp
42
56
  requirement: !ruby/object:Gem::Requirement
@@ -51,6 +65,20 @@ dependencies:
51
65
  - - "~>"
52
66
  - !ruby/object:Gem::Version
53
67
  version: '0.26'
68
+ - !ruby/object:Gem::Dependency
69
+ name: syntax_tree
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '6.3'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '6.3'
54
82
  description: Provides Ruby support for building web applications with the Primate
55
83
  framework
56
84
  email:
@@ -61,7 +89,6 @@ extra_rdoc_files: []
61
89
  files:
62
90
  - lib/primate.rb
63
91
  - lib/primate/i18n.rb
64
- - lib/primate/pema.rb
65
92
  - lib/primate/readable.rb
66
93
  - lib/primate/request.rb
67
94
  - lib/primate/request_bag.rb
@@ -79,8 +106,8 @@ licenses:
79
106
  - MIT
80
107
  metadata:
81
108
  homepage_uri: https://primate.run
82
- source_code_uri: https://github.com/primate-run/ruby
83
- bug_tracker_uri: https://github.com/primate-run/ruby/issues
109
+ source_code_uri: https://repopack.com/project/primate
110
+ bug_tracker_uri: https://repopack.com/project/primate/tasks
84
111
  rdoc_options: []
85
112
  require_paths:
86
113
  - lib
@@ -88,14 +115,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
115
  requirements:
89
116
  - - ">="
90
117
  - !ruby/object:Gem::Version
91
- version: 3.4.0
118
+ version: 4.0.0
92
119
  required_rubygems_version: !ruby/object:Gem::Requirement
93
120
  requirements:
94
121
  - - ">="
95
122
  - !ruby/object:Gem::Version
96
123
  version: '0'
97
124
  requirements: []
98
- rubygems_version: 3.6.9
125
+ rubygems_version: 4.0.20
99
126
  specification_version: 4
100
127
  summary: Ruby route handlers for the Primate web framework
101
128
  test_files: []
data/lib/primate/pema.rb DELETED
@@ -1,134 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pema
4
- class ValidationError < StandardError; end
5
-
6
- class Field
7
- def parse(value, coerce = false)
8
- raise NotImplementedError, 'Subclasses must implement parse'
9
- end
10
- end
11
-
12
- class StringType < Field
13
- def parse(value, coerce = false)
14
- return value if value.is_a?(String)
15
-
16
- if coerce
17
- return value.to_s
18
- end
19
-
20
- raise ValidationError, "expected string, got #{value.class}"
21
- end
22
- end
23
-
24
- class BooleanType < Field
25
- def parse(value, coerce = false)
26
- return value if value.is_a?(TrueClass) || value.is_a?(FalseClass)
27
-
28
- if coerce
29
- case value
30
- when String
31
- return false if value.empty?
32
- case value.downcase
33
- when 'true', '1', 'yes', 'on'
34
- return true
35
- when 'false', '0', 'no', 'off'
36
- return false
37
- else
38
- raise ValidationError, "cannot parse '#{value}' as boolean"
39
- end
40
- else
41
- raise ValidationError, "cannot coerce #{value.class} to boolean"
42
- end
43
- end
44
-
45
- raise ValidationError, "expected boolean, got #{value.class}"
46
- end
47
- end
48
-
49
- class IntType < Field
50
- def parse(value, coerce = false)
51
- return value if value.is_a?(Integer)
52
-
53
- if coerce
54
- case value
55
- when Float
56
- return value.to_i
57
- when String
58
- return 0 if value.empty?
59
- return Integer(value)
60
- else
61
- raise ValidationError, "cannot coerce #{value.class} to int"
62
- end
63
- end
64
-
65
- raise ValidationError, "expected integer, got #{value.class}"
66
- rescue ArgumentError
67
- raise ValidationError, "cannot parse '#{value}' as integer"
68
- end
69
- end
70
-
71
- class FloatType < Field
72
- def parse(value, coerce = false)
73
- return value if value.is_a?(Float)
74
-
75
- if coerce
76
- case value
77
- when Integer
78
- return value.to_f
79
- when String
80
- return 0.0 if value.empty?
81
- return Float(value)
82
- else
83
- raise ValidationError, "cannot coerce #{value.class} to float"
84
- end
85
- end
86
-
87
- raise ValidationError, "expected float, got #{value.class}"
88
- rescue ArgumentError
89
- raise ValidationError, "cannot parse '#{value}' as float"
90
- end
91
- end
92
-
93
- def self.string
94
- StringType.new
95
- end
96
-
97
- def self.boolean
98
- BooleanType.new
99
- end
100
-
101
- def self.int
102
- IntType.new
103
- end
104
-
105
- def self.float
106
- FloatType.new
107
- end
108
-
109
- class Schema
110
- def initialize(fields)
111
- @fields = fields
112
- end
113
-
114
- def parse(data, coerce = false)
115
- result = {}
116
-
117
- @fields.each do |name, field|
118
- value = data.key?(name) ? data[name] : ''
119
-
120
- begin
121
- result[name] = field.parse(value, coerce)
122
- rescue ValidationError => e
123
- raise ValidationError, "parsing failed for field '#{name}': #{e.message}"
124
- end
125
- end
126
-
127
- result
128
- end
129
- end
130
-
131
- def self.schema(fields)
132
- Schema.new(fields)
133
- end
134
- end