ar_sync 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +53 -0
- data/LICENSE.txt +21 -0
- data/README.md +128 -0
- data/Rakefile +10 -0
- data/ar_sync.gemspec +28 -0
- data/bin/console +12 -0
- data/bin/setup +8 -0
- data/core/ActioncableAdapter.d.ts +10 -0
- data/core/ActioncableAdapter.js +29 -0
- data/core/ArSyncApi.d.ts +5 -0
- data/core/ArSyncApi.js +74 -0
- data/core/ArSyncModelBase.d.ts +71 -0
- data/core/ArSyncModelBase.js +110 -0
- data/core/ConnectionAdapter.d.ts +7 -0
- data/core/ConnectionAdapter.js +2 -0
- data/core/ConnectionManager.d.ts +19 -0
- data/core/ConnectionManager.js +75 -0
- data/core/DataType.d.ts +60 -0
- data/core/DataType.js +2 -0
- data/core/hooksBase.d.ts +29 -0
- data/core/hooksBase.js +80 -0
- data/graph/ArSyncModel.d.ts +10 -0
- data/graph/ArSyncModel.js +22 -0
- data/graph/ArSyncStore.d.ts +28 -0
- data/graph/ArSyncStore.js +593 -0
- data/graph/hooks.d.ts +3 -0
- data/graph/hooks.js +10 -0
- data/graph/index.d.ts +2 -0
- data/graph/index.js +4 -0
- data/lib/ar_sync.rb +25 -0
- data/lib/ar_sync/class_methods.rb +215 -0
- data/lib/ar_sync/collection.rb +83 -0
- data/lib/ar_sync/config.rb +18 -0
- data/lib/ar_sync/core.rb +138 -0
- data/lib/ar_sync/field.rb +96 -0
- data/lib/ar_sync/instance_methods.rb +130 -0
- data/lib/ar_sync/rails.rb +155 -0
- data/lib/ar_sync/type_script.rb +80 -0
- data/lib/ar_sync/version.rb +3 -0
- data/lib/generators/ar_sync/install/install_generator.rb +87 -0
- data/lib/generators/ar_sync/types/types_generator.rb +11 -0
- data/package-lock.json +1115 -0
- data/package.json +19 -0
- data/src/core/ActioncableAdapter.ts +30 -0
- data/src/core/ArSyncApi.ts +75 -0
- data/src/core/ArSyncModelBase.ts +126 -0
- data/src/core/ConnectionAdapter.ts +5 -0
- data/src/core/ConnectionManager.ts +69 -0
- data/src/core/DataType.ts +73 -0
- data/src/core/hooksBase.ts +86 -0
- data/src/graph/ArSyncModel.ts +21 -0
- data/src/graph/ArSyncStore.ts +567 -0
- data/src/graph/hooks.ts +7 -0
- data/src/graph/index.ts +2 -0
- data/src/tree/ArSyncModel.ts +145 -0
- data/src/tree/ArSyncStore.ts +323 -0
- data/src/tree/hooks.ts +7 -0
- data/src/tree/index.ts +2 -0
- data/tree/ArSyncModel.d.ts +39 -0
- data/tree/ArSyncModel.js +143 -0
- data/tree/ArSyncStore.d.ts +21 -0
- data/tree/ArSyncStore.js +365 -0
- data/tree/hooks.d.ts +3 -0
- data/tree/hooks.js +10 -0
- data/tree/index.d.ts +2 -0
- data/tree/index.js +4 -0
- data/tsconfig.json +15 -0
- data/vendor/assets/javascripts/ar_sync_actioncable_adapter.js.erb +7 -0
- data/vendor/assets/javascripts/ar_sync_graph.js.erb +17 -0
- data/vendor/assets/javascripts/ar_sync_tree.js.erb +17 -0
- metadata +187 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
class ArSync::Field
|
2
|
+
attr_reader :name
|
3
|
+
def initialize(name)
|
4
|
+
@name = name
|
5
|
+
end
|
6
|
+
|
7
|
+
def skip_propagation?(_parent, _child, _path)
|
8
|
+
false
|
9
|
+
end
|
10
|
+
|
11
|
+
def action_convert(action)
|
12
|
+
action
|
13
|
+
end
|
14
|
+
|
15
|
+
def order_param; end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ArSync::DataField < ArSync::Field
|
19
|
+
def type
|
20
|
+
:data
|
21
|
+
end
|
22
|
+
|
23
|
+
def data(parent, _child, to_user:, **)
|
24
|
+
ArSync.serialize parent, name, user: to_user
|
25
|
+
end
|
26
|
+
|
27
|
+
def path(_child)
|
28
|
+
[]
|
29
|
+
end
|
30
|
+
|
31
|
+
def action_convert(_action)
|
32
|
+
:update
|
33
|
+
end
|
34
|
+
|
35
|
+
def skip_propagation?(_parent, _child, path)
|
36
|
+
!path.nil?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class ArSync::HasOneField < ArSync::Field
|
41
|
+
def type
|
42
|
+
:one
|
43
|
+
end
|
44
|
+
|
45
|
+
def data(_parent, child, action:, **)
|
46
|
+
child._sync_data new_record: action == :create
|
47
|
+
end
|
48
|
+
|
49
|
+
def path(_child)
|
50
|
+
[name]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class ArSync::HasManyField < ArSync::Field
|
55
|
+
attr_reader :limit, :order, :association, :propagate_when
|
56
|
+
def type
|
57
|
+
:many
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize(name, association: nil, limit: nil, order: nil, propagate_when: nil)
|
61
|
+
super name
|
62
|
+
@limit = limit
|
63
|
+
@order = order
|
64
|
+
@association = association || name
|
65
|
+
@propagate_when = propagate_when
|
66
|
+
end
|
67
|
+
|
68
|
+
def skip_propagation?(parent, child, _path)
|
69
|
+
return false unless limit
|
70
|
+
return !propagate_when.call(child) if propagate_when
|
71
|
+
ids = parent.send(association).order(id: order).limit(limit).ids
|
72
|
+
if child.destroyed?
|
73
|
+
ids.size == limit && (order == :asc ? ids.max < child.id : child.id < ids.min)
|
74
|
+
else
|
75
|
+
!ids.include? child.id
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def data(_parent, child, action:, **)
|
80
|
+
child._sync_data new_record: action == :create
|
81
|
+
end
|
82
|
+
|
83
|
+
def order_param
|
84
|
+
{ limit: limit, order: order } if order
|
85
|
+
end
|
86
|
+
|
87
|
+
def path(child)
|
88
|
+
[name, child.id]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class ArSync::CollectionField < ArSync::HasManyField
|
93
|
+
def path(child)
|
94
|
+
[child.id]
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module ArSync::TreeSync::InstanceMethods
|
2
|
+
def _sync_notify(action)
|
3
|
+
if self.class._sync_self?
|
4
|
+
data = action == :destroy ? nil : _sync_data
|
5
|
+
ArSync.sync_tree_send to: self, action: action, path: [], data: data
|
6
|
+
end
|
7
|
+
_sync_notify_parent action
|
8
|
+
end
|
9
|
+
|
10
|
+
def _sync_data(new_record: false)
|
11
|
+
fallbacks = {}
|
12
|
+
names = []
|
13
|
+
self.class._each_sync_child do |name, info|
|
14
|
+
names << name if info.type == :data
|
15
|
+
if new_record
|
16
|
+
fallbacks[name] = [] if info.type == :many
|
17
|
+
fallbacks[name] = nil if info.type == :one
|
18
|
+
end
|
19
|
+
end
|
20
|
+
data = ArSync.serialize self, names
|
21
|
+
fallbacks.update data
|
22
|
+
end
|
23
|
+
|
24
|
+
def sync_send_event(type:, relation: nil, to_user: nil, data:)
|
25
|
+
path = [*relation]
|
26
|
+
event_data = { type: type, data: data }
|
27
|
+
if self.class._sync_self?
|
28
|
+
ArSync.sync_tree_send to: self, action: :event, path: path, data: event_data, to_user: to_user
|
29
|
+
end
|
30
|
+
_sync_notify_parent(:event, path: path, data: event_data, only_to_user: to_user)
|
31
|
+
end
|
32
|
+
|
33
|
+
def _sync_notify_parent(action, path: nil, data: nil, order_param: nil, only_to_user: nil)
|
34
|
+
self.class._each_sync_parent do |parent, inverse_name:, only_to:|
|
35
|
+
parent = send(parent) if parent.is_a? Symbol
|
36
|
+
parent = instance_exec(&parent) if parent.is_a? Proc
|
37
|
+
next unless parent
|
38
|
+
next if parent.respond_to?(:destroyed?) && parent.destroyed?
|
39
|
+
inverse_name = instance_exec(&inverse_name) if inverse_name.is_a? Proc
|
40
|
+
next unless inverse_name
|
41
|
+
association_field = parent.class._sync_child_info inverse_name
|
42
|
+
next if association_field.skip_propagation? parent, self, path
|
43
|
+
action2 = association_field.action_convert action
|
44
|
+
only_to_user2 = only_to_user
|
45
|
+
if only_to
|
46
|
+
to_user = only_to.is_a?(Symbol) ? instance_eval(&only_to) : instance_exec(&only_to)
|
47
|
+
next unless to_user
|
48
|
+
next if only_to_user && only_to_user != to_user
|
49
|
+
only_to_user2 = to_user
|
50
|
+
end
|
51
|
+
data2 = path || action2 == :destroy ? data : association_field.data(parent, self, to_user: to_user, action: action)
|
52
|
+
order_param2 = path ? order_param : association_field.order_param
|
53
|
+
path2 = [*association_field.path(self), *path]
|
54
|
+
ArSync.sync_tree_send(
|
55
|
+
to: parent, action: action2, path: path2, data: data2,
|
56
|
+
to_user: only_to_user2,
|
57
|
+
ordering: order_param2
|
58
|
+
)
|
59
|
+
parent._sync_notify_parent action2, path: path2, data: data2, order_param: order_param2, only_to_user: to_user || only_to_user
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module ArSync::GraphSync::InstanceMethods
|
65
|
+
def _sync_notify(action)
|
66
|
+
_sync_notify_parent action
|
67
|
+
_sync_notify_self if self.class._sync_self? && action == :update
|
68
|
+
end
|
69
|
+
|
70
|
+
def _sync_current_parents_info
|
71
|
+
parents = []
|
72
|
+
self.class._each_sync_parent do |parent, inverse_name:, only_to:|
|
73
|
+
parent = send parent if parent.is_a? Symbol
|
74
|
+
parent = instance_exec(&parent) if parent.is_a? Proc
|
75
|
+
if only_to
|
76
|
+
to_user = only_to.is_a?(Symbol) ? instance_eval(&only_to) : instance_exec(&only_to)
|
77
|
+
parent = nil unless to_user
|
78
|
+
end
|
79
|
+
owned = parent.class._sync_child_info(inverse_name).present? if parent
|
80
|
+
parents << [parent, [inverse_name, to_user, owned]]
|
81
|
+
end
|
82
|
+
parents
|
83
|
+
end
|
84
|
+
|
85
|
+
def _sync_notify_parent(action)
|
86
|
+
if action == :create
|
87
|
+
parents = _sync_current_parents_info
|
88
|
+
parents_was = parents.map { nil }
|
89
|
+
elsif action == :destroy
|
90
|
+
parents_was = _sync_parents_info_before_mutation
|
91
|
+
parents = parents_was.map { nil }
|
92
|
+
else
|
93
|
+
parents_was = _sync_parents_info_before_mutation
|
94
|
+
parents = _sync_current_parents_info
|
95
|
+
end
|
96
|
+
parents_was.zip(parents).each do |(parent_was, info_was), (parent, info)|
|
97
|
+
if parent_was == parent && info_was == info
|
98
|
+
parent&._sync_notify_child_changed self, *info
|
99
|
+
else
|
100
|
+
parent_was&._sync_notify_child_removed self, *info_was
|
101
|
+
parent&._sync_notify_child_added self, *info
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def _sync_notify_child_removed(child, name, to_user, owned)
|
107
|
+
if owned
|
108
|
+
ArSync.sync_graph_send to: self, action: :remove, model: child, path: name, to_user: to_user
|
109
|
+
else
|
110
|
+
ArSync.sync_graph_send to: self, action: :update, model: self, to_user: to_user
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def _sync_notify_child_added(child, name, to_user, owned)
|
115
|
+
if owned
|
116
|
+
ArSync.sync_graph_send to: self, action: :add, model: child, path: name, to_user: to_user
|
117
|
+
else
|
118
|
+
ArSync.sync_graph_send to: self, action: :update, model: self, to_user: to_user
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def _sync_notify_child_changed(_child, _name, to_user, owned)
|
123
|
+
return if owned
|
124
|
+
ArSync.sync_graph_send(to: self, action: :update, model: self, to_user: to_user)
|
125
|
+
end
|
126
|
+
|
127
|
+
def _sync_notify_self
|
128
|
+
ArSync.sync_graph_send(to: self, action: :update, model: self)
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module ArSync
|
2
|
+
module Rails
|
3
|
+
class Engine < ::Rails::Engine; end
|
4
|
+
end
|
5
|
+
|
6
|
+
class ApiNotFound < StandardError; end
|
7
|
+
|
8
|
+
on_notification do |events|
|
9
|
+
events.each do |key, patch|
|
10
|
+
ActionCable.server.broadcast key, patch
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module StaticJsonConcern
|
15
|
+
def ar_sync_static_json(record_or_records, query)
|
16
|
+
if respond_to?(ArSync.config.current_user_method)
|
17
|
+
current_user = send ArSync.config.current_user_method
|
18
|
+
end
|
19
|
+
ArSync.serialize(record_or_records, query.as_json, user: current_user)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ActionController::Base.class_eval do
|
24
|
+
include StaticJsonConcern
|
25
|
+
def action_with_compact_ar_sync_notification(&block)
|
26
|
+
ArSync.with_compact_notification(&block)
|
27
|
+
end
|
28
|
+
around_action :action_with_compact_ar_sync_notification
|
29
|
+
end
|
30
|
+
|
31
|
+
module ApiControllerConcern
|
32
|
+
extend ActiveSupport::Concern
|
33
|
+
include ArSerializer::Serializable
|
34
|
+
|
35
|
+
included do
|
36
|
+
protect_from_forgery except: %i[sync_call static_call graphql_call]
|
37
|
+
serializer_field :__schema do
|
38
|
+
ArSerializer::GraphQL::SchemaClass.new self.class
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def sync_call
|
43
|
+
_api_call :sync do |model, current_user, query|
|
44
|
+
case model
|
45
|
+
when ArSync::Collection::Graph, ArSync::GraphSync
|
46
|
+
serialized = ArSerializer.serialize model, query, context: current_user, include_id: true, use: :sync
|
47
|
+
next serialized if model.is_a? ArSync::GraphSync
|
48
|
+
{
|
49
|
+
sync_keys: ArSync.sync_graph_keys(model, current_user),
|
50
|
+
order: { mode: model.order, limit: model.limit },
|
51
|
+
collection: serialized
|
52
|
+
}
|
53
|
+
when ArSync::Collection::Tree
|
54
|
+
ArSync.sync_collection_api model, current_user, query
|
55
|
+
when ActiveRecord::Relation, Array
|
56
|
+
ArSync.serialize model.to_a, query, user: current_user
|
57
|
+
when ActiveRecord::Base
|
58
|
+
ArSync.sync_api model, current_user, query
|
59
|
+
else
|
60
|
+
model
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def graphql_schema
|
66
|
+
render plain: ArSerializer::GraphQL.definition(self.class)
|
67
|
+
end
|
68
|
+
|
69
|
+
def graphql_call
|
70
|
+
render json: ArSerializer::GraphQL.serialize(
|
71
|
+
self,
|
72
|
+
params[:query],
|
73
|
+
operation_name: params[:operationName],
|
74
|
+
variables: (params[:variables] || {}).as_json,
|
75
|
+
context: current_user
|
76
|
+
)
|
77
|
+
rescue StandardError => e
|
78
|
+
render json: { error: handle_exception(e) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def static_call
|
82
|
+
_api_call :static do |model, current_user, query|
|
83
|
+
case model
|
84
|
+
when ArSync::Collection, ActiveRecord::Relation, Array
|
85
|
+
ArSerializer.serialize model.to_a, query, context: current_user
|
86
|
+
when ActiveRecord::Base
|
87
|
+
ArSerializer.serialize model, query, context: current_user
|
88
|
+
else
|
89
|
+
model
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def _api_call(type)
|
97
|
+
if respond_to?(ArSync.config.current_user_method)
|
98
|
+
current_user = send ArSync.config.current_user_method
|
99
|
+
end
|
100
|
+
responses = params[:requests].map do |request|
|
101
|
+
begin
|
102
|
+
api_name = request[:api]
|
103
|
+
info = self.class._serializer_field_info api_name
|
104
|
+
raise ArSync::ApiNotFound, "#{type.to_s.capitalize} API named `#{api_name}` not configured" unless info
|
105
|
+
api_params = (request[:params].as_json || {}).transform_keys(&:to_sym)
|
106
|
+
model = instance_exec(current_user, api_params, &info.data_block)
|
107
|
+
{ data: yield(model, current_user, request[:query].as_json) }
|
108
|
+
rescue StandardError => e
|
109
|
+
{ error: handle_exception(e) }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
render json: responses
|
113
|
+
end
|
114
|
+
|
115
|
+
def log_internal_exception_trace(trace)
|
116
|
+
if logger.formatter&.respond_to? :tags_text
|
117
|
+
logger.fatal trace.join("\n#{logger.formatter.tags_text}")
|
118
|
+
else
|
119
|
+
logger.fatal trace.join("\n")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def exception_trace(exception)
|
124
|
+
backtrace_cleaner = request.get_header 'action_dispatch.backtrace_cleaner'
|
125
|
+
wrapper = ActionDispatch::ExceptionWrapper.new backtrace_cleaner, exception
|
126
|
+
trace = wrapper.application_trace
|
127
|
+
trace.empty? ? wrapper.framework_trace : trace
|
128
|
+
end
|
129
|
+
|
130
|
+
def log_internal_exception(exception)
|
131
|
+
ActiveSupport::Deprecation.silence do
|
132
|
+
logger.fatal ' '
|
133
|
+
logger.fatal "#{exception.class} (#{exception.message}):"
|
134
|
+
log_internal_exception_trace exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
|
135
|
+
logger.fatal ' '
|
136
|
+
log_internal_exception_trace exception_trace(exception)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def handle_exception(exception)
|
141
|
+
log_internal_exception exception
|
142
|
+
backtrace = exception_trace exception unless ::Rails.env.production?
|
143
|
+
case exception
|
144
|
+
when ArSerializer::InvalidQuery, ArSync::ApiNotFound, ArSerializer::GraphQL::Parser::ParseError
|
145
|
+
{ type: 'Bad Request', message: exception.message, backtrace: backtrace }
|
146
|
+
when ActiveRecord::RecordNotFound
|
147
|
+
message = exception.message unless ::Rails.env.production?
|
148
|
+
{ type: 'Record Not Found', message: message.to_s, backtrace: backtrace }
|
149
|
+
else
|
150
|
+
message = "#{exception.class} (#{exception.message})" unless ::Rails.env.production?
|
151
|
+
{ type: 'Internal Server Error', message: message.to_s, backtrace: backtrace }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module ArSync::TypeScript
|
2
|
+
def self.generate_typed_files(api_class, dir:, mode: nil, comment: nil)
|
3
|
+
mode ||= :graph if ActiveRecord::Base.include? ArSync::GraphSync
|
4
|
+
mode ||= :tree if ActiveRecord::Base.include? ArSync::TreeSync
|
5
|
+
raise 'ar_sync mode: graph or tree, is not specified.' unless mode
|
6
|
+
{
|
7
|
+
'types.ts' => generate_type_definition(api_class),
|
8
|
+
'ArSyncModel.ts' => generate_model_script(mode),
|
9
|
+
'hooks.ts' => generate_hooks_script(mode)
|
10
|
+
}.each { |file, code| File.write File.join(dir, file), "#{comment}#{code}" }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.generate_type_definition(api_class)
|
14
|
+
[
|
15
|
+
ArSerializer::TypeScript.generate_type_definition(api_related_classes(api_class)),
|
16
|
+
request_type_definition(api_class)
|
17
|
+
].join "\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.api_related_classes(api_class)
|
21
|
+
classes = ArSerializer::TypeScript.related_serializer_types([api_class]).map(&:type)
|
22
|
+
classes - [api_class]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.request_type_definition(api_class)
|
26
|
+
type = ArSerializer::GraphQL::TypeClass.from api_class
|
27
|
+
definitions = []
|
28
|
+
request_types = {}
|
29
|
+
type.fields.each do |field|
|
30
|
+
association_type = field.type.association_type
|
31
|
+
next unless association_type
|
32
|
+
prefix = 'Class' if field.name.match?(/\A[A-Z]/) # for class reload query
|
33
|
+
request_type_name = "Type#{prefix}#{field.name.camelize}Request"
|
34
|
+
request_types[field.name] = request_type_name
|
35
|
+
multiple = field.type.is_a? ArSerializer::GraphQL::ListTypeClass
|
36
|
+
definitions << <<~CODE
|
37
|
+
export interface #{request_type_name} {
|
38
|
+
api: '#{field.name}'
|
39
|
+
params?: #{field.args_ts_type}
|
40
|
+
query: Type#{association_type.name}Query
|
41
|
+
_meta?: { data: Type#{field.type.association_type.name}#{'[]' if multiple} }
|
42
|
+
}
|
43
|
+
CODE
|
44
|
+
end
|
45
|
+
[
|
46
|
+
'export type TypeRequest = ',
|
47
|
+
request_types.values.map { |value| " | #{value}" },
|
48
|
+
'export type ApiNameRequests = {',
|
49
|
+
request_types.map { |key, value| " #{key}: #{value}" },
|
50
|
+
'}',
|
51
|
+
definitions
|
52
|
+
].join("\n")
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.generate_model_script(mode)
|
56
|
+
<<~CODE
|
57
|
+
import { TypeRequest, ApiNameRequests } from './types'
|
58
|
+
import { DataTypeFromRequest } from 'ar_sync/core/DataType'
|
59
|
+
import ArSyncModelBase from 'ar_sync/#{mode}/ArSyncModel'
|
60
|
+
export default class ArSyncModel<R extends TypeRequest> extends ArSyncModelBase<{}> {
|
61
|
+
constructor(r: R) { super(r) }
|
62
|
+
data: DataTypeFromRequest<ApiNameRequests[R['api']], R> | null
|
63
|
+
}
|
64
|
+
CODE
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.generate_hooks_script(mode)
|
68
|
+
<<~CODE
|
69
|
+
import { TypeRequest, ApiNameRequests } from './types'
|
70
|
+
import { DataTypeFromRequest } from 'ar_sync/core/DataType'
|
71
|
+
import { useArSyncModel as useArSyncModelBase, useArSyncFetch as useArSyncFetchBase } from 'ar_sync/#{mode}/hooks'
|
72
|
+
export function useArSyncModel<R extends TypeRequest>(request: R | null) {
|
73
|
+
return useArSyncModelBase<DataTypeFromRequest<ApiNameRequests[R['api']], R>>(request)
|
74
|
+
}
|
75
|
+
export function useArSyncFetch<R extends TypeRequest>(request: R | null) {
|
76
|
+
return useArSyncFetchBase<DataTypeFromRequest<ApiNameRequests[R['api']], R>>(request)
|
77
|
+
}
|
78
|
+
CODE
|
79
|
+
end
|
80
|
+
end
|