draisine 0.7.10
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/Guardfile +70 -0
- data/LICENSE.txt +21 -0
- data/README.md +134 -0
- data/Rakefile +6 -0
- data/app/controllers/draisine/soap_controller.rb +49 -0
- data/bin/console +7 -0
- data/bin/setup +6 -0
- data/config/routes.rb +4 -0
- data/draisine.gemspec +32 -0
- data/lib/draisine/active_record.rb +191 -0
- data/lib/draisine/auditor/result.rb +48 -0
- data/lib/draisine/auditor.rb +130 -0
- data/lib/draisine/concerns/array_setter.rb +23 -0
- data/lib/draisine/concerns/attributes_mapping.rb +46 -0
- data/lib/draisine/concerns/import.rb +36 -0
- data/lib/draisine/conflict_detector.rb +38 -0
- data/lib/draisine/conflict_resolver.rb +97 -0
- data/lib/draisine/engine.rb +6 -0
- data/lib/draisine/importer.rb +111 -0
- data/lib/draisine/ip_checker.rb +15 -0
- data/lib/draisine/jobs/inbound_delete_job.rb +8 -0
- data/lib/draisine/jobs/inbound_update_job.rb +8 -0
- data/lib/draisine/jobs/job_base.rb +39 -0
- data/lib/draisine/jobs/outbound_create_job.rb +7 -0
- data/lib/draisine/jobs/outbound_delete_job.rb +7 -0
- data/lib/draisine/jobs/outbound_update_job.rb +7 -0
- data/lib/draisine/jobs/soap_delete_job.rb +7 -0
- data/lib/draisine/jobs/soap_update_job.rb +7 -0
- data/lib/draisine/partitioner.rb +73 -0
- data/lib/draisine/poller.rb +101 -0
- data/lib/draisine/query_mechanisms/base.rb +15 -0
- data/lib/draisine/query_mechanisms/default.rb +13 -0
- data/lib/draisine/query_mechanisms/last_modified_date.rb +18 -0
- data/lib/draisine/query_mechanisms/system_modstamp.rb +18 -0
- data/lib/draisine/query_mechanisms.rb +18 -0
- data/lib/draisine/registry.rb +22 -0
- data/lib/draisine/setup.rb +97 -0
- data/lib/draisine/soap_handler.rb +79 -0
- data/lib/draisine/syncer.rb +52 -0
- data/lib/draisine/type_mapper.rb +105 -0
- data/lib/draisine/util/caching_client.rb +73 -0
- data/lib/draisine/util/hash_diff.rb +39 -0
- data/lib/draisine/util/parse_time.rb +14 -0
- data/lib/draisine/util/salesforce_comparisons.rb +53 -0
- data/lib/draisine/version.rb +3 -0
- data/lib/draisine.rb +48 -0
- data/lib/ext/databasedotcom.rb +98 -0
- data/lib/generators/draisine/delta_migration_generator.rb +77 -0
- data/lib/generators/draisine/integration_generator.rb +53 -0
- data/lib/generators/draisine/templates/delta_migration.rb +24 -0
- data/lib/generators/draisine/templates/migration.rb +21 -0
- data/lib/generators/draisine/templates/model.rb +11 -0
- data/salesforce/sample_delete_trigger.apex +7 -0
- data/salesforce/sample_test_class_for_delete_trigger.apex +15 -0
- metadata +242 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module Draisine
|
2
|
+
module SalesforceComparisons
|
3
|
+
JSON_TIME_REGEX = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d{3})?[+\-]\d{2}:\d{2}\z/
|
4
|
+
EPSILON = 1e-10
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def salesforce_equals?(value, other)
|
8
|
+
value = salesforce_cleanup(value)
|
9
|
+
other = salesforce_cleanup(other)
|
10
|
+
value = salesforce_coerce(value)
|
11
|
+
other = salesforce_coerce(other)
|
12
|
+
|
13
|
+
return fp_equals?(value, other) if numeric?(value) && numeric?(other)
|
14
|
+
return value == other if value.class != other.class
|
15
|
+
|
16
|
+
case value
|
17
|
+
when String
|
18
|
+
normalize_string(value) == normalize_string(other)
|
19
|
+
else
|
20
|
+
value == other
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def salesforce_cleanup(value)
|
25
|
+
case value
|
26
|
+
when String
|
27
|
+
value.encode("UTF-8", invalid: :replace, undef: :replace, replace: "?")
|
28
|
+
else
|
29
|
+
value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def salesforce_coerce(value)
|
34
|
+
if value.kind_of?(DateTime) || value.kind_of?(Time) || value.kind_of?(Date) ||
|
35
|
+
value =~ JSON_TIME_REGEX
|
36
|
+
value = value.to_time.utc.change(usec: 0)
|
37
|
+
end
|
38
|
+
value
|
39
|
+
end
|
40
|
+
|
41
|
+
def normalize_string(string)
|
42
|
+
string.gsub(/\W/, "")
|
43
|
+
end
|
44
|
+
|
45
|
+
def fp_equals?(value, other)
|
46
|
+
(value - other).abs <= EPSILON
|
47
|
+
end
|
48
|
+
|
49
|
+
def numeric?(value)
|
50
|
+
value.kind_of?(Numeric)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/draisine.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext"
|
3
|
+
require "active_support/concern"
|
4
|
+
require "active_job/base"
|
5
|
+
|
6
|
+
require "databasedotcom"
|
7
|
+
require "ext/databasedotcom"
|
8
|
+
|
9
|
+
require "draisine/version"
|
10
|
+
require "draisine/setup"
|
11
|
+
|
12
|
+
require "draisine/jobs/job_base"
|
13
|
+
require "draisine/jobs/inbound_update_job"
|
14
|
+
require "draisine/jobs/inbound_delete_job"
|
15
|
+
require "draisine/jobs/soap_update_job"
|
16
|
+
require "draisine/jobs/soap_delete_job"
|
17
|
+
require "draisine/jobs/outbound_create_job"
|
18
|
+
require "draisine/jobs/outbound_update_job"
|
19
|
+
require "draisine/jobs/outbound_delete_job"
|
20
|
+
|
21
|
+
require "draisine/registry"
|
22
|
+
require "draisine/ip_checker"
|
23
|
+
|
24
|
+
require "draisine/util/hash_diff"
|
25
|
+
require "draisine/util/parse_time"
|
26
|
+
require "draisine/util/salesforce_comparisons"
|
27
|
+
require "draisine/util/caching_client"
|
28
|
+
|
29
|
+
require "draisine/query_mechanisms"
|
30
|
+
require "draisine/partitioner"
|
31
|
+
require "draisine/auditor"
|
32
|
+
require "draisine/conflict_detector"
|
33
|
+
require "draisine/conflict_resolver"
|
34
|
+
require "draisine/syncer"
|
35
|
+
require "draisine/type_mapper"
|
36
|
+
require "draisine/soap_handler"
|
37
|
+
require "draisine/engine"
|
38
|
+
require "draisine/importer"
|
39
|
+
require "draisine/poller"
|
40
|
+
|
41
|
+
require "draisine/concerns/array_setter"
|
42
|
+
require "draisine/concerns/attributes_mapping"
|
43
|
+
require "draisine/concerns/import"
|
44
|
+
require "draisine/active_record"
|
45
|
+
|
46
|
+
|
47
|
+
module Draisine
|
48
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Databasedotcom
|
2
|
+
class Client
|
3
|
+
# Fetches a collection of sobjects with given ids
|
4
|
+
# Useful in conjunction with get_updated / get_deleted calls
|
5
|
+
def fetch_multiple(classname, ids, batch_size = 100, field_list = nil)
|
6
|
+
return [] unless ids.present?
|
7
|
+
klass = find_or_materialize(classname)
|
8
|
+
field_list ||= klass.field_list.split(",")
|
9
|
+
field_list = field_list | ["Id"]
|
10
|
+
field_list = field_list.join(",")
|
11
|
+
ids.in_groups_of(batch_size).flat_map do |ids|
|
12
|
+
query <<-EOQ
|
13
|
+
SELECT #{field_list}
|
14
|
+
FROM #{klass.sobject_name}
|
15
|
+
WHERE id IN (#{ids.map {|id| "'%s'" % id}.join(',')})
|
16
|
+
EOQ
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a list of updated sobject ids for provided date range
|
21
|
+
# reference: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_getupdated.htm?search_text=getUpdated
|
22
|
+
def get_updated(classname, start_date, end_date = Time.current)
|
23
|
+
result = http_get("/services/data/v#{self.version}/sobjects/#{classname}/updated",
|
24
|
+
start: prepare_date_arg(start_date),
|
25
|
+
end: prepare_date_arg(end_date))
|
26
|
+
JSON.parse(result.body)
|
27
|
+
rescue Databasedotcom::SalesForceError => e
|
28
|
+
if e.message.include?("is not replicable")
|
29
|
+
{}
|
30
|
+
else
|
31
|
+
raise
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_updated_ids(classname, start_date, end_date = Time.current)
|
36
|
+
get_updated(classname, start_date, end_date).fetch("ids", [])
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns a list of updated sobject ids for provided date range
|
40
|
+
# reference: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_getdeleted.htm?search_text=getUpdated
|
41
|
+
def get_deleted(classname, start_date, end_date = Time.current)
|
42
|
+
result = http_get("/services/data/v#{self.version}/sobjects/#{classname}/deleted",
|
43
|
+
start: prepare_date_arg(start_date),
|
44
|
+
end: prepare_date_arg(end_date))
|
45
|
+
JSON.parse(result.body)
|
46
|
+
rescue Databasedotcom::SalesForceError => e
|
47
|
+
if e.message.include?("is not replicable")
|
48
|
+
{}
|
49
|
+
else
|
50
|
+
raise
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_deleted_ids(classname, start_date, end_date = Time.current)
|
55
|
+
get_deleted(classname, start_date, end_date).fetch("deletedRecords", []).map {|r| r["id"] }
|
56
|
+
end
|
57
|
+
|
58
|
+
def count(classname)
|
59
|
+
query("SELECT COUNT() FROM #{classname}").total_size
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
def prepare_date_arg(date)
|
65
|
+
return date.to_time.iso8601 if date.respond_to?(:to_time)
|
66
|
+
date
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_or_materialize(class_or_classname)
|
70
|
+
if class_or_classname.is_a?(Class)
|
71
|
+
clazz = class_or_classname
|
72
|
+
else
|
73
|
+
match = class_or_classname.match(/(?:(.+)::)?(\w+)$/)
|
74
|
+
preceding_namespace = match[1]
|
75
|
+
classname = match[2]
|
76
|
+
raise ArgumentError if preceding_namespace && preceding_namespace != module_namespace.name
|
77
|
+
clazz = module_namespace.const_get(classname.to_sym, false) rescue nil # fix inherited namespace
|
78
|
+
clazz ||= self.materialize(classname)
|
79
|
+
end
|
80
|
+
clazz
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
module Sobject
|
85
|
+
module NumericFieldsExtensions
|
86
|
+
def register_field(name, field)
|
87
|
+
super
|
88
|
+
%w[scale precision length byteLength].each do |attr|
|
89
|
+
self.type_map[name][attr.underscore.to_sym] = field[attr] if field[attr]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class <<Sobject
|
95
|
+
prepend NumericFieldsExtensions
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module Draisine
|
4
|
+
class DeltaMigrationGenerator < Rails::Generators::Base
|
5
|
+
self.source_root(File.expand_path('../templates/', __FILE__))
|
6
|
+
|
7
|
+
desc <<-DESC
|
8
|
+
Generates a migration for missing columns for given model
|
9
|
+
DESC
|
10
|
+
|
11
|
+
argument :model,
|
12
|
+
type: :string,
|
13
|
+
banner: "ModelName",
|
14
|
+
desc: "Model name for active record (e.g. Lead)",
|
15
|
+
required: true
|
16
|
+
|
17
|
+
argument :salesforce_object_name,
|
18
|
+
type: :string,
|
19
|
+
banner: "SalesforceObjectName",
|
20
|
+
desc: "Salesforce object name (inferred from ModelName by default)",
|
21
|
+
required: false
|
22
|
+
|
23
|
+
class_option :fields,
|
24
|
+
type: :array,
|
25
|
+
banner: "Field1,Field2",
|
26
|
+
desc: "Fields to generate (would otherwise generate all missing fields)",
|
27
|
+
required: false
|
28
|
+
|
29
|
+
def generate
|
30
|
+
@model_name = model.classify.singularize
|
31
|
+
@model_file = "app/models/#{model.underscore.singularize}.rb"
|
32
|
+
@table_name = model.underscore.gsub("/", "_").pluralize
|
33
|
+
@migration_title = "DeltaUpdate#{model.classify.gsub('::', '').pluralize}#{migration_uid}"
|
34
|
+
@migration_file = existing_migration_name(@migration_title) ||
|
35
|
+
"db/migrate/#{migration_number}_#{@migration_title.underscore}.rb"
|
36
|
+
@fields = (options["fields"] || []).map(&:downcase)
|
37
|
+
@salesforce_object_name = salesforce_object_name || model.classify.demodulize
|
38
|
+
@materialized_model = Draisine.salesforce_client.materialize(@salesforce_object_name)
|
39
|
+
@mapper = Draisine::TypeMapper.new(@materialized_model.type_map)
|
40
|
+
@existing_columns = ActiveRecord::Base.connection
|
41
|
+
.columns(@table_name)
|
42
|
+
.each_with_object({}) {|col, h| h[col.name] = Draisine::TypeMapper::ActiveRecordColumnDef.from_ar_column(col) }
|
43
|
+
@new_ar_col_defs = @mapper.active_record_column_defs
|
44
|
+
.reject {|col_def| @existing_columns.key?(col_def.column_name) }
|
45
|
+
.select {|col_def| column_in_required_fields?(col_def.column_name) }
|
46
|
+
@changed_ar_col_defs = @mapper.active_record_column_defs
|
47
|
+
.select {|col_def| @existing_columns.key?(col_def.column_name) }
|
48
|
+
.select {|col_def| different_column?(@existing_columns[col_def.column_name], col_def) }
|
49
|
+
.select {|col_def| column_in_required_fields?(col_def.column_name) }
|
50
|
+
template "delta_migration.rb", @migration_file
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def migration_number
|
56
|
+
Time.current.utc.strftime("%Y%m%d%H%M%S")
|
57
|
+
end
|
58
|
+
|
59
|
+
def migration_uid
|
60
|
+
Time.current.utc.strftime("%Y%m%d")
|
61
|
+
end
|
62
|
+
|
63
|
+
def column_in_required_fields?(column)
|
64
|
+
@fields.empty? || @fields.include?(column.to_s.downcase)
|
65
|
+
end
|
66
|
+
|
67
|
+
# This is needed for rails destroy to work, since every time we generate a new name for our migration
|
68
|
+
def existing_migration_name(migration_title)
|
69
|
+
Dir.glob("#{Rails.root}/db/migrate/[0-9]*_#{migration_title.underscore}.rb").first
|
70
|
+
end
|
71
|
+
|
72
|
+
def different_column?(first, second)
|
73
|
+
first.column_type != second.column_type ||
|
74
|
+
(first.options[:limit] && second.options[:limit] && first.options[:limit] != second.options[:limit])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module Draisine
|
4
|
+
class IntegrationGenerator < Rails::Generators::Base
|
5
|
+
self.source_root(File.expand_path('../templates/', __FILE__))
|
6
|
+
|
7
|
+
desc <<-DESC
|
8
|
+
Uses materialized models from salesforce.
|
9
|
+
Generates a migration creating an integration table for a salesforce model and
|
10
|
+
an active_record model under app/models/salesforce/
|
11
|
+
DESC
|
12
|
+
|
13
|
+
argument :model, type: :string,
|
14
|
+
banner: "ModelName",
|
15
|
+
desc: "Model name for active record (e.g. Lead)",
|
16
|
+
required: true
|
17
|
+
argument :salesforce_object_name, type: :string,
|
18
|
+
banner: "SalesforceObjectName",
|
19
|
+
desc: "Salesforce object name (inferred from ModelName by default)",
|
20
|
+
required: false
|
21
|
+
def create_salesforce_integrations
|
22
|
+
@model_name = model.classify.singularize
|
23
|
+
@model_file = "app/models/#{model.underscore.singularize}.rb"
|
24
|
+
@table_name = model.underscore.gsub("/", "_").pluralize
|
25
|
+
@migration_title = "CreateSalesforce#{model.classify.gsub('::', '').pluralize}"
|
26
|
+
@migration_file = existing_migration_name(@migration_title) ||
|
27
|
+
"db/migrate/#{migration_number}_#{@migration_title.underscore}.rb"
|
28
|
+
|
29
|
+
@salesforce_object_name = salesforce_object_name || model.classify.demodulize
|
30
|
+
@materialized_model = Draisine.salesforce_client.materialize(@salesforce_object_name)
|
31
|
+
@mapper = Draisine::TypeMapper.new(@materialized_model.type_map)
|
32
|
+
@ar_col_defs = @mapper.active_record_column_defs
|
33
|
+
@columns = @mapper.active_record_column_defs.map(&:column_name)
|
34
|
+
@serialized_columns = @mapper.serialized_columns
|
35
|
+
@array_columns = @mapper.array_columns
|
36
|
+
@non_audited_columns = @mapper.columns - @mapper.updateable_columns
|
37
|
+
|
38
|
+
template "migration.rb", @migration_file
|
39
|
+
template "model.rb", @model_file
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def migration_number
|
45
|
+
Time.current.utc.strftime("%Y%m%d%H%M%S")
|
46
|
+
end
|
47
|
+
|
48
|
+
# This is needed for rails destroy to work, since every time we generate a new name for our migration
|
49
|
+
def existing_migration_name(migration_title)
|
50
|
+
Dir.glob("#{Rails.root}/db/migrate/[0-9]*_#{migration_title.underscore}.rb").first
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class <%= @migration_title %> < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
change_table :<%= @table_name %>, bulk: true do |t|
|
4
|
+
<%- @new_ar_col_defs.each do |col_def| -%>
|
5
|
+
t.column :<%= col_def.column_name %>, :<%= col_def.column_type %>, <%= col_def.options.inspect %>
|
6
|
+
<%- end -%>
|
7
|
+
<%- @changed_ar_col_defs.each do |col_def| -%>
|
8
|
+
t.change :<%= col_def.column_name %>, :<%= col_def.column_type %>, <%= col_def.options.inspect %>
|
9
|
+
<%- end -%>
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def down
|
14
|
+
change_table :<%= @table_name %>, bulk: true do |t|
|
15
|
+
<%- @new_ar_col_defs.each do |col_def| -%>
|
16
|
+
t.remove :<%= col_def.column_name %>
|
17
|
+
<%- end -%>
|
18
|
+
<%- @changed_ar_col_defs.each do |col_def| -%>
|
19
|
+
<%- ex_col = @existing_columns[col_def.column_name] -%>
|
20
|
+
t.change :<%= ex_col.column_name %>, :<%= ex_col.column_type %>, <%= ex_col.options.inspect %>
|
21
|
+
<%- end -%>
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class <%= @migration_title %> < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :<%= @table_name %> do |t|
|
4
|
+
<%- @ar_col_defs.each do |col_def| -%>
|
5
|
+
t.column :<%= col_def.column_name %>, :<%= col_def.column_type %>, <%= col_def.options.inspect %>
|
6
|
+
<%- end -%>
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
t.index :updated_at
|
10
|
+
|
11
|
+
# These are draisine-specific columns
|
12
|
+
t.column :salesforce_id, :string, :limit => 18
|
13
|
+
t.column :salesforce_updated_at, :datetime
|
14
|
+
t.index :salesforce_id, :unique => true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def down
|
19
|
+
drop_table :<%= @table_name %>
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class <%= @model_name %> < ActiveRecord::Base
|
2
|
+
self.table_name = <%= @table_name.inspect %>
|
3
|
+
<%- @serialized_columns.each do |col| -%>
|
4
|
+
serialize :<%= col %>, JSON
|
5
|
+
<%- end -%>
|
6
|
+
|
7
|
+
salesforce_syncable synced_attributes: <%= @columns.map(&:to_sym).inspect %>,
|
8
|
+
non_audited_attributes: <%= @non_audited_columns.map(&:to_sym).inspect %>,
|
9
|
+
array_attributes: <%= @array_columns.map(&:to_sym).inspect %>,
|
10
|
+
salesforce_object_name: <%= @salesforce_object_name.inspect %>
|
11
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
@isTest(seeAllData=false)
|
2
|
+
|
3
|
+
private class CreateDeletedObjectAfterLeadDeleteTest {
|
4
|
+
static testMethod void testCreatedDeletedObject() {
|
5
|
+
Test.startTest();
|
6
|
+
Lead lead = new Lead(FirstName = 'John', LastName = 'Snow');
|
7
|
+
insert lead;
|
8
|
+
System.assertEquals([SELECT COUNT() FROM Deleted_Object__c], 0);
|
9
|
+
String id = lead.Id;
|
10
|
+
delete lead;
|
11
|
+
System.assertEquals([SELECT COUNT() FROM Deleted_Object__c], 1);
|
12
|
+
System.assertEquals([SELECT Object_Id__c FROM Deleted_Object__c LIMIT 1].Object_Id__c, id);
|
13
|
+
Test.stopTest();
|
14
|
+
}
|
15
|
+
}
|
metadata
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: draisine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.10
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark Abramov
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-collection_matchers
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: guard-rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: sqlite3
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rails
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '4.2'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '4.2'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: databasedotcom
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '1.3'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '1.3'
|
153
|
+
description: Bidirectional synchronization for salesforce / activerecord
|
154
|
+
email:
|
155
|
+
- mark.abramov@chloeandisabel.com
|
156
|
+
executables: []
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- ".gitignore"
|
161
|
+
- ".rspec"
|
162
|
+
- ".travis.yml"
|
163
|
+
- Gemfile
|
164
|
+
- Guardfile
|
165
|
+
- LICENSE.txt
|
166
|
+
- README.md
|
167
|
+
- Rakefile
|
168
|
+
- app/controllers/draisine/soap_controller.rb
|
169
|
+
- bin/console
|
170
|
+
- bin/setup
|
171
|
+
- config/routes.rb
|
172
|
+
- draisine.gemspec
|
173
|
+
- lib/draisine.rb
|
174
|
+
- lib/draisine/active_record.rb
|
175
|
+
- lib/draisine/auditor.rb
|
176
|
+
- lib/draisine/auditor/result.rb
|
177
|
+
- lib/draisine/concerns/array_setter.rb
|
178
|
+
- lib/draisine/concerns/attributes_mapping.rb
|
179
|
+
- lib/draisine/concerns/import.rb
|
180
|
+
- lib/draisine/conflict_detector.rb
|
181
|
+
- lib/draisine/conflict_resolver.rb
|
182
|
+
- lib/draisine/engine.rb
|
183
|
+
- lib/draisine/importer.rb
|
184
|
+
- lib/draisine/ip_checker.rb
|
185
|
+
- lib/draisine/jobs/inbound_delete_job.rb
|
186
|
+
- lib/draisine/jobs/inbound_update_job.rb
|
187
|
+
- lib/draisine/jobs/job_base.rb
|
188
|
+
- lib/draisine/jobs/outbound_create_job.rb
|
189
|
+
- lib/draisine/jobs/outbound_delete_job.rb
|
190
|
+
- lib/draisine/jobs/outbound_update_job.rb
|
191
|
+
- lib/draisine/jobs/soap_delete_job.rb
|
192
|
+
- lib/draisine/jobs/soap_update_job.rb
|
193
|
+
- lib/draisine/partitioner.rb
|
194
|
+
- lib/draisine/poller.rb
|
195
|
+
- lib/draisine/query_mechanisms.rb
|
196
|
+
- lib/draisine/query_mechanisms/base.rb
|
197
|
+
- lib/draisine/query_mechanisms/default.rb
|
198
|
+
- lib/draisine/query_mechanisms/last_modified_date.rb
|
199
|
+
- lib/draisine/query_mechanisms/system_modstamp.rb
|
200
|
+
- lib/draisine/registry.rb
|
201
|
+
- lib/draisine/setup.rb
|
202
|
+
- lib/draisine/soap_handler.rb
|
203
|
+
- lib/draisine/syncer.rb
|
204
|
+
- lib/draisine/type_mapper.rb
|
205
|
+
- lib/draisine/util/caching_client.rb
|
206
|
+
- lib/draisine/util/hash_diff.rb
|
207
|
+
- lib/draisine/util/parse_time.rb
|
208
|
+
- lib/draisine/util/salesforce_comparisons.rb
|
209
|
+
- lib/draisine/version.rb
|
210
|
+
- lib/ext/databasedotcom.rb
|
211
|
+
- lib/generators/draisine/delta_migration_generator.rb
|
212
|
+
- lib/generators/draisine/integration_generator.rb
|
213
|
+
- lib/generators/draisine/templates/delta_migration.rb
|
214
|
+
- lib/generators/draisine/templates/migration.rb
|
215
|
+
- lib/generators/draisine/templates/model.rb
|
216
|
+
- salesforce/sample_delete_trigger.apex
|
217
|
+
- salesforce/sample_test_class_for_delete_trigger.apex
|
218
|
+
homepage: https://github.com/chloeandisabel/draisine
|
219
|
+
licenses:
|
220
|
+
- MIT
|
221
|
+
metadata: {}
|
222
|
+
post_install_message:
|
223
|
+
rdoc_options: []
|
224
|
+
require_paths:
|
225
|
+
- lib
|
226
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
227
|
+
requirements:
|
228
|
+
- - ">="
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: '0'
|
231
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
232
|
+
requirements:
|
233
|
+
- - ">="
|
234
|
+
- !ruby/object:Gem::Version
|
235
|
+
version: '0'
|
236
|
+
requirements: []
|
237
|
+
rubyforge_project:
|
238
|
+
rubygems_version: 2.5.1
|
239
|
+
signing_key:
|
240
|
+
specification_version: 4
|
241
|
+
summary: Synchronization machinery for salesforce
|
242
|
+
test_files: []
|