ar_sync 1.0.3 → 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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +27 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +29 -31
  5. data/ar_sync.gemspec +1 -1
  6. data/core/{ActioncableAdapter.d.ts → ActionCableAdapter.d.ts} +0 -0
  7. data/core/ActionCableAdapter.js +31 -0
  8. data/core/ArSyncApi.d.ts +8 -2
  9. data/core/ArSyncApi.js +123 -49
  10. data/core/ArSyncModel.js +69 -60
  11. data/core/ArSyncStore.js +522 -381
  12. data/core/ConnectionManager.d.ts +1 -1
  13. data/core/ConnectionManager.js +45 -38
  14. data/core/DataType.d.ts +14 -9
  15. data/core/hooks.d.ts +5 -0
  16. data/core/hooks.js +64 -36
  17. data/gemfiles/Gemfile-rails-6 +9 -0
  18. data/gemfiles/Gemfile-rails-7 +9 -0
  19. data/index.js +2 -2
  20. data/lib/ar_sync/class_methods.rb +71 -36
  21. data/lib/ar_sync/collection.rb +23 -19
  22. data/lib/ar_sync/core.rb +3 -3
  23. data/lib/ar_sync/instance_methods.rb +7 -4
  24. data/lib/ar_sync/rails.rb +1 -1
  25. data/lib/ar_sync/type_script.rb +50 -14
  26. data/lib/ar_sync/version.rb +1 -1
  27. data/lib/generators/ar_sync/install/install_generator.rb +1 -1
  28. data/package-lock.json +1706 -227
  29. data/package.json +1 -1
  30. data/src/core/{ActioncableAdapter.ts → ActionCableAdapter.ts} +0 -0
  31. data/src/core/ArSyncApi.ts +20 -7
  32. data/src/core/ArSyncStore.ts +177 -125
  33. data/src/core/ConnectionManager.ts +1 -0
  34. data/src/core/DataType.ts +15 -16
  35. data/src/core/hooks.ts +31 -7
  36. data/tsconfig.json +2 -2
  37. data/vendor/assets/javascripts/{ar_sync_actioncable_adapter.js.erb → ar_sync_action_cable_adapter.js.erb} +1 -1
  38. metadata +17 -16
  39. data/core/ActioncableAdapter.js +0 -29
  40. data/lib/ar_sync/field.rb +0 -96
@@ -8,7 +8,7 @@ module ArSync::ModelBase::InstanceMethods
8
8
  values = {}
9
9
  self.class._each_sync_parent do |_, info|
10
10
  [*info[:watch]].each do |watch|
11
- values[watch] = watch.is_a?(Proc) ? instance_exec(&watch) : send(watch)
11
+ values[watch] = watch.is_a?(Proc) ? instance_exec(&watch) : __send__(watch)
12
12
  end
13
13
  end
14
14
  values
@@ -33,10 +33,10 @@ module ArSync::ModelBase::InstanceMethods
33
33
  def _serializer_field_value(name)
34
34
  field = self.class._serializer_field_info name
35
35
  preloadeds = field.preloaders.map do |preloader|
36
- args = [[self], nil, {}]
36
+ args = [[self], nil]
37
37
  preloader.call(*(preloader.arity < 0 ? args : args.take(preloader.arity)))
38
38
  end
39
- instance_exec(*preloadeds, nil, {}, &field.data_block)
39
+ instance_exec(*preloadeds, nil, &field.data_block)
40
40
  end
41
41
 
42
42
  def _sync_current_belongs_to_info
@@ -65,11 +65,13 @@ module ArSync::ModelBase::InstanceMethods
65
65
  parents_was = parents.map { nil }
66
66
  elsif action == :destroy
67
67
  parents_was = _sync_parents_info_before_mutation
68
+ return unless parents_was
68
69
  parents = parents_was.map { nil }
69
70
  else
70
71
  parents_was = _sync_parents_info_before_mutation
72
+ return unless parents_was
71
73
  parents = _sync_current_parents_info
72
- column_values_was = _sync_watch_values_before_mutation
74
+ column_values_was = _sync_watch_values_before_mutation || {}
73
75
  column_values = _sync_current_watch_values
74
76
  end
75
77
  parents_was.zip(parents).each do |(parent_was, info_was), (parent, info)|
@@ -109,6 +111,7 @@ module ArSync::ModelBase::InstanceMethods
109
111
 
110
112
  def _sync_notify_self
111
113
  belongs_was = _sync_belongs_to_info_before_mutation
114
+ return unless belongs_was
112
115
  belongs = _sync_current_belongs_to_info
113
116
  belongs.each do |name, info|
114
117
  next if belongs_was[name] == info
data/lib/ar_sync/rails.rb CHANGED
@@ -94,7 +94,7 @@ module ArSync
94
94
  info = sch.class._serializer_field_info api_name
95
95
  raise ArSync::ApiNotFound, "#{type.to_s.capitalize} API named `#{api_name}` not configured" unless info
96
96
  api_params = (request[:params].as_json || {}).transform_keys(&:to_sym)
97
- model = sch.instance_exec(current_user, api_params, &info.data_block)
97
+ model = sch.instance_exec(current_user, **api_params, &info.data_block)
98
98
  { data: yield(model, current_user, request[:query].as_json) }
99
99
  rescue StandardError => e
100
100
  { error: handle_exception(e) }
@@ -10,17 +10,14 @@ module ArSync::TypeScript
10
10
  end
11
11
 
