harmonia 0.1.7 → 0.1.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe5daefb15ce5ef2a2cc7a90a66123967ade8300f08b8f017220d5919c9aebe4
4
- data.tar.gz: f826c905f2192dd472ab849108afebda80282b1af0050b400ddcd15402dc2ae8
3
+ metadata.gz: 60bf90e5016a52779a8cd3400aa7a92ebb510c8b4304a38d77b6b4891d255a41
4
+ data.tar.gz: 4a7a354befe4e2d0164e0c1cdd0a2d83769a5429f205da68816faa54c2bb34df
5
5
  SHA512:
6
- metadata.gz: 40ec19a556c1f7fe3251f4f8d2885a6f5f4480098df2bac9654473b55870457029133abc013434cbdf99859873b51317266037c06432bbbd343a82a6afeec92a
7
- data.tar.gz: 5e2d1d49a203f0c5b415413a577665f0ab1f2fe90bec8b22a70cbdf1d5b96f4f1259f3817baeff760505e16c4474f61f5e5d6850d9402429ec991862f97a7344
6
+ metadata.gz: b9012312d892912d09d3bb214197ffbb388d0ab11940fd0df940c8f3b9beea4a54fa66b7dd51c0fa7b759e414b90c41cde8244d9cc716f9c25da123ff2256867
7
+ data.tar.gz: 70fb50a16c8fc7a998818d89522d88432b00ec23df2630781b1752ee2f67b294a5349008a981b9c99d4de6a9c7503d64651cc1c244def6c06f9f97b7ed18f781
@@ -17,6 +17,57 @@ module Harmonia
17
17
  migration_template "add_filemaker_id_to_table.rb", "db/migrate/add_filemaker_id_to_#{table_name}.rb"
18
18
  end
19
19
 
20
+ def create_or_update_rake_task
21
+ rake_file = "lib/tasks/sync_data.rake"
22
+ task_name = "sync_#{file_name}_to_filemaker"
23
+
24
+ if File.exist?(rake_file)
25
+ # Read existing file
26
+ content = File.read(rake_file)
27
+
28
+ # Add new task before the final 'end' if it doesn't exist
29
+ unless content.include?("task #{task_name}:")
30
+ # Add the new task before the final 'end'
31
+ new_task = <<~TASK
32
+
33
+ desc 'sync #{table_name} from ActiveRecord to FileMaker'
34
+ task #{task_name}: :environment do
35
+ #{class_name}ToFileMakerSyncer.new.sync
36
+ end
37
+ TASK
38
+
39
+ # Insert before the final 'end'
40
+ content = content.sub(/^end\s*$/, "#{new_task}end")
41
+
42
+ # Add task to the 'all' array
43
+ content = content.sub(/task all: %i\[(.*?)\]/) do
44
+ tasks = $1.split.map(&:to_sym)
45
+ tasks << task_name.to_sym unless tasks.include?(task_name.to_sym)
46
+ "task all: %i[#{tasks.join(' ')}]"
47
+ end
48
+
49
+ File.write(rake_file, content)
50
+ end
51
+ else
52
+ # Create new rake file
53
+ template "sync_data.rake", rake_file
54
+
55
+ # Add the new task
56
+ content = File.read(rake_file)
57
+ new_task = <<~TASK
58
+
59
+ desc 'sync #{table_name} from ActiveRecord to FileMaker'
60
+ task #{task_name}: :environment do
61
+ #{class_name}ToFileMakerSyncer.new.sync
62
+ end
63
+ TASK
64
+
65
+ content = content.sub(/^end\s*$/, "#{new_task}end")
66
+ content = content.sub(/task all: %i\[\]/, "task all: %i[#{task_name}]")
67
+ File.write(rake_file, content)
68
+ end
69
+ end
70
+
20
71
  def show_readme
21
72
  readme_content = <<~README
22
73
 
@@ -27,6 +78,7 @@ module Harmonia
27
78
  Files created:
