json-emitter 1.0.0 → 1.1.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: d2228e23ca76886db68f546e171db2e9c318f35c02e4262b19adf162d43e1151
4
- data.tar.gz: c137299a78e76f0ffdeba789d9477ef94a4b161d0718287ba074dcbc90f4c82d
3
+ metadata.gz: e68f558ac45759cbf0ddd691b8b20f499433dbf2d6321a360280a10b5fdb4840
4
+ data.tar.gz: 86e900c436e45efcf09fff15061a9e325781699bbb98c4fed8ca7accbeab3b5c
5
5
  SHA512:
6
- metadata.gz: b2a21066822a1e4f84df8facee734dd60951dbd489d5e80444be11546e9851e77f9d8c9edc6f9fd1900fd55407cf96cbc193c4402223aa78459265a119ab75a5
7
- data.tar.gz: 2b025e42af1069efc578d5138e6535b35b507659e931ce59dd9164d96adc7dd1d1ceec334b22aa710a233198b63d8cad033b67e244d52a78854d553383e41bea
6
+ metadata.gz: ea19aa1f2ec09eb2b9634af8f2dfc9455957696bf28a26cf01bd70b49ff4b81d88e891f6564517cf56490e9482cc00a8a2dc94a0aaa76441d2587a4f0cbb9024
7
+ data.tar.gz: d246a354e506ccefbd5c8de2b2db1ff7faa2bad31b9425c18d2dc5eadf9fd9ad78e49fa2384aec0bb9f8b6881a3d3b9ac872e3661d40468046ee9e057912fbf6
data/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # JsonEmitter [![Build Status](https://travis-ci.org/jhollinger/json-emitter.svg?branch=master)](https://travis-ci.org/jhollinger/json-emitter)
1
+ # JsonEmitter
2
+
3
+ *NOTE this project may appear inactive, but that's just because it's **done** (I think). Enjoy!*
2
4
 
3
5
  JsonEmitter is a library for efficiently generating very large bits of JSON in Ruby. Need to generate a JSON array of 10,000 database records without eating up all your RAM? No problem! Objects? Nested structures? JsonEmitter has you covered.
4
6
 
@@ -21,7 +23,7 @@ class OrdersController < ApplicationController
21
23
  def index
22
24
  headers["Content-Type"] = "application/json"
23
25
  headers["Last-Modified"] = Time.now.ctime.to_s
24
- self.response_body = JsonEmitter.array(enumerator) { |order|
26
+ self.response_body = JsonEmitter.array(enumerator, rack: env) { |order|
25
27
  order.to_h
26
28
  }
27
29
  end
@@ -33,7 +35,7 @@ end
33
35
  ```ruby
34
36
  get "/orders" do
35
37
  content_type :json
36
- JsonEmitter.array(enumerator) { |order|
38
+ JsonEmitter.array(enumerator, rack: env) { |order|
37
39
  order.to_h
38
40
  }
39
41
  end
@@ -43,7 +45,7 @@ end
43
45
 
44
46
  ```ruby
45
47
  get :orders do
46
- stream JsonEmitter.array(enumerator) { |order|
48
+ stream JsonEmitter.array(enumerator, rack: env) { |order|
47
49
  ApiV1::Entities::Order.new(order)
48
50
  }
49
51
  end
@@ -52,53 +54,70 @@ end
52
54
  **Rack**
53
55
 
54
56
  ```ruby
55
- app = ->(env) {
56
- stream = JsonEmitter.array(enumerator) { |order|
57
- order.to_h
57
+ map "/orders" do
58
+ run ->(env) {
59
+ stream = JsonEmitter.array(enumerator, rack: env) { |order|
60
+ order.to_h
61
+ }
62
+ [200, {"Content-Type" => "application/json"}, stream]
58
63
  }
59
- [200, {"Content-Type" => "application/json"}, stream]
60
- }
64
+ end
61
65
  ```
62
66
 
63
- # Other uses
64
-
65
- `JsonEmitter.array` takes an `Enumerable` and returns a stream that generates chunks of JSON.
66
-
67
- ```ruby
68
- JsonEmitter.array(enumerator).each { |json_chunk|
69
- # write json_chunk somewhere
70
- }
71
- ```
67
+ ## Sending objects
72
68
 
73
- `JsonEmitter.object` takes a `Hash` and returns a stream that generates chunks of JSON. Hash values can be literals, Enumerators, Arrays, other Hashes, or Procs that return any of those. Hashes and arrays may be nested.
69
+ You may also stream Hashes as JSON objects. Keys must be Strings or Symbols, but values may be anything: literals, Enumerators, Arrays, other Hashes, or Procs that return any of those.
74
70
 
75
71
  ```ruby
76
72
  JsonEmitter.object({
77
73
  orders: Order.find_each.lazy.map { |order|
78
74
  {id: order.id, desc: order.description}
79
75
  },
76
+ big_text: ->() { load_tons_of_text },
77
+ }, rack: env)
78
+ ```
80
79
 
81
- big_text_1: ->() {
82
- load_tons_of_text
83
- },
80
+ ## Rack middleware won't work!
84
81
 
85
- big_text_2: ->() {
86
- load_tons_of_text
87
- },
88
- }).each { |json_chunk|
89
- # write json_chunk somewhere
90
- }
82
+ **IMPORTANT** Your Rack middleware will be *finished* by the time your JSON is built! So if you're depending on middleware to set `Time.zone`, report exceptions, etc. it won't work here. Fortunately, you can use `JsonEmitter.wrap` and `JsonEmitter.error` as replacements.
83
+
84
+ Put these somewhere like `config/initializers/json_emitter.rb`.
85
+
86
+ ### JsonEmitter.wrap
87
+
88
+ ```ruby
89
+ # Ensure that ActiveRecord connections are returned to the connection pool
90
+ JsonEmitter.wrap do
91
+ ->(app) { ActiveRecord::Base.with_connection(&app.call) }
92
+ end
93
+
94
+ JsonEmitter.wrap do
95
+ # Get TZ at the call site
96
+ current_tz = Time.zone
97
+
98
+ # Return a Proc that restores the call site's TZ before building the JSON
99
+ ->(app) {
100
+ default_tz = Time.zone
101
+ Time.zone = current_tz
102
+ res = app.call
103
+ Time.zone = default_tz
104
+ res
105
+ }
106
+ end
91
107
  ```