12
12
  def self.generate_type_definition(api_class)
13
+ types = ArSerializer::TypeScript.related_serializer_types([api_class]).reject { |t| t.type == api_class }
13
14
  [
14
- ArSerializer::TypeScript.generate_type_definition(api_related_classes(api_class)),
15
+ types.map { |t| data_type_definition t },
16
+ types.map { |t| query_type_definition t },
15
17
  request_type_definition(api_class)
16
18
  ].join "\n"
17
19
  end
18
20
 
19
- def self.api_related_classes(api_class)
20
- classes = ArSerializer::TypeScript.related_serializer_types([api_class]).map(&:type)
21
- classes - [api_class]
22
- end
23
-
24
21
  def self.request_type_definition(api_class)
25
22
  type = ArSerializer::GraphQL::TypeClass.from api_class
26
23
  definitions = []
@@ -78,24 +75,63 @@ module ArSync::TypeScript
78
75
  <<~CODE
79
76
  import { TypeRequest, ApiNameRequests } from './types'
80
77
  import { DataTypeFromRequest as DataTypeFromRequestPair } from 'ar_sync/core/DataType'
81
- type DataTypeFromRequest<R extends TypeRequest> = DataTypeFromRequestPair<ApiNameRequests[R['api']], R>
78
+ export type NeverMatchArgument = { __nevermatch: never }
79
+ type DataTypeFromRequest<R extends TypeRequest | NeverMatchArgument> = NeverMatchArgument extends R ? never : R extends TypeRequest ? DataTypeFromRequestPair<ApiNameRequests[R['api']], R> : never
82
80
  export default DataTypeFromRequest
83
81
  CODE
84
82
  end
85
83
 
86
84
  def self.generate_hooks_script
87
85
  <<~CODE
88
- import { useState, useEffect, useMemo } from 'react'
86
+ import { useState, useEffect, useMemo, useRef } from 'react'
89
87
  import { TypeRequest } from './types'
90
- import DataTypeFromRequest from './DataTypeFromRequest'
88
+ import DataTypeFromRequest, { NeverMatchArgument } from './DataTypeFromRequest'
91
89
  import { initializeHooks, useArSyncModel as useArSyncModelBase, useArSyncFetch as useArSyncFetchBase } from 'ar_sync/core/hooks'
92
- initializeHooks({ useState, useEffect, useMemo })
93
- export function useArSyncModel<R extends TypeRequest>(request: R | null) {
94
- return useArSyncModelBase<DataTypeFromRequest<R>>(request)
90
+ initializeHooks({ useState, useEffect, useMemo, useRef })
91
+ export function useArSyncModel<R extends TypeRequest | NeverMatchArgument>(request: R | null) {
92
+ return useArSyncModelBase<DataTypeFromRequest<R>>(request as TypeRequest)
95
93
  }
96
- export function useArSyncFetch<R extends TypeRequest>(request: R | null) {
97
- return useArSyncFetchBase<DataTypeFromRequest<R>>(request)
94
+ export function useArSyncFetch<R extends TypeRequest | NeverMatchArgument>(request: R | null) {
95
+ return useArSyncFetchBase<DataTypeFromRequest<R>>(request as TypeRequest)
98
96
  }
99
97
  CODE
100
98
  end
99
+
100
+ def self.query_type_definition(type)
101
+ field_definitions = type.fields.map do |field|
102
+ association_type = field.type.association_type
103
+ if association_type
104
+ qname = "Type#{association_type.name}Query"
105
+ if field.args.empty?
106
+ "#{field.name}?: true | #{qname} | { attributes?: #{qname} }"
107
+ else
108
+ "#{field.name}?: true | #{qname} | { params: #{field.args_ts_type}; attributes?: #{qname} }"
109
+ end
110
+ else
111
+ "#{field.name}?: true"
112
+ end
113
+ end
114
+ field_definitions << "'*'?: true"
115
+ query_type_name = "Type#{type.name}Query"
116
+ base_query_type_name = "Type#{type.name}QueryBase"
117
+ <<~TYPE
118
+ export type #{query_type_name} = keyof (#{base_query_type_name}) | Readonly<(keyof (#{base_query_type_name}))[]> | #{base_query_type_name}
119
+ export interface #{base_query_type_name} {
120
+ #{field_definitions.map { |line| " #{line}" }.join("\n")}
121
+ }
122
+ TYPE
123
+ end
124
+
125
+ def self.data_type_definition(type)
126
+ field_definitions = []
127
+ type.fields.each do |field|
128
+ field_definitions << "#{field.name}: #{field.type.ts_type}"
129
+ end
130
+ field_definitions << "_meta?: { name: '#{type.name}'; query: Type#{type.name}QueryBase }"
131
+ <<~TYPE
132
+ export interface Type#{type.name} {
133
+ #{field_definitions.map { |line| " #{line}" }.join("\n")}
134
+ }
135
+ TYPE
136
+ end
101
137
  end
@@ -1,3 +1,3 @@
1
1
  module ArSync
2
- VERSION = '1.0.3'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -78,7 +78,7 @@ module ArSync
78
78
  [
79
79
  '//= require ar_sync',
80
80
  '//= require action_cable',
81
- '//= require ar_sync_actioncable_adapter',
81
+ '//= require ar_sync_action_cable_adapter',
82
82
  'ArSyncModel.setConnectionAdapter(new ArSyncActionCableAdapter(ActionCable))'
83
83
  ].join("\n") + "\n",
84
84
  before: '//= require_tree .'