@@ -42,7 +42,7 @@ opinions. Please communicate with us on [Slack](https://t.mp/slack) in the `#rub
4242 - [ Cloud Client Using mTLS] ( #cloud-client-using-mtls )
4343 - [ Cloud Client Using API Key] ( #cloud-client-using-api-key )
4444 - [ Data Conversion] ( #data-conversion )
45- - [ ActiveRecord and ActiveModel] ( #activerecord-and- activemodel )
45+ - [ ActiveModel] ( #activemodel )
4646 - [ Workers] ( #workers )
4747 - [ Workflows] ( #workflows )
4848 - [ Workflow Definition] ( #workflow-definition )
@@ -71,6 +71,9 @@ opinions. Please communicate with us on [Slack](https://t.mp/slack) in the `#rub
7171 - [ Metrics] ( #metrics )
7272 - [ OpenTelemetry Tracing] ( #opentelemetry-tracing )
7373 - [ OpenTelemetry Tracing in Workflows] ( #opentelemetry-tracing-in-workflows )
74+ - [ Rails] ( #rails )
75+ - [ ActiveRecord] ( #activerecord )
76+ - [ Lazy/Eager Loading] ( #lazyeager-loading )
7477 - [ Ractors] ( #ractors )
7578 - [ Platform Support] ( #platform-support )
7679- [ Development] ( #development )
@@ -295,57 +298,43 @@ will be tried in order until one accepts (default falls through to the JSON one)
295298` encoding ` metadata value which is used to know which converter to use on deserialize. Custom encoding converters can be
296299created, or even the entire payload converter can be replaced with a different implementation.
297300
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.
299306
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:
302311
303312``` ruby
304- module ActiveRecordJSONSupport
313+ module ActiveModelJSONSupport
305314 extend ActiveSupport ::Concern
306315 include ActiveModel ::Serializers ::JSON
307316
308317 included do
318+ def as_json (* )
319+ super .merge(::JSON .create_id => self .class .name)
320+ end
321+
309322 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)
313324 end
314325
315326 def self .json_create (object )
327+ object = object.dup
316328 object.delete(::JSON .create_id)
317- ret = new
318- ret.attributes = object
319- ret
329+ new (** object.symbolize_keys)
320330 end
321331 end
322332end
323333```
324334
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.
349338
350339### Workers
351340
@@ -1156,6 +1145,43 @@ workflow and time to run the activity attempt respectively), but the other spans
11561145are created in workflows and closed immediately since long-lived spans cannot work for durable software that may resume
11571146on other machines.
11581147
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+
11591185### Ractors
11601186
11611187It was an original goal to have workflows actually be Ractors for deterministic state isolation and have the library
0 commit comments