92
108
 
93
- Streams have a `#write` method for writing directly to a `File` or `IO` object.
109
+ ### JsonEmitter.error
94
110
 
95
111
  ```ruby
96
- File.open("~/out.json", "w+") { |f|
97
- JsonEmitter.array(enumerator).write f
98
- }
112
+ JsonEmitter.error do |ex, context|
113
+ Airbrake.notify(ex, {
114
+ request_path: context.request&.path,
115
+ query_string: context.request&.query_string,
116
+ })
117
+ end
99
118
  ```
100
119
 
101
- # Deailing with errors
120
+ ## Returning errors
102
121
 
103
122
  When streaming an HTTP response, you can't change the response code once you start sending data. So if you hit an error after you start, you need another way to communicate errors to the client.
104
123
 
@@ -129,10 +148,28 @@ One way is to always steam an object that includes an `errors` field. Any errors
129
148
  })
130
149
  ```
131
150
 
151
+ # Non-HTTP uses
152
+
153
+ `JsonEmitter.array` takes an `Enumerable` and returns a stream that generates chunks of JSON.
154
+
155
+ ```ruby
156
+ JsonEmitter.array(enumerator).each { |json_chunk|
157
+ # write json_chunk somewhere
158
+ }
159
+ ```
160
+
161
+ Streams have a `#write` method for writing directly to a `File` or `IO` object.
162
+
163
+ ```ruby
164
+ File.open("~/out.json", "w+") { |f|
165
+ JsonEmitter.array(enumerator).write f
166
+ }
167
+ ```
168
+
132
169
  # License
133
170
 
134
171
  MIT License. See LICENSE for details.
135
172
 
136
173
  # Copyright
137
174
 
138
- Copywrite (c) 2019 Jordan Hollinger.
175
+ Copyright (c) 2019 Jordan Hollinger.
@@ -62,12 +62,12 @@ module JsonEmitter
62
62
 
63
63
  def buffer
64
64
  Enumerator.new { |y|
65
- buff = ""
65
+ buff = +""
66
66
  @enum.each { |str|
67
67
  buff << str
68
68
  if buff.bytesize >= @buffer_size
69
69
  y << buff
70
- buff = ""
70
+ buff = +""
71
71
  end
72
72
  }
73
73
  y << buff unless buff.empty?
@@ -19,16 +19,43 @@ module JsonEmitter
19
19
  @pass_through_errors << Puma::ConnectionError if defined? Puma::ConnectionError
20
20
  end
21
21
 
22
+ #
22
23
  # Wrap the enumeration in a block. It will be passed a callback which it must call to continue.
23
- # TODO better docs and examples.
24
+ # Define your wrappers somewhere like config/initializers/json_emitter.rb.
25
+ #
26
+ # JsonEmitter.wrap do
27
+ # # Get TZ at the call site
28
+ # current_tz = Time.zone
29
+ #
30
+ # # Return a Proc that restores the call site's TZ before building the JSON
31
+ # ->(app) {
32
+ # default_tz = Time.zone
33
+ # Time.zone = current_tz
34
+ # res = app.call
35
+ # Time.zone = default_tz
36
+ # res
37
+ # }
38
+ # end
39
+ #
24
40
  def wrap(&block)
25
41
  if (wrapper = block.call)
26
42
  @wrappers.unshift wrapper
27
43
  end
28
44
  end
29
45
 
30
- # Add an error handler.
31
- # TODO better docs and examples.
46
+ #
47
+ # Add an error handler. If the callsite is in a Rack app, the context will have a
48
+ # Rack::Request in context.request.
49
+ #
50
+ # Define your error handlers somewhere like config/initializers/json_emitter.rb.
51
+ #
52
+ # JsonEmitter.error do |ex, context|
53
+ # Airbrake.notify(ex, {
54
+ # request_path: context.request&.path,
55
+ # query_string: context.request&.query_string,
56
+ # })
57
+ # end
58
+ #
32
59
  def error(&handler)
33
60
  @error_handlers += [handler]
34
61
  end
@@ -1,4 +1,4 @@
1
1
  module JsonEmitter
2
2
  # Library version
3
- VERSION = "1.0.0".freeze
3
+ VERSION = "1.1.0".freeze
4
4
  end
data/lib/json-emitter.rb CHANGED
@@ -104,14 +104,41 @@ module JsonEmitter
104
104
  BufferedStream.new(emitter, buffer_size, unit: buffer_unit)
105
105
  end
106
106
 
107
- # Wrap the enumeration in a Proc. It will be passed a callback which it must call to continue.
108
- # TODO better docs and examples.
107
+ #
108
+ # Wrap the enumeration in a block. It will be passed a callback which it must call to continue.
109
+ # Define your wrappers somewhere like config/initializers/json_emitter.rb.
110
+ #
111
+ # JsonEmitter.wrap do
112
+ # # Get TZ at the call site
113
+ # current_tz = Time.zone
114
+ #
115
+ # # Return a Proc that restores the call site's TZ before building the JSON
116
+ # ->(app) {
117
+ # default_tz = Time.zone
118
+ # Time.zone = current_tz
119
+ # res = app.call
120
+ # Time.zone = default_tz
121
+ # res
122
+ # }
123
+ # end
124
+ #
109
125
  def self.wrap(&wrapper)
110
126
  @wrappers.unshift wrapper
111
127
  end
112
128
 
113
- # Add an error handler.
114
- # TODO better docs and examples.
129
+ #
130
+ # Add an error handler. If the callsite is in a Rack app, the context will have a
131
+ # Rack::Request in context.request.
132
+ #
133
+ # Define your error handlers somewhere like config/initializers/json_emitter.rb.
134
+ #
135
+ # JsonEmitter.error do |ex, context|
136
+ # Airbrake.notify(ex, {
137
+ # request_path: context.request&.path,
138
+ # query_string: context.request&.query_string,
139
+ # })
140
+ # end
141
+ #
115
142
  def self.error(&handler)
116
143
  @error_handlers << handler
117
144
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-emitter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Hollinger
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-31 00:00:00.000000000 Z
11
+ date: 2025-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -41,7 +41,7 @@ homepage: https://jhollinger.github.io/json-emitter/
41
41
  licenses:
42
42
  - MIT
43
43
  metadata: {}
44
- post_install_message:
44
+ post_install_message:
45
45
  rdoc_options: []
46
46
  require_paths:
47
47
  - lib
@@ -49,15 +49,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
49
  requirements:
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
- version: 2.3.0
52
+ version: 3.1.0
53
53
  required_rubygems_version: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - ">="
56
56
  - !ruby/object:Gem::Version
57
57
  version: '0'
58
58
  requirements: []
59
- rubygems_version: 3.0.3.1
60
- signing_key:
59
+ rubygems_version: 3.4.19
60
+ signing_key:
61
61
  specification_version: 4
62
62
  summary: Efficiently generate tons of JSON
63
63
  test_files: []