syncable_models 0.0.11 → 0.0.12
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.
- 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 [](https://travis-ci.org/flant/
|
1
|
+
# SyncableModels [](https://travis-ci.org/flant/syncable_models) [](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
|