28
79
  - app/syncers/#{file_name}_to_filemaker_syncer.rb
29
80
  - db/migrate/..._add_filemaker_id_to_#{table_name}.rb
81
+ - lib/tasks/sync_data.rake (updated with sync_#{file_name}_to_filemaker task)
30
82
 
31
83
  Next steps:
32
84
  1. Run migrations: rails db:migrate
@@ -58,6 +110,10 @@ module Harmonia
58
110
  Note: The total_required count used for sync tracking is automatically calculated
59
111
  from @total_create_required + @total_update_required
60
112
 
113
+ 7. Run the sync task:
114
+ - Individual sync: rake sync:sync_#{file_name}_to_filemaker
115
+ - All syncs: rake sync:all
116
+
61
117
  README
62
118
 
63
119
  say readme_content, :green if behavior == :invoke
@@ -17,6 +17,57 @@ module Harmonia
17
17
  migration_template "add_filemaker_id_to_table.rb", "db/migrate/add_filemaker_id_to_#{table_name}.rb"
18
18
  end
19
19
 
20
+ def create_or_update_rake_task
21
+ rake_file = "lib/tasks/sync_data.rake"
22
+ task_name = "sync_#{file_name}"
23
+
24
+ if File.exist?(rake_file)
25
+ # Read existing file
26
+ content = File.read(rake_file)
27
+
28
+ # Add new task before the final 'end' if it doesn't exist
29
+ unless content.include?("task #{task_name}:")
30
+ # Add the new task before the final 'end'
31
+ new_task = <<~TASK
32
+
33
+ desc 'sync #{table_name} from FileMaker to ActiveRecord'
34
+ task #{task_name}: :environment do
35
+ #{class_name}Syncer.new.sync
36
+ end
37
+ TASK
38
+
39
+ # Insert before the final 'end'
40
+ content = content.sub(/^end\s*$/, "#{new_task}end")
41
+
42
+ # Add task to the 'all' array
43
+ content = content.sub(/task all: %i\[(.*?)\]/) do
44
+ tasks = $1.split.map(&:to_sym)
45
+ tasks << task_name.to_sym unless tasks.include?(task_name.to_sym)
46
+ "task all: %i[#{tasks.join(' ')}]"
47
+ end
48
+
49
+ File.write(rake_file, content)
50
+ end
51
+ else
52
+ # Create new rake file
53
+ template "sync_data.rake", rake_file
54
+
55
+ # Add the new task
56
+ content = File.read(rake_file)
57
+ new_task = <<~TASK
58
+
59
+ desc 'sync #{table_name} from FileMaker to ActiveRecord'
60
+ task #{task_name}: :environment do
61
+ #{class_name}Syncer.new.sync
62
+ end
63
+ TASK
64
+
65
+ content = content.sub(/^end\s*$/, "#{new_task}end")
66
+ content = content.sub(/task all: %i\[\]/, "task all: %i[#{task_name}]")
67
+ File.write(rake_file, content)
68
+ end
69
+ end
70
+
20
71
  def show_readme
21
72
  readme_content = <<~README
22
73
 
@@ -27,6 +78,7 @@ module Harmonia
27
78
  Files created:
28
79
  - app/syncers/#{file_name}_syncer.rb
29
80
  - db/migrate/..._add_filemaker_id_to_#{table_name}.rb
81
+ - lib/tasks/sync_data.rake (updated with sync_#{file_name} task)
30
82
 
31
83
  Next steps:
32
84
  1. Run migrations: rails db:migrate
@@ -45,6 +97,10 @@ module Harmonia
45
97
  Note: The total_required count used for sync tracking is automatically calculated
46
98
  from @total_create_required + @total_update_required
47
99
 
100
+ 5. Run the sync task:
101
+ - Individual sync: rake sync:sync_#{file_name}
102
+ - All syncs: rake sync:all
103
+
48
104
  README
49
105
 
50
106
  say readme_content, :green if behavior == :invoke
@@ -10,6 +10,8 @@ class CreateHarmoniaSyncs < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>
10
10
  t.string :status, default: 'pending'
11
11
  t.string :direction
12
12
  t.text :error_message
13
+ t.string :failed_fm_ids, array: true
14
+ t.integer :failed_pg_ids, array: true
13
15
 
14
16
  t.timestamps
15
17
  end
@@ -6,6 +6,8 @@ class <%= class_name %>Syncer
6
6
  def initialize(database_connector)
7
7
  @database_connector = database_connector
8
8
  @last_synced_on = Harmonia::Sync.last_sync_for('<%= table_name %>', 'FileMaker to ActiveRecord')&.ran_on || (Time.now - 15.year)
9
+ @failed_fm_ids = []
10
+ @failed_pg_ids = []
9
11
  end
10
12
 
11
13
  # Main sync method
@@ -20,7 +22,7 @@ class <%= class_name %>Syncer
20
22
  sync_records(sync_record)
21
23
  end
22
24
  rescue StandardError => e
23
- sync_record&.fail!(e.message)
25
+ sync_record&.fail!(e.message, failed_fm_ids: @failed_fm_ids, failed_pg_ids: @failed_pg_ids)
24
26
  raise
25
27
  end
26
28
 
@@ -36,55 +38,68 @@ class <%= class_name %>Syncer
36
38
 
37
39
  sync_record.finish!(
38
40
  records_synced: total_synced,
39
- records_required: total_required
41
+ records_required: total_required,
42
+ failed_fm_ids: @failed_fm_ids,
43
+ failed_pg_ids: @failed_pg_ids
40
44
  )
41
45
  end
42
46
 
43
47
  # Returns an array of Trophonius records that need to be created
44
- # Use YourTrophoniusModel.to_pg(record) to convert to PostgreSQL attributes
48
+ # Use FileMaker::<%= class_name %>.to_pg(record) to convert to PostgreSQL attributes
45
49
  # Set @total_create_required to the total number of records that should exist after creation
46
50
  # @return [Array<Trophonius::Record>] Array of Trophonius records
47
51
  def records_to_create
48
52
  # TODO: Implement logic to fetch records from FileMaker that need to be created in PostgreSQL
49
53
  # Example:
50
- # filemaker_records = YourTrophoniusModel.all
51
- # @total_create_required = filemaker_records.length
52
- # existing_ids = <%= class_name %>.pluck(:filemaker_id)
53
- # filemaker_records.reject { |record| existing_ids.include?(record.record_id) }
54
- @total_create_required = 0
55
- []
54
+ filemaker_records = FileMaker::<%= class_name %>.where(creation_timestamp: ">= #{@last_synced_on.to_fm}").not
55
+ @total_create_required = filemaker_records.length
56
+ existing_ids = <%= class_name %>.pluck(:filemaker_id)
57
+ filemaker_records.reject { |record| existing_ids.include?(record.id) }
56
58
  end
57
59
 
58
60
  # Returns an array of Trophonius records that need to be updated
59
- # Use YourTrophoniusModel.to_pg(record) to convert to PostgreSQL attributes
61
+ # Use FileMaker::<%= class_name %>.to_pg(record) to convert to PostgreSQL attributes
60
62
  # Set @total_update_required to the total number of records that should be updated
61
63
  # @return [Array<Trophonius::Record>] Array of Trophonius records
62
64
  def records_to_update
63
65
  # TODO: Implement logic to fetch records from FileMaker that need to be updated in PostgreSQL
64
66
  # Example:
65
- # filemaker_records = YourTrophoniusModel.all
66
- # records_needing_update = filemaker_records.select { |fm_record|
67
- # pg_record = <%= class_name %>.find_by(filemaker_id: fm_record.record_id)
68
- # pg_record && needs_update?(fm_record, pg_record)
69
- # }
70
- # @total_update_required = records_needing_update.length
71
- # records_needing_update
72
- @total_update_required = 0
73
- []
67
+ filemaker_records = FileMaker::<%= class_name %>.where(modification_timestamp: ">= #{@last_synced_on.to_fm}")
68
+ records_needing_update = filemaker_records.select { |fm_record|
69
+ pg_record = <%= class_name %>.find_by(filemaker_id: fm_record.record_id)
70
+ pg_record && needs_update?(fm_record, pg_record)
71
+ }
72
+ @total_update_required = records_needing_update.length
73
+ records_needing_update
74
74
  end
75
75
 
76
76
  # Returns an array of record identifiers that need to be deleted
77
77
  # @return [Array] Array of record identifiers
78
78
  def records_to_delete
79
- # TODO: Implement logic to determine which PostgreSQL records should be deleted
80
- # Example:
81
- # filemaker_ids = YourTrophoniusModel.all.map(&:record_id)
82
- # <%= class_name %>.where.not(filemaker_id: filemaker_ids).pluck(:id)
83
- []
79
+ # Get all modified FileMaker record IDs
80
+ filemaker_records = FileMaker::<%= class_name %>.where(modification_timestamp: ">= #{@last_synced_on.to_fm}")
81
+ fm_ids = filemaker_records.map(&:record_id)
82
+
83
+ # Find PostgreSQL records whose FileMaker IDs aren't in the modified set
84
+ # These might have been deleted in FileMaker
85
+ fm_ids_no_update_needed = <%= class_name %>.where.not(filemaker_id: fm_ids).pluck(:filemaker_id)
86
+ return [] if fm_ids_no_update_needed.empty?
87
+
88
+ # Query FileMaker to check if these records still exist
89
+ possibly_deleted_query = FileMaker::<%= class_name %>.where(record_id: fm_ids_no_update_needed.first)
90
+ fm_ids_no_update_needed.count > 1 && fm_ids_no_update_needed[1..].each do |fm_id|
91
+ possibly_deleted_query.or(record_id: fm_id)
92
+ end
93
+
94
+ # Find IDs that exist in PostgreSQL but not in FileMaker (truly deleted)
95
+ deleted_fm_ids = fm_ids_no_update_needed - possibly_deleted_query.map(&:record_id)
96
+
97
+ # Return PostgreSQL IDs for records with these FileMaker IDs
98
+ <%= class_name %>.where(filemaker_id: deleted_fm_ids).pluck(:id)
84
99
  end
85
100
 
86
101
  def needs_update?(fm_record, pg_record)
87
- pg_attributes = YourTrophoniusModel.to_pg(fm_record)
102
+ pg_attributes = FileMaker::<%= class_name %>.to_pg(fm_record)
88
103
 
89
104
  pg_attributes.any? { |key, value| pg_record.send(key) != value }
90
105
  end
@@ -93,37 +108,60 @@ class <%= class_name %>Syncer
93
108
  records = records_to_create
94
109
  return 0 if records.empty?
95
110
 
96
- attributes_array = records.map do |trophonius_record|
97
- YourTrophoniusModel.to_pg(trophonius_record).merge(
98
- created_at: Time.current,
99
- updated_at: Time.current
100
- )
111
+ success_count = 0
112
+
113
+ records.each do |trophonius_record|
114
+ begin
115
+ attributes = FileMaker::<%= class_name %>.to_pg(trophonius_record).merge(
116
+ created_at: Time.current,
117
+ updated_at: Time.current
118
+ )
119
+ <%= class_name %>.create!(attributes)
120
+ success_count += 1
121
+ rescue StandardError => e
122
+ @failed_fm_ids << trophonius_record.record_id
123
+ Rails.logger.error("Failed to create record from FileMaker ID #{trophonius_record.record_id}: #{e.message}")
124
+ end
101
125
  end
102
126
 
103
- <%= class_name %>.insert_all(attributes_array)
104
- records.size
127
+ success_count
105
128
  end
106
129
 
107
130
  def update_records
108
131
  records = records_to_update
109
132
  return 0 if records.empty?
110
133
 
111
- records.each do |trophonius_record|
112
- pg_attributes = YourTrophoniusModel.to_pg(trophonius_record)
134
+ success_count = 0
113
135
 
114
- <%= class_name %>.where(filemaker_id: trophonius_record.record_id).update_all(
115
- pg_attributes.merge(updated_at: Time.current)
116
- )
136
+ records.each do |trophonius_record|
137
+ begin
138
+ pg_attributes = FileMaker::<%= class_name %>.to_pg(trophonius_record)
139
+
140
+ <%= class_name %>.where(filemaker_id: trophonius_record.record_id).update_all(
141
+ pg_attributes.merge(updated_at: Time.current)
142
+ )
143
+ success_count += 1
144
+ rescue StandardError => e
145
+ @failed_fm_ids << trophonius_record.record_id
146
+ Rails.logger.error("Failed to update record from FileMaker ID #{trophonius_record.record_id}: #{e.message}")
147
+ end
117
148
  end
118
149
 
119
- records.size
150
+ success_count
120
151
  end
121
152
 
122
153
  def delete_records
123
154
  ids = records_to_delete
124
155
  return if ids.empty?
125
156
 
126
- <%= class_name %>.where(id: ids).destroy_all
157
+ ids.each do |pg_id|
158
+ begin
159
+ <%= class_name %>.where(id: pg_id).destroy_all
160
+ rescue StandardError => e
161
+ @failed_pg_ids << pg_id
162
+ Rails.logger.error("Failed to delete record with PostgreSQL ID #{pg_id}: #{e.message}")
163
+ end
164
+ end
127
165
  end
128
166
 
129
167
  def create_sync_record
@@ -24,9 +24,9 @@ module Harmonia
24
24
  scope :completed, -> { where(status: 'completed') }
25
25
  scope :failed, -> { where(status: 'failed') }
26
26
 
27
- # Get the most recent sync for a table in a given direction
27
+ # Get the most recent successful sync for a table in a given direction
28
28
  def self.last_sync_for(table_name, direction)
29
- for_direction(direction).for_table(table_name).recent.first
29
+ completed.for_direction(direction).for_table(table_name).recent.first
30
30
  end
31
31
 
32
32
  # Calculate sync completion percentage
@@ -46,19 +46,23 @@ module Harmonia
46
46
  end
47
47
 
48
48
  # Mark sync as completed
49
- def finish!(records_synced:, records_required:)
49
+ def finish!(records_synced:, records_required:, failed_fm_ids: [], failed_pg_ids: [])
50
50
  update!(
51
51
  status: 'completed',
52
52
  records_synced: records_synced,
53
- records_required: records_required
53
+ records_required: records_required,
54
+ failed_fm_ids: failed_fm_ids,
55
+ failed_pg_ids: failed_pg_ids
54
56
  )
55
57
  end
56
58
 
57
59
  # Mark sync as failed
58
- def fail!(error_message)
60
+ def fail!(error_message, failed_fm_ids: [], failed_pg_ids: [])
59
61
  update!(
60
62
  status: 'failed',
61
- error_message: error_message
63
+ error_message: error_message,
64
+ failed_fm_ids: failed_fm_ids,
65
+ failed_pg_ids: failed_pg_ids
62
66
  )
63
67
  end
64
68
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :sync do
4
+ desc 'sync all'
5
+ task all: %i[]
6
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harmonia
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kempen Automatisering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-11 00:00:00.000000000 Z
11
+ date: 2025-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trophonius
@@ -43,6 +43,7 @@ files:
43
43
  - lib/generators/harmonia/templates/database_connector.rb
44
44
  - lib/generators/harmonia/templates/filemaker_to_activerecord_syncer_template.rb
45
45
  - lib/generators/harmonia/templates/harmonia_sync.rb
46
+ - lib/generators/harmonia/templates/sync_data.rake
46
47
  - lib/generators/harmonia/templates/trophonius_model_extension.rb
47
48
  - lib/harmonia.rb
48
49
  - lib/harmonia/railtie.rb