syncable_models 0.0.11 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +1 -1
- data/Rakefile +4 -14
- data/app/models/syncable_models/sync.rb +22 -0
- data/db/migrate/20160225141153_create_syncs.rb +5 -1
- data/lib/syncable_models/active_record.rb +20 -7
- data/lib/syncable_models/controller.rb +25 -6
- data/lib/syncable_models/engine.rb +5 -0
- data/lib/syncable_models/importer.rb +54 -23
- data/lib/syncable_models/syncable.rb +20 -0
- data/lib/syncable_models/version.rb +1 -1
- data/lib/syncable_models.rb +1 -0
- data/lib/tasks/syncable_models.rake +2 -2
- data/spec/controllers/syncable_models/import_api_controller_spec.rb +101 -0
- data/{test → spec}/dummy/README.rdoc +0 -0
- data/{test → spec}/dummy/Rakefile +0 -0
- data/{test → spec}/dummy/app/assets/javascripts/application.js +0 -0
- data/{test → spec}/dummy/app/assets/stylesheets/application.css +0 -0
- data/{test → spec}/dummy/app/controllers/application_controller.rb +0 -0
- data/spec/dummy/app/controllers/import_api_controller.rb +6 -0
- data/{test → spec}/dummy/app/helpers/application_helper.rb +0 -0
- data/spec/dummy/app/models/project.rb +12 -0
- data/spec/dummy/app/models/team.rb +12 -0
- data/{test → spec}/dummy/app/views/layouts/application.html.erb +0 -0
- data/{test → spec}/dummy/bin/bundle +0 -0
- data/{test → spec}/dummy/bin/rails +0 -0
- data/{test → spec}/dummy/bin/rake +0 -0
- data/{test → spec}/dummy/bin/setup +0 -0
- data/{test → spec}/dummy/config/application.rb +7 -1
- data/{test → spec}/dummy/config/boot.rb +0 -0
- data/{test → spec}/dummy/config/database.yml +0 -0
- data/{test → spec}/dummy/config/environment.rb +0 -0
- data/{test → spec}/dummy/config/environments/development.rb +0 -0
- data/{test → spec}/dummy/config/environments/production.rb +0 -0
- data/{test → spec}/dummy/config/environments/test.rb +0 -0
- data/{test → spec}/dummy/config/initializers/assets.rb +0 -0
- data/{test → spec}/dummy/config/initializers/backtrace_silencers.rb +0 -0
- data/{test → spec}/dummy/config/initializers/cookies_serializer.rb +0 -0
- data/{test → spec}/dummy/config/initializers/filter_parameter_logging.rb +0 -0
- data/{test → spec}/dummy/config/initializers/inflections.rb +0 -0
- data/{test → spec}/dummy/config/initializers/mime_types.rb +0 -0
- data/{test → spec}/dummy/config/initializers/session_store.rb +0 -0
- data/{test → spec}/dummy/config/initializers/wrap_parameters.rb +0 -0
- data/{test → spec}/dummy/config/locales/en.yml +0 -0
- data/spec/dummy/config/routes.rb +11 -0
- data/{test → spec}/dummy/config/secrets.yml +0 -0
- data/{test → spec}/dummy/config.ru +0 -0
- data/spec/dummy/db/migrate/20160327104327_create_projects.rb +13 -0
- data/spec/dummy/db/migrate/20160327111225_create_teams.rb +9 -0
- data/spec/dummy/db/migrate/20160327154807_create_syncs.syncable_models.rb +19 -0
- data/{test → spec}/dummy/db/schema.rb +23 -2
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/test.log +819 -0
- data/{test → spec}/dummy/public/404.html +0 -0
- data/{test → spec}/dummy/public/422.html +0 -0
- data/{test → spec}/dummy/public/500.html +0 -0
- data/{test → spec}/dummy/public/favicon.ico +0 -0
- data/spec/dummy/spec/factories/projects.rb +21 -0
- data/spec/dummy/spec/factories/teams.rb +17 -0
- data/spec/models/syncable_models/importer/import_spec.rb +104 -0
- data/spec/models/syncable_models/sync_spec.rb +106 -0
- data/spec/rails_helper.rb +19 -0
- data/spec/spec_helper.rb +85 -0
- data/{test → spec}/syncable_models_test.rb +0 -0
- metadata +106 -80
- data/test/dummy/config/routes.rb +0 -4
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +0 -22
- data/test/test_helper.rb +0 -21
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NmFjMzY5ODRkYmFmOGMwNzdiNWQ4YWFlOTNmZjQ0ZmJmYjg1YTk3MA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
Mzk4OTVkM2EwNTFiYzI1M2ExYTgwMzMwMjgwOTcwZDFhMmE3MjZmMA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MWQ4ODJiN2UyYjZkY2IzYTEzOWIwZjJkNmEwZTcwYzMxODk1NjQ4NzMzNGEx
|
10
|
+
NmYzM2M1MzMwM2IxNmEwMDJjMGE1ZjNhOGMwZDVhMDRlYzkyNDEyOWQ2MWIx
|
11
|
+
ZjQwOTgzNTJhMjU1MDU5YjI4MjNkZGY3YjQwNTBkNDY5MGFjZmQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
Y2I2OGNkNzNlMDExMTEyNGRkYWRlNzVmNjVkOTVmMGIwMDkxYjZiNzdiM2Mx
|
14
|
+
YzI5ZWU1MzQ1ZDUzMzQ3OThjMmE1NDkyY2Y1MjgwYzY5NzdiZjAyMTg5Y2Q1
|
15
|
+
MjRmYWJlYWRmN2FmOGM5Mjg2YzllODRhZjliNDU0Yjg0ZTk3MmY=
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# SyncableModels [![Build Status](https://travis-ci.org/flant/syncable_models.svg?branch=master)](https://travis-ci.org/flant/
|
1
|
+
# SyncableModels [![Build Status](https://travis-ci.org/flant/syncable_models.svg?branch=master)](https://travis-ci.org/flant/syncable_models) [![Gem Version](https://badge.fury.io/rb/syncable_models.svg)](https://badge.fury.io/rb/syncable_models)
|
2
2
|
|
3
3
|
This gem helps you to:
|
4
4
|
|
data/Rakefile
CHANGED
@@ -4,23 +4,13 @@ rescue LoadError
|
|
4
4
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
5
|
end
|
6
6
|
|
7
|
-
|
7
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
8
8
|
|
9
|
-
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
10
9
|
load 'rails/tasks/engine.rake'
|
11
|
-
|
12
|
-
|
13
10
|
load 'rails/tasks/statistics.rake'
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
require 'rake/testtask'
|
12
|
+
RSpec::Core::RakeTask.new(:spec)
|
18
13
|
|
19
|
-
|
20
|
-
t.libs << 'lib'
|
21
|
-
t.libs << 'test'
|
22
|
-
t.pattern = 'test/**/*_test.rb'
|
23
|
-
t.verbose = false
|
24
|
-
end
|
14
|
+
task :default => :spec
|
25
15
|
|
26
|
-
|
16
|
+
Bundler::GemHelper.install_tasks
|
@@ -7,5 +7,27 @@ module SyncableModels
|
|
7
7
|
validates :subject, :subject_type, :destination, presence: true
|
8
8
|
|
9
9
|
scope :by_destination, ->(*d) { where(destination: d) }
|
10
|
+
scope :for_destroying, ->{ where(subject_destroyed: true) }
|
11
|
+
|
12
|
+
before_save do
|
13
|
+
self.subject_external_id = subject.syncable_models_external_id
|
14
|
+
end
|
15
|
+
|
16
|
+
def subject
|
17
|
+
super || begin
|
18
|
+
subject_klass = subject_type.constantize
|
19
|
+
subject_klass.where(subject_klass.syncable_models_id_key => subject_external_id).first
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def sync_destruction!
|
24
|
+
if subject
|
25
|
+
self.subject = subject
|
26
|
+
self.subject_destroyed = false
|
27
|
+
save!
|
28
|
+
else
|
29
|
+
destroy
|
30
|
+
end
|
31
|
+
end
|
10
32
|
end
|
11
33
|
end
|
@@ -3,12 +3,16 @@ class CreateSyncs < ActiveRecord::Migration
|
|
3
3
|
create_table :syncs do |t|
|
4
4
|
t.integer :subject_id
|
5
5
|
t.string :subject_type
|
6
|
-
t.string :destination
|
6
|
+
t.string :destination, null: false
|
7
|
+
|
8
|
+
t.string :subject_external_id, null: false
|
9
|
+
t.boolean :subject_destroyed, null: false, default: false
|
7
10
|
|
8
11
|
t.timestamps
|
9
12
|
end
|
10
13
|
|
11
14
|
add_index :syncs, [:subject_id, :subject_type, :destination]
|
12
15
|
add_index :syncs, :destination
|
16
|
+
add_index :syncs, [:subject_external_id, :subject_destroyed]
|
13
17
|
end
|
14
18
|
end
|
@@ -2,9 +2,14 @@ module SyncableModels::ActiveRecord
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
included do
|
5
|
-
has_many :syncs,
|
5
|
+
has_many :syncs,
|
6
|
+
as: :subject,
|
7
|
+
inverse_of: :subject,
|
8
|
+
class_name: ::SyncableModels::Sync,
|
9
|
+
dependent: :nullify
|
6
10
|
|
7
11
|
after_update :clear_syncs_on_update
|
12
|
+
after_destroy :set_syncs_destroyed
|
8
13
|
|
9
14
|
scope :synced, ->(d){ joins(:syncs).where(syncs: { destination: d }) }
|
10
15
|
scope :not_synced, ->(d) do
|
@@ -19,23 +24,31 @@ module SyncableModels::ActiveRecord
|
|
19
24
|
end
|
20
25
|
|
21
26
|
def synced?(destination)
|
22
|
-
|
27
|
+
syncs.where(destination: destination).exists?
|
23
28
|
end
|
24
29
|
|
25
30
|
def sync(destination)
|
26
|
-
|
31
|
+
syncs.create(destination: destination) unless synced?(destination)
|
27
32
|
end
|
28
33
|
|
29
34
|
def clear_syncs(destination = nil)
|
30
|
-
|
31
|
-
|
35
|
+
(destination ? syncs.by_destination(destination) : syncs).destroy_all
|
36
|
+
end
|
37
|
+
|
38
|
+
def syncable_models_external_id
|
39
|
+
self.send(self.class.syncable_models_id_key)
|
32
40
|
end
|
33
41
|
|
34
42
|
def clear_syncs_on_update
|
35
|
-
|
43
|
+
clear_syncs
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_syncs_destroyed
|
47
|
+
SyncableModels::Sync.where(subject_external_id: self.syncable_models_external_id)
|
48
|
+
.update_all(subject_destroyed: true)
|
36
49
|
end
|
37
50
|
|
38
51
|
def to_import_hash
|
39
|
-
|
52
|
+
attributes
|
40
53
|
end
|
41
54
|
end
|
@@ -4,7 +4,7 @@ module SyncableModels::Controller
|
|
4
4
|
BATCH_COUNT = 50
|
5
5
|
|
6
6
|
module ClassMethods
|
7
|
-
def sync_for(subject, class_name: nil, sync_method: nil, fetch_method: nil)
|
7
|
+
def sync_for(subject, id_key: :uuid, class_name: nil, sync_method: nil, fetch_method: nil)
|
8
8
|
klass = class_name || subject.to_s.singularize.camelize
|
9
9
|
klass = klass.constantize unless klass.is_a?(Class)
|
10
10
|
|
@@ -13,7 +13,7 @@ module SyncableModels::Controller
|
|
13
13
|
|
14
14
|
class_eval """
|
15
15
|
def #{sync_method}
|
16
|
-
set_synced #{klass.name}
|
16
|
+
set_synced #{klass.name}, :#{id_key.to_s}
|
17
17
|
end
|
18
18
|
|
19
19
|
def #{fetch_method}
|
@@ -31,16 +31,35 @@ module SyncableModels::Controller
|
|
31
31
|
def fetch(klass)
|
32
32
|
if params[:destination]
|
33
33
|
count = params[:count].present? ? params[:count].to_i : BATCH_COUNT
|
34
|
-
|
35
|
-
|
34
|
+
|
35
|
+
for_sync = klass.not_synced(params[:destination]).limit(count).map(&:to_import_hash)
|
36
|
+
count = count - for_sync.count
|
37
|
+
for_destroy = SyncableModels::Sync
|
38
|
+
.by_destination(params[:destination])
|
39
|
+
.for_destroying
|
40
|
+
.limit(count)
|
41
|
+
.map(&:subject_external_id)
|
42
|
+
|
43
|
+
render json: { status: 200, for_sync: for_sync, for_destroy: for_destroy }
|
36
44
|
else
|
37
45
|
render_argument_error
|
38
46
|
end
|
39
47
|
end
|
40
48
|
|
41
|
-
def set_synced(klass, id_key
|
49
|
+
def set_synced(klass, id_key)
|
42
50
|
if params[:ids] && params[:destination]
|
43
|
-
|
51
|
+
ids = params[:ids]
|
52
|
+
|
53
|
+
destruction_syncs = SyncableModels::Sync
|
54
|
+
.by_destination(params[:destination])
|
55
|
+
.for_destroying
|
56
|
+
.where(subject_external_id: ids)
|
57
|
+
|
58
|
+
destruction_sync_ids = destruction_syncs.pluck(:subject_external_id).map(&:to_s)
|
59
|
+
destruction_syncs.each{ |s| s.sync_destruction! }
|
60
|
+
ids -= destruction_sync_ids
|
61
|
+
|
62
|
+
klass.where(id_key => ids).sync(params[:destination])
|
44
63
|
render json: { status: 200 }
|
45
64
|
else
|
46
65
|
render_argument_error
|
@@ -36,45 +36,76 @@ module SyncableModels
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def import(model_names=[])
|
39
|
+
model_names = [model_names] unless model_names.is_a?(Array)
|
40
|
+
model_names.map!(&:to_s)
|
39
41
|
selected_models = model_names.any? ?
|
40
42
|
@models.select{ |k, v| k.in? model_names } :
|
41
43
|
@models
|
42
44
|
|
43
45
|
selected_models.each do |model_name, params|
|
44
46
|
puts "[SyncableModels::Importer] Importing #{model_name.underscore.pluralize}..."
|
45
|
-
next if model_names.any? && !model_name.in?(model_names)
|
46
47
|
|
47
|
-
|
48
|
-
sync_url = self.api_url + params[:sync_path]
|
48
|
+
next if model_names.any? && !model_name.in?(model_names)
|
49
49
|
|
50
|
-
|
51
|
-
response = conn.get '', params_with_api_key(destination: destination)
|
50
|
+
response = fetch_request(params)
|
52
51
|
|
53
52
|
if response.success?
|
54
53
|
response = JSON.parse(response.body)
|
55
|
-
|
56
54
|
if response["status"].to_i == 401
|
57
55
|
puts "[SyncableModels::Importer] Wrong api key!"
|
58
56
|
end
|
59
57
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
58
|
+
synced_ids = []
|
59
|
+
|
60
|
+
synced_ids += sync_update model_name, params, response['for_sync']
|
61
|
+
synced_ids += sync_destroy model_name, response['for_destroy']
|
62
|
+
|
63
|
+
sync_request(params, synced_ids) if synced_ids.any?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def fetch_request(params)
|
69
|
+
conn = Faraday.new(url: api_url + params[:fetch_path])
|
70
|
+
conn.get '', params_with_api_key(destination: destination)
|
71
|
+
end
|
72
|
+
|
73
|
+
def sync_request(params, ids)
|
74
|
+
conn = Faraday.new(url: api_url + params[:sync_path])
|
75
|
+
conn.get '', params_with_api_key(destination: destination, ids: ids)
|
76
|
+
end
|
77
|
+
|
78
|
+
def sync_update(model_name, params, objects)
|
79
|
+
synced_ids = []
|
80
|
+
|
81
|
+
if objects
|
82
|
+
klass = model_name.constantize
|
83
|
+
|
84
|
+
objects.each do |o|
|
85
|
+
id = o[params[:id_key].to_s]
|
86
|
+
result = klass.from_import_hash(o)
|
87
|
+
puts "[SyncableModels::Importer] Importing #{model_name} (external_id=#{id}): #{ result ? 'OK' : 'FAIL' }"
|
88
|
+
synced_ids << id if result
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
synced_ids
|
93
|
+
end
|
94
|
+
|
95
|
+
def sync_destroy(model_name, ids)
|
96
|
+
synced_ids = []
|
97
|
+
|
98
|
+
if ids
|
99
|
+
klass = model_name.constantize
|
100
|
+
|
101
|
+
ids.each do |id|
|
102
|
+
result = klass.where(external_id: id).first.try(:destroy)
|
103
|
+
puts "[SyncableModels::Importer] Destroying #{model_name} (external_id=#{id}): #{ result ? 'OK' : 'FAIL' }"
|
104
|
+
synced_ids << id if result
|
76
105
|
end
|
77
106
|
end
|
107
|
+
|
108
|
+
synced_ids
|
78
109
|
end
|
79
110
|
end
|
80
111
|
|
@@ -96,7 +127,7 @@ module SyncableModels
|
|
96
127
|
end
|
97
128
|
|
98
129
|
def self.import_all
|
99
|
-
@@imports.each
|
130
|
+
@@imports.each(&:import)
|
100
131
|
end
|
101
132
|
end
|
102
133
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
module SyncableModels
|
4
|
+
module Syncable
|
5
|
+
def syncable(options = {})
|
6
|
+
options.assert_valid_keys(
|
7
|
+
:id_key
|
8
|
+
)
|
9
|
+
|
10
|
+
class_attribute :syncable_models_id_key
|
11
|
+
self.syncable_models_id_key = options[:id_key] || :uuid
|
12
|
+
|
13
|
+
include SyncableModels::ActiveRecord
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
ActiveSupport.on_load :active_record do
|
19
|
+
ActiveRecord::Base.send :extend, SyncableModels::Syncable
|
20
|
+
end
|
data/lib/syncable_models.rb
CHANGED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe ImportApiController, type: :controller do
|
4
|
+
let(:json) { JSON.parse(response.body) }
|
5
|
+
let(:for_sync) { json['for_sync'] }
|
6
|
+
let(:for_destroy) { json['for_destroy'] }
|
7
|
+
|
8
|
+
describe '#fetch method' do
|
9
|
+
before do
|
10
|
+
@teams = [create(:first_team),
|
11
|
+
create(:second_team),
|
12
|
+
create(:third_team),
|
13
|
+
create(:fourth_team)]
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'fetches objects if all unsynced' do
|
17
|
+
get :teams, destination: :test
|
18
|
+
expect(for_sync.count).to eq(4)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'fetches only unisynced objects' do
|
22
|
+
@teams.last.sync(:test)
|
23
|
+
get :teams, destination: :test
|
24
|
+
expect(for_sync.count).to eq(3)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'fetching mixed response' do
|
28
|
+
before do
|
29
|
+
@teams.last(2).each{ |t| t.sync(:test) }
|
30
|
+
@teams.last(2).each(&:destroy)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'fetches destroyed syncs' do
|
34
|
+
get :teams, destination: :test
|
35
|
+
expect(for_destroy.count).to eq(2)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'response still has undestroyed syncs' do
|
39
|
+
get :teams, destination: :test
|
40
|
+
expect(for_sync.count).to eq(2)
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'when using limit' do
|
44
|
+
it 'returns correct amount of for_sync objects' do
|
45
|
+
get :teams, destination: :test, count: 3
|
46
|
+
expect(for_sync.count).to eq(2)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns correct amount of for_destroy objects' do
|
50
|
+
get :teams, destination: :test, count: 3
|
51
|
+
expect(for_destroy.count).to eq(1)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#sync method' do
|
58
|
+
it 'syncs objects' do
|
59
|
+
teams = [create(:first_team), create(:second_team), create(:third_team)]
|
60
|
+
get :sync_teams, destination: :test, ids: teams.first(2).map(&:id)
|
61
|
+
expect(Team.synced(:test)).to eq(teams.first(2))
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'when objects destroyed' do
|
65
|
+
describe 'and they are non-permanent' do
|
66
|
+
before do
|
67
|
+
@teams = [create(:first_team), create(:second_team), create(:third_team)]
|
68
|
+
@teams.each{ |o| o.sync(:test) }
|
69
|
+
@teams.first(2).each(&:destroy)
|
70
|
+
get :sync_teams, destination: :test, ids: @teams.first(2).map(&:id)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'destroys their syncs' do
|
74
|
+
expect(SyncableModels::Sync.count).to eq(1)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'keeps the remaining syncs' do
|
78
|
+
expect(SyncableModels::Sync.first.subject).to eq(@teams.last)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'and they are permanent' do
|
83
|
+
before do
|
84
|
+
@projects = [create(:first_project), create(:second_project), create(:third_project)]
|
85
|
+
@projects.each{ |o| o.sync(:test) }
|
86
|
+
@projects.first(2).each(&:destroy)
|
87
|
+
get :sync_projects, destination: :test, ids: @projects.first(2).map(&:uuid)
|
88
|
+
@projects.each(&:reload)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'doesn\'t destroy their syncs' do
|
92
|
+
expect(SyncableModels::Sync.count).to eq(3)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'makes syncs of destroyed object with correct subject_destroyed attribute' do
|
96
|
+
expect(@projects.first(2).map(&:syncs).flatten.compact.map(&:subject_destroyed)).to eq([false, false])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Project < ActiveRecord::Base
|
2
|
+
syncable
|
3
|
+
|
4
|
+
validates :name, :uuid, presence: true
|
5
|
+
|
6
|
+
def self.from_import_hash(hash)
|
7
|
+
object = Project.where(external_id: hash['uuid']).first || Project.new(uuid: SecureRandom.uuid, external_id: hash['uuid'])
|
8
|
+
object.name = hash['name']
|
9
|
+
object.external_id = hash['uuid']
|
10
|
+
object.save
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Team < ActiveRecord::Base
|
2
|
+
syncable id_key: :id
|
3
|
+
|
4
|
+
validates :name, presence: true
|
5
|
+
|
6
|
+
def self.from_import_hash(hash)
|
7
|
+
object = Team.where(external_id: hash['id']).first || Team.new
|
8
|
+
object.name = hash['name']
|
9
|
+
object.external_id = hash['id']
|
10
|
+
object.save
|
11
|
+
end
|
12
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,6 +1,12 @@
|
|
1
1
|
require File.expand_path('../boot', __FILE__)
|
2
2
|
|
3
|
-
|
3
|
+
# Pick the frameworks you want:
|
4
|
+
require "active_record/railtie"
|
5
|
+
require "action_controller/railtie"
|
6
|
+
require "action_mailer/railtie"
|
7
|
+
require "action_view/railtie"
|
8
|
+
require "sprockets/railtie"
|
9
|
+
# require "rails/test_unit/railtie"
|
4
10
|
|
5
11
|
Bundler.require(*Rails.groups)
|
6
12
|
require "syncable_models"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateProjects < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :projects do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.string :uuid, null: false
|
6
|
+
t.datetime :deleted_at, null: true
|
7
|
+
t.string :external_id
|
8
|
+
t.timestamps null: false
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :projects, :uuid, unique: true, length: 36
|
12
|
+
end
|
13
|
+
end
|