ar_sync 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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 .'