@@ -42,7 +42,7 @@ opinions. Please communicate with us on [Slack](https://t.mp/slack) in the `#rub
42
42
- [ Cloud Client Using mTLS] ( #cloud-client-using-mtls )
43
43
- [ Cloud Client Using API Key] ( #cloud-client-using-api-key )
44
44
- [ Data Conversion] ( #data-conversion )
45
- - [ ActiveRecord and ActiveModel] ( #activerecord-and- activemodel )
45
+ - [ ActiveModel] ( #activemodel )
46
46
- [ Workers] ( #workers )
47
47
- [ Workflows] ( #workflows )
48
48
- [ Workflow Definition] ( #workflow-definition )
@@ -71,6 +71,9 @@ opinions. Please communicate with us on [Slack](https://t.mp/slack) in the `#rub
71
71
- [ Metrics] ( #metrics )
72
72
- [ OpenTelemetry Tracing] ( #opentelemetry-tracing )
73
73
- [ OpenTelemetry Tracing in Workflows] ( #opentelemetry-tracing-in-workflows )
74
+ - [ Rails] ( #rails )
75
+ - [ ActiveRecord] ( #activerecord )
76
+ - [ Lazy/Eager Loading] ( #lazyeager-loading )
74
77
- [ Ractors] ( #ractors )
75
78
- [ Platform Support] ( #platform-support )
76
79
- [ Development] ( #development )
@@ -295,57 +298,43 @@ will be tried in order until one accepts (default falls through to the JSON one)
295
298
` encoding ` metadata value which is used to know which converter to use on deserialize. Custom encoding converters can be
296
299
created, or even the entire payload converter can be replaced with a different implementation.
297
300
298
- ##### ActiveRecord and ActiveModel
301
+ ** NOTE:** For ActiveRecord, or other general/ORM models that are used for a different purpose, it is not recommended to
302
+ try to reuse them as Temporal models. Eventually model purposes diverge and models for a Temporal workflows/activities
303
+ should be specific to their use for clarity and compatibility reasons. Also many Ruby ORMs do many lazy things and
304
+ therefore provide unclear serialization semantics. Instead, consider having models specific for workflows/activities and
305
+ translate to/from existing models as needed. See the next section on how to do this with ActiveModel objects.
299
306
300
- By default, ` ActiveRecord ` and ` ActiveModel ` objects do not natively support the ` JSON ` module. A mixin can be created
301
- to add this support for ` ActiveRecord ` , for example:
307
+ ##### ActiveModel
308
+
309
+ By default, ActiveModel objects do not natively support the ` JSON ` module. A mixin can be created to add this support
310
+ for ActiveRecord, for example:
302
311
303
312
``` ruby
304
- module ActiveRecordJSONSupport
313
+ module ActiveModelJSONSupport
305
314
extend ActiveSupport ::Concern
306
315
include ActiveModel ::Serializers ::JSON
307
316
308
317
included do
318
+ def as_json (* )
319
+ super .merge(::JSON .create_id => self .class .name)
320
+ end
321
+
309
322
def to_json (* args )
310
- hash = as_json
311
- hash[::JSON .create_id] = self .class .name
312
- hash.to_json(* args)
323
+ as_json.to_json(* args)
313
324
end
314
325
315
326
def self .json_create (object )
327
+ object = object.dup
316
328
object.delete(::JSON .create_id)
317
- ret = new
318
- ret.attributes = object
319
- ret
329
+ new (** object.symbolize_keys)
320
330
end
321
331
end
322
332
end
323
333
```
324
334
325
- Similarly, a mixin for ` ActiveModel ` that adds ` attributes ` accessors can leverage this same mixin, for example:
326
-
327
- ``` ruby
328
- module ActiveModelJSONSupport
329
- extend ActiveSupport ::Concern
330
- include ActiveRecordJSONSupport
331
-
332
- included do
333
- def attributes= (hash )
334
- hash.each do |key , value |
335
- send(" #{ key } =" , value)
336
- end
337
- end
338
-
339
- def attributes
340
- instance_values
341
- end
342
- end
343
- end
344
- ```
345
-
346
- Now ` include ActiveRecordJSONSupport ` or ` include ActiveModelJSONSupport ` will make the models work with Ruby ` JSON `
347
- module and therefore Temporal. Of course any other approach to make the models work with the ` JSON ` module will work as
348
- well.
335
+ Now if ` include ActiveModelJSONSupport ` is present on any ActiveModel class, on serialization ` to_json ` will be used
336
+ which will use ` as_json ` which calls the super ` as_json ` but also includes the fully qualified class name as the JSON
337
+ ` create_id ` key. On deserialization, Ruby JSON then uses this key to know what class to call ` json_create ` on.
349
338
350
339
### Workers
351
340
@@ -1156,6 +1145,43 @@ workflow and time to run the activity attempt respectively), but the other spans
1156
1145
are created in workflows and closed immediately since long-lived spans cannot work for durable software that may resume
1157
1146
on other machines.
1158
1147
1148
+ ### Rails
1149
+
1150
+ Temporal Ruby SDK is a generic Ruby library that can work in any Ruby environment. However, there are some common
1151
+ conventions for Rails users to be aware of.
1152
+
1153
+ See the [ rails_app] ( https://github.com/temporalio/samples-ruby/tree/main/rails_app ) sample for an example of using
1154
+ Temporal from Rails.
1155
+
1156
+ #### ActiveRecord
1157
+
1158
+ For ActiveRecord, or other general/ORM models that are used for a different purpose, it is not recommended to
1159
+ try to reuse them as Temporal models. Eventually model purposes diverge and models for a Temporal workflows/activities
1160
+ should be specific to their use for clarity and compatibility reasons. Also many Ruby ORMs do many lazy things and
1161
+ therefore provide unclear serialization semantics. Instead, consider having models specific for workflows/activities and
1162
+ translate to/from existing models as needed. See the [ ActiveModel] ( #activemodel ) section on how to do this with
1163
+ ActiveModel objects.
1164
+
1165
+ #### Lazy/Eager Loading
1166
+
1167
+ By default, Rails
1168
+ [ eagerly loads] ( https://guides.rubyonrails.org/v7.2/autoloading_and_reloading_constants.html#eager-loading ) all
1169
+ application code on application start in production, but lazily loads it in non-production environments. Temporal
1170
+ workflows by default disallow use of IO during the workflow run. With lazy loading enabled in dev/test environments,
1171
+ when an activity class is referenced in a workflow before it has been explicitly ` require ` d, it can give an error like:
1172
+
1173
+ > Cannot access File path from inside a workflow. If this is known to be safe, the code can be run in a
1174
+ > Temporalio::Workflow::Unsafe.illegal_call_tracing_disabled block.
1175
+
1176
+ This comes from ` bootsnap ` via ` zeitwork ` because it is lazily loading a class/module at workflow runtime. It is not
1177
+ good to lazily load code durnig a workflow run because it can be side effecting. Workflows and the classes they
1178
+ reference should not be eagerly loaded.
1179
+
1180
+ To resolve this, either always eagerly load (e.g. ` config.eager_load = true ` ) or explicitly ` require ` what is used by a
1181
+ workflow at the top of the file.
1182
+
1183
+ Note, this only affects non-production environments.
1184
+
1159
1185
### Ractors
1160
1186
1161
1187
It was an original goal to have workflows actually be Ractors for deterministic state isolation and have the library
0 commit comments