no_fly_list 0.2.0 → 0.3.0

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: a736dbad67c9674eb172dc3633226f261d4d6bfaba47e06b3c539f98dfbfc439
4
- data.tar.gz: 378ddddcfc84f69bc667df66195a87298705f6fe4ebd3067bce71d295a7eb309
3
+ metadata.gz: da62f66e9694575999da6c6864650b05036fe0f32ed762255d4552e11fc53ce4
4
+ data.tar.gz: 965687001501fec670c74c1a89d95340776b3b8ffa061517d34c430439471572
5
5
  SHA512:
6
- metadata.gz: 2f5dada9474e4defdd42556d2ab049a85774d9d7e75966c75ceac76ec9c1a793873f13b06281a12089264aa64de94268db2b5e1724d3bccf2e8c9c79062ceba9
7
- data.tar.gz: 888516a4c63cc367b976e7c0626efef8d65cb06d9d3d207f6100a0a6cb3590528f1f6f3f44b7fb697cb8bf3271f90d23878725615dd1304f10d74fa0fc313198
6
+ metadata.gz: 3c347ef635cb9c1adeb14df10b7ce8a6b6220104f93b0fc4b7debccf30c0537da86e232a4b62000fb3498253c8ae5535c5ca9ddb9290371d3afd4dd021c894ab
7
+ data.tar.gz: 3b1685788f672eadfe558f2bbceacdfd287dbcb865b7e2bbd8b718e710cf3870a575900bcae69452ebc9a720f8ca77c4c0f2b4488d56431e6c5a30946f4d630b
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails/generators'
4
- require 'rails/generators/active_record'
5
- require 'rails/generators/named_base'
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+ require "rails/generators/named_base"
6
6
 
7
7
  # Usage:
8
8
  # bin/rails generate no_fly_list:application_tag
@@ -11,15 +11,15 @@ module NoFlyList
11
11
  module Generators
12
12
  class InstallGenerator < Rails::Generators::Base
13
13
  include Rails::Generators::Migration
14
- source_root File.expand_path('templates', __dir__)
14
+ source_root File.expand_path("templates", __dir__)
15
15
 
16
- argument :connection_name, type: :string, desc: 'The name of the database connection', default: 'primary'
16
+ argument :connection_name, type: :string, desc: "The name of the database connection", default: "primary"
17
17
 
18
18
  def copy_application_tag
19
19
  ensure_connection_exists
20
- template 'application_tag.rb.erb', File.join('app/models', 'application_tag.rb')
21
- template 'application_tagging.rb.erb', File.join('app/models', 'application_tagging.rb')
22
- migration_template 'create_application_tagging_table.rb.erb', 'db/migrate/create_application_tagging_table.rb'
20
+ template "application_tag.rb.erb", File.join("app/models", "application_tag.rb")
21
+ template "application_tagging.rb.erb", File.join("app/models", "application_tagging.rb")
22
+ migration_template "create_application_tagging_table.rb.erb", "db/migrate/create_application_tagging_table.rb"
23
23
  end
24
24
 
25
25
  def self.next_migration_number(dirname)
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
- require 'rails/generators'
5
- require 'rails/generators/active_record'
6
- require 'rails/generators/named_base'
3
+ require "forwardable"
4
+ require "rails/generators"
5
+ require "rails/generators/active_record"
6
+ require "rails/generators/named_base"
7
7
 
8
8
  module NoFlyList
9
9
  module Generators
10
10
  class ModelsGenerator < Rails::Generators::NamedBase
11
- source_root File.expand_path('templates', __dir__)
11
+ source_root File.expand_path("templates", __dir__)
12
12
 
13
13
  def create_model_files
14
14
  return unless validate_model # Ensure it's an ActiveRecord model
15
15
 
16
- template 'tagging_model.rb.erb', File.join('app/models', class_path, "#{file_name.underscore}/tagging.rb")
17
- template 'tag_model.rb.erb', File.join('app/models', class_path, "#{file_name.underscore}_tag.rb")
16
+ template "tagging_model.rb.erb", File.join("app/models", class_path, "#{file_name.underscore}/tagging.rb")
17
+ template "tag_model.rb.erb", File.join("app/models", class_path, "#{file_name.underscore}_tag.rb")
18
18
  end
19
19
 
20
20
  private
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
- require 'rails/generators'
5
- require 'rails/generators/active_record'
6
- require 'rails/generators/named_base'
3
+ require "forwardable"
4
+ require "rails/generators"
5
+ require "rails/generators/active_record"
6
+ require "rails/generators/named_base"
7
7
 
8
8
  module NoFlyList
9
9
  module Generators
10
10
  class TaggingGenerator < Rails::Generators::NamedBase
11
11
  include ActiveRecord::Generators::Migration
12
12
 
13
- class_option :database, type: :string, default: 'primary',
14
- desc: 'Use different database for migration'
13
+ class_option :database, type: :string, default: "primary",
14
+ desc: "Use different database for migration"
15
15
 
16
16
  def self.default_generator_root
17
17
  File.dirname(__FILE__)
@@ -19,8 +19,8 @@ module NoFlyList
19
19
 
20
20
  def create_migration_file
21
21
  ensure_model_exists
22
- migration_template 'create_tagging_table.rb.erb',
23
- [db_migrate_path, "create_#{migration_name}.rb"].compact.join('/')
22
+ migration_template "create_tagging_table.rb.erb",
23
+ [ db_migrate_path, "create_#{migration_name}.rb" ].compact.join("/")
24
24
  end
25
25
 
26
26
  def self.next_migration_number(dirname)
@@ -18,6 +18,6 @@ module ApplicationTagTransformer
18
18
 
19
19
  # @return [String]
20
20
  def separator
21
- ','
21
+ ","
22
22
  end
23
23
  end
@@ -1,17 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
- require 'rails/generators'
5
- require 'rails/generators/active_record'
6
- require 'rails/generators/named_base'
3
+ require "forwardable"
4
+ require "rails/generators"
5
+ require "rails/generators/active_record"
6
+ require "rails/generators/named_base"
7
7
 
8
8
  unless defined?(ApplicationTagTransformer)
9
9
  module NoFlyList
10
10
  module Generators
11
+ # bin/rails g no_fly_list:transformer
11
12
  class TransformerGenerator < Rails::Generators::Base
12
- source_root File.expand_path('templates', __dir__)
13
+ source_root File.expand_path("templates", __dir__)
13
14
  def create_tag_transformer_file
14
- template 'tag_transformer.rb', File.join('app/transformers', 'application_tag_transformer.rb')
15
+ template "tag_transformer.rb", File.join("app/transformers", "application_tag_transformer.rb")
15
16
  end
16
17
  end
17
18
  end
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_record/railtie'
4
- require 'rails'
3
+ require "active_record/railtie"
4
+ require "rails"
5
5
 
6
6
  module NoFlyList
7
7
  class Railtie < Rails::Railtie # :nodoc:
8
8
  config.no_fly_list = ActiveSupport::OrderedOptions.new
9
- config.no_fly_list.tag_class_name = 'ApplicationTag'
10
- config.no_fly_list.tag_table_name = 'application_tags'
11
- config.no_fly_list.tagging_class_name = 'ApplicationTagging'
12
- config.no_fly_list.tagging_table_name = 'application_taggings'
9
+ config.no_fly_list.tag_class_name = "ApplicationTag"
10
+ config.no_fly_list.tag_table_name = "application_tags"
11
+ config.no_fly_list.tagging_class_name = "ApplicationTagging"
12
+ config.no_fly_list.tagging_table_name = "application_taggings"
13
13
 
14
14
  rake_tasks do
15
- load 'no_fly_list/railties/tasks.rake'
15
+ load "no_fly_list/railties/tasks.rake"
16
16
  end
17
17
  end
18
18
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  namespace :no_fly_list do
4
- desc 'List all taggable records'
4
+ desc "List all taggable records"
5
5
  task taggable_records: :environment do
6
6
  Rails.application.eager_load!
7
7
  taggable_classes = ActiveRecord::Base.descendants.select do |klass|
8
- klass.included_modules.any? { |mod| mod.in?([NoFlyList::TaggableRecord]) }
8
+ klass.included_modules.any? { |mod| mod.in?([ NoFlyList::TaggableRecord ]) }
9
9
  end
10
10
 
11
11
  puts "Found #{taggable_classes.size} taggable classes:\n\n"
@@ -15,11 +15,11 @@ namespace :no_fly_list do
15
15
  end
16
16
  end
17
17
 
18
- desc 'List all tag records'
18
+ desc "List all tag records"
19
19
  task tag_records: :environment do
20
20
  Rails.application.eager_load!
21
21
  tag_classes = ActiveRecord::Base.descendants.select do |klass|
22
- klass.included_modules.any? { |mod| mod.in?([NoFlyList::ApplicationTag, NoFlyList::TagRecord]) }
22
+ klass.included_modules.any? { |mod| mod.in?([ NoFlyList::ApplicationTag, NoFlyList::TagRecord ]) }
23
23
  end
24
24
 
25
25
  puts "Found #{tag_classes.size} tag classes:\n\n"
@@ -29,11 +29,11 @@ namespace :no_fly_list do
29
29
  end
30
30
  end
31
31
 
32
- desc 'Check taggable records and their associated tables'
32
+ desc "Check taggable records and their associated tables"
33
33
  task check_taggable_records: :environment do
34
34
  Rails.application.eager_load!
35
35
  taggable_classes = ActiveRecord::Base.descendants.select do |klass|
36
- klass.included_modules.any? { |mod| mod.in?([NoFlyList::TaggableRecord]) }
36
+ klass.included_modules.any? { |mod| mod.in?([ NoFlyList::TaggableRecord ]) }
37
37
  end
38
38
 
39
39
  puts "Checking #{taggable_classes.size} taggable classes:\n\n"
@@ -32,9 +32,9 @@ module NoFlyList
32
32
 
33
33
  def determine_adapter
34
34
  case taggable_class.connection.adapter_name.downcase
35
- when 'postgresql'
35
+ when "postgresql"
36
36
  :postgresql
37
- when 'mysql2'
37
+ when "mysql2"
38
38
  :mysql
39
39
  else
40
40
  :sqlite
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'mutation'
4
- require_relative 'query'
5
- require_relative 'tag_setup'
3
+ require_relative "mutation"
4
+ require_relative "query"
5
+ require_relative "tag_setup"
6
6
 
7
7
  module NoFlyList
8
8
  module TaggableRecord
@@ -90,11 +90,11 @@ module NoFlyList
90
90
  # Add the basic associations
91
91
  belongs_to :tag,
92
92
  class_name: setup.tag_class_name,
93
- foreign_key: 'tag_id'
93
+ foreign_key: "tag_id"
94
94
 
95
95
  belongs_to :taggable,
96
96
  class_name: setup.taggable_klass.name,
97
- foreign_key: 'taggable_id'
97
+ foreign_key: "taggable_id"
98
98
 
99
99
  include NoFlyList::TaggingRecord
100
100
  end
@@ -118,12 +118,12 @@ module NoFlyList
118
118
  # Set up the tag model associations
119
119
  setup.tag_class_name.constantize.class_eval do
120
120
  # Fix: Use 'tagging' for the join association when context is 'tag'
121
- association_name = (singular_name == 'tag' ? :taggings : :"#{singular_name}_taggings")
121
+ association_name = (singular_name == "tag" ? :taggings : :"#{singular_name}_taggings")
122
122
 
123
123
  has_many association_name,
124
124
  -> { where(context: singular_name) },
125
125
  class_name: setup.tagging_class_name,
126
- foreign_key: 'tag_id',
126
+ foreign_key: "tag_id",
127
127
  dependent: :destroy
128
128
 
129
129
  # Fix: Use consistent naming for through association
@@ -137,7 +137,7 @@ module NoFlyList
137
137
  setup.tagging_class_name.constantize.class_eval do
138
138
  belongs_to :tag,
139
139
  class_name: setup.tag_class_name,
140
- foreign_key: 'tag_id'
140
+ foreign_key: "tag_id"
141
141
 
142
142
  belongs_to :taggable,
143
143
  polymorphic: true
@@ -149,7 +149,7 @@ module NoFlyList
149
149
 
150
150
  validates :tag_id, uniqueness: {
151
151
  scope: %i[taggable_type taggable_id context],
152
- message: 'has already been tagged on this record in this context'
152
+ message: "has already been tagged on this record in this context"
153
153
  }
154
154
  end
155
155
  end
@@ -161,7 +161,7 @@ module NoFlyList
161
161
  has_many :"#{singular_name}_taggings",
162
162
  -> { where(context: singular_name) },
163
163
  class_name: setup.tagging_class_name,
164
- foreign_key: 'tag_id',
164
+ foreign_key: "tag_id",
165
165
  dependent: :destroy
166
166
 
167
167
  has_many :"#{singular_name}_taggables",
@@ -173,17 +173,17 @@ module NoFlyList
173
173
  setup.tagging_class_name.constantize.class_eval do
174
174
  belongs_to :tag,
175
175
  class_name: setup.tag_class_name,
176
- foreign_key: 'tag_id'
176
+ foreign_key: "tag_id"
177
177
 
178
178
  # For local tags, we use a simple belongs_to without polymorphic
179
179
  belongs_to :taggable,
180
180
  class_name: setup.taggable_klass.name,
181
- foreign_key: 'taggable_id'
181
+ foreign_key: "taggable_id"
182
182
 
183
183
  validates :tag, :taggable, :context, presence: true
184
184
  validates :tag_id, uniqueness: {
185
185
  scope: %i[taggable_id context],
186
- message: 'has already been tagged on this record in this context'
186
+ message: "has already been tagged on this record in this context"
187
187
  }
188
188
  end
189
189
  end
@@ -196,7 +196,7 @@ module NoFlyList
196
196
  has_many :"#{singular_name}_taggings",
197
197
  -> { where(context: singular_name) },
198
198
  class_name: setup.tagging_class_name,
199
- foreign_key: 'taggable_id',
199
+ foreign_key: "taggable_id",
200
200
  as: :taggable,
201
201
  dependent: :destroy
202
202
 
@@ -217,7 +217,7 @@ module NoFlyList
217
217
  has_many :"#{singular_name}_taggings",
218
218
  -> { where(context: singular_name) },
219
219
  class_name: setup.tagging_class_name,
220
- foreign_key: 'taggable_id',
220
+ foreign_key: "taggable_id",
221
221
  dependent: :destroy
222
222
 
223
223
  has_many setup.context,
@@ -237,9 +237,9 @@ module NoFlyList
237
237
  define_method :create_and_set_proxy do |instance_variable_name, setup|
238
238
  tag_model = if setup.polymorphic
239
239
  setup.tag_class_name.constantize
240
- else
240
+ else
241
241
  self.class.const_get("#{self.class.name}Tag")
242
- end
242
+ end
243
243
 
244
244
  proxy = TaggingProxy.new(
245
245
  self,
@@ -298,9 +298,9 @@ module NoFlyList
298
298
  end
299
299
 
300
300
  def define_constant_in_namespace(const_name)
301
- parts = const_name.split('::')
301
+ parts = const_name.split("::")
302
302
  const_name = parts.pop
303
- namespace = parts.join('::').safe_constantize || Object
303
+ namespace = parts.join("::").safe_constantize || Object
304
304
  return if namespace.const_defined?(const_name, false)
305
305
 
306
306
  namespace.const_set(const_name, yield)
@@ -40,8 +40,8 @@ module NoFlyList
40
40
  return none if tags.empty?
41
41
 
42
42
  count_function = Arel::Nodes::NamedFunction.new(
43
- 'COUNT',
44
- [Arel::Nodes::NamedFunction.new('DISTINCT', [tag_table[:name]])]
43
+ "COUNT",
44
+ [ Arel::Nodes::NamedFunction.new("DISTINCT", [ tag_table[:name] ]) ]
45
45
  )
46
46
 
47
47
  query = Arel::SelectManager.new(self)
@@ -78,11 +78,11 @@ module NoFlyList
78
78
  .where(context: singular_name)
79
79
  .where(taggable_type: name)
80
80
  .select(:taggable_id)
81
- else
81
+ else
82
82
  setup.tagging_class_name.constantize
83
83
  .where(context: singular_name)
84
84
  .select(:taggable_id)
85
- end
85
+ end
86
86
 
87
87
  where("#{table_name}.#{primary_key} NOT IN (?)", subquery)
88
88
  }
@@ -95,8 +95,8 @@ module NoFlyList
95
95
  send("without_#{context}")
96
96
  else
97
97
  Arel::Nodes::NamedFunction.new(
98
- 'COUNT',
99
- [Arel::Nodes::NamedFunction.new('DISTINCT', [tag_table[:id]])]
98
+ "COUNT",
99
+ [ Arel::Nodes::NamedFunction.new("DISTINCT", [ tag_table[:id] ]) ]
100
100
  )
101
101
 
102
102
  # Build the query for records having exactly the tags
@@ -40,8 +40,8 @@ module NoFlyList
40
40
  return none if tags.empty?
41
41
 
42
42
  count_function = Arel::Nodes::NamedFunction.new(
43
- 'COUNT',
44
- [Arel::Nodes::NamedFunction.new('DISTINCT', [tag_table[:name]])]
43
+ "COUNT",
44
+ [ Arel::Nodes::NamedFunction.new("DISTINCT", [ tag_table[:name] ]) ]
45
45
  )
46
46
 
47
47
  query = Arel::SelectManager.new(self)
@@ -78,11 +78,11 @@ module NoFlyList
78
78
  .where(context: singular_name)
79
79
  .where(taggable_type: name)
80
80
  .select(:taggable_id)
81
- else
81
+ else
82
82
  setup.tagging_class_name.constantize
83
83
  .where(context: singular_name)
84
84
  .select(:taggable_id)
85
- end
85
+ end
86
86
 
87
87
  where.not(primary_key => subquery)
88
88
  }
@@ -95,8 +95,8 @@ module NoFlyList
95
95
  send("without_#{context}")
96
96
  else
97
97
  Arel::Nodes::NamedFunction.new(
98
- 'COUNT',
99
- [Arel::Nodes::NamedFunction.new('DISTINCT', [tag_table[:id]])]
98
+ "COUNT",
99
+ [ Arel::Nodes::NamedFunction.new("DISTINCT", [ tag_table[:id] ]) ]
100
100
  )
101
101
 
102
102
  # Build the query for records having exactly the tags
@@ -40,8 +40,8 @@ module NoFlyList
40
40
  return none if tags.empty?
41
41
 
42
42
  count_function = Arel::Nodes::NamedFunction.new(
43
- 'COUNT',
44
- [Arel::Nodes::NamedFunction.new('DISTINCT', [tag_table[:name]])]
43
+ "COUNT",
44
+ [ Arel::Nodes::NamedFunction.new("DISTINCT", [ tag_table[:name] ]) ]
45
45
  )
46
46
 
47
47
  query = Arel::SelectManager.new(self)
@@ -70,7 +70,7 @@ module NoFlyList
70
70
  .pluck("#{table_name}.id")
71
71
 
72
72
  # Handle empty tagged_ids explicitly for SQLite compatibility
73
- where("#{table_name}.id NOT IN (?)", tagged_ids.present? ? tagged_ids : [-1])
73
+ where("#{table_name}.id NOT IN (?)", tagged_ids.present? ? tagged_ids : [ -1 ])
74
74
  }
75
75
 
76
76
  # Find records without any tags
@@ -79,12 +79,12 @@ module NoFlyList
79
79
  setup.tagging_class_name.constantize
80
80
  .where(context: singular_name, taggable_type: name)
81
81
  .select(:taggable_id)
82
- else
82
+ else
83
83
  setup.tagging_class_name.constantize
84
84
  .where(context: singular_name)
85
85
  .select(:taggable_id)
86
- end
87
- where('id NOT IN (?)', subquery)
86
+ end
87
+ where("id NOT IN (?)", subquery)
88
88
  }
89
89
 
90
90
  # Find records with exactly these tags
@@ -95,8 +95,8 @@ module NoFlyList
95
95
  send("without_#{context}")
96
96
  else
97
97
  Arel::Nodes::NamedFunction.new(
98
- 'COUNT',
99
- [Arel::Nodes::NamedFunction.new('DISTINCT', [tag_table[:id]])]
98
+ "COUNT",
99
+ [ Arel::Nodes::NamedFunction.new("DISTINCT", [ tag_table[:id] ]) ]
100
100
  )
101
101
 
102
102
  # Build the query for records having exactly the tags
@@ -130,8 +130,8 @@ module NoFlyList
130
130
  send("without_#{context}")
131
131
  else
132
132
  Arel::Nodes::NamedFunction.new(
133
- 'COUNT',
134
- [Arel::Nodes::NamedFunction.new('DISTINCT', [tag_table[:id]])]
133
+ "COUNT",
134
+ [ Arel::Nodes::NamedFunction.new("DISTINCT", [ tag_table[:id] ]) ]
135
135
  )
136
136
 
137
137
  # Build the query for records having exactly the tags
@@ -23,9 +23,9 @@ module NoFlyList
23
23
 
24
24
  def determine_adapter
25
25
  case ActiveRecord::Base.connection.adapter_name.downcase
26
- when 'postgresql'
26
+ when "postgresql"
27
27
  :postgresql
28
- when 'mysql2'
28
+ when "mysql2"
29
29
  :mysql
30
30
  else
31
31
  :sqlite
@@ -12,6 +12,10 @@ module NoFlyList
12
12
  before_validation :validate_tag_proxies
13
13
  end
14
14
 
15
+ def changed_for_autosave?
16
+ super || tag_proxies_changed?
17
+ end
18
+
15
19
  private
16
20
 
17
21
  def validate_tag_proxies
@@ -66,6 +70,19 @@ module NoFlyList
66
70
  tag_contexts[context.to_sym]
67
71
  end
68
72
 
73
+ def tag_proxies_changed?
74
+ return false if @saving_proxies || @validating_proxies
75
+
76
+ instance_variables.any? do |var|
77
+ next unless var.to_s.match?(/_list_proxy$/)
78
+
79
+ proxy = instance_variable_get(var)
80
+ next if proxy.nil?
81
+
82
+ proxy.changed?
83
+ end
84
+ end
85
+
69
86
  class_methods do
70
87
  def has_tags(*contexts, **options)
71
88
  contexts.each do |context|
@@ -26,6 +26,10 @@ module NoFlyList
26
26
  @pending_changes = []
27
27
  end
28
28
 
29
+ def changed?
30
+ @pending_changes.present? && @pending_changes != current_list_from_database
31
+ end
32
+
29
33
  def method_missing(method_name, *args)
30
34
  if current_list.respond_to?(method_name)
31
35
  current_list.send(method_name, *args)
@@ -47,7 +51,7 @@ module NoFlyList
47
51
  end
48
52
 
49
53
  def coerce(other)
50
- [other, to_a]
54
+ [ other, to_a ]
51
55
  end
52
56
 
53
57
  def to_ary
@@ -65,7 +69,7 @@ module NoFlyList
65
69
  model.class.transaction do
66
70
  # Always save parent first if needed
67
71
  if model.new_record? && !model.save
68
- errors.add(:base, 'Failed to save parent record')
72
+ errors.add(:base, "Failed to save parent record")
69
73
  raise ActiveRecord::Rollback
70
74
  end
71
75
 
@@ -160,7 +164,9 @@ module NoFlyList
160
164
  end
161
165
 
162
166
  def remove(tag)
163
- @pending_changes = current_list - [tag.to_s.strip]
167
+ old_list = current_list.dup
168
+ @pending_changes = current_list - [ tag.to_s.strip ]
169
+ mark_record_dirty if @pending_changes != old_list
164
170
  self
165
171
  end
166
172
 
@@ -170,7 +176,9 @@ module NoFlyList
170
176
  end
171
177
 
172
178
  def clear
179
+ old_list = current_list.dup
173
180
  @pending_changes = []
181
+ mark_record_dirty if @pending_changes != old_list
174
182
  self
175
183
  end
176
184
 
@@ -194,6 +202,19 @@ module NoFlyList
194
202
 
195
203
  private
196
204
 
205
+ def current_list_from_database
206
+ if setup[:polymorphic]
207
+ tagging_table = setup[:tagging_class_name].tableize
208
+ @model.send(@context.to_s)
209
+ .joins("INNER JOIN #{tagging_table} ON #{tagging_table}.tag_id = tags.id")
210
+ .where("#{tagging_table}.taggable_type = ? AND #{tagging_table}.taggable_id = ?",
211
+ @model.class.name, @model.id)
212
+ .pluck(:name)
213
+ else
214
+ @model.send(@context.to_s).pluck(:name)
215
+ end
216
+ end
217
+
197
218
  def set_list(_context, value)
198
219
  @pending_changes = transformer.parse_tags(value)
199
220
  valid? # Just check validity without raising
@@ -221,7 +242,7 @@ module NoFlyList
221
242
 
222
243
  # Transform tags to lowercase for comparison
223
244
  normalized_changes = @pending_changes.map(&:downcase)
224
- existing_tags = @tag_model.where('LOWER(name) IN (?)', normalized_changes).pluck(:name)
245
+ existing_tags = @tag_model.where("LOWER(name) IN (?)", normalized_changes).pluck(:name)
225
246
  missing_tags = @pending_changes - existing_tags
226
247
 
227
248
  return unless missing_tags.any?
@@ -279,20 +300,21 @@ module NoFlyList
279
300
  def current_list
280
301
  if @pending_changes.any?
281
302
  @pending_changes
282
- elsif setup[:polymorphic]
283
- tagging_table = setup[:tagging_class_name].tableize
284
- @model.send(@context.to_s)
285
- .joins("INNER JOIN #{tagging_table} ON #{tagging_table}.tag_id = tags.id")
286
- .where("#{tagging_table}.taggable_type = ? AND #{tagging_table}.taggable_id = ?",
287
- @model.class.name, @model.id)
288
- .pluck(:name)
289
303
  else
290
- @model.send(@context.to_s).pluck(:name)
304
+ current_list_from_database
291
305
  end
292
306
  end
293
307
 
294
308
  def limit_reached?
295
309
  @limit && current_list.size >= @limit
296
310
  end
311
+
312
+ def mark_record_dirty
313
+ return unless model.respond_to?(:changed_attributes)
314
+
315
+ # We use a virtual attribute name based on the context
316
+ # This ensures the record is marked as changed when tags are modified
317
+ model.send(:attribute_will_change!, "#{context}_list")
318
+ end
297
319
  end
298
320
  end
@@ -45,9 +45,9 @@ module NoFlyList
45
45
  def assert_polymorphic_tag_classes_exist(tags_klass, tagging_klass)
46
46
  # Verify they include the correct modules
47
47
  assert tags_klass.include?(NoFlyList::ApplicationTag),
48
- 'Polymorphic Tag should include NoFlyList::ApplicationTag'
48
+ "Polymorphic Tag should include NoFlyList::ApplicationTag"
49
49
  assert tagging_klass.include?(NoFlyList::ApplicationTagging),
50
- 'Polymorphic Tagging should include NoFlyList::ApplicationTagging'
50
+ "Polymorphic Tagging should include NoFlyList::ApplicationTagging"
51
51
  end
52
52
 
53
53
  def assert_local_tag_classes_exist(klass, context)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NoFlyList
4
- VERSION = '0.2.0'
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/no_fly_list.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_record'
4
- require 'active_support'
5
- require 'active_support/rails'
6
- require 'active_support/core_ext/numeric/time'
7
- require_relative 'no_fly_list/version'
8
- require 'no_fly_list/railtie' if defined?(Rails)
3
+ require "active_record"
4
+ require "active_support"
5
+ require "active_support/rails"
6
+ require "active_support/core_ext/numeric/time"
7
+ require_relative "no_fly_list/version"
8
+ require "no_fly_list/railtie" if defined?(Rails)
9
9
 
10
10
  module NoFlyList
11
11
  extend ActiveSupport::Autoload
@@ -16,10 +16,10 @@ module NoFlyList
16
16
  # Common tagging tables
17
17
  autoload :TaggableRecord
18
18
 
19
- autoload_under 'taggable_record' do
19
+ autoload_under "taggable_record" do
20
20
  autoload :Configuration
21
21
  autoload :Config
22
- autoload_under 'taggable_record/query' do
22
+ autoload_under "taggable_record/query" do
23
23
  autoload :SqliteStrategy
24
24
  autoload :MysqlStrategy
25
25
  autoload :PostgresqlStrategy
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: no_fly_list
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-09 00:00:00.000000000 Z
11
+ date: 2024-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -39,7 +39,7 @@ files:
39
39
  - lib/generators/no_fly_list/templates/create_application_tagging_table.rb.erb
40
40
  - lib/generators/no_fly_list/templates/create_tagging_table.rb.erb
41
41
  - lib/generators/no_fly_list/templates/tag_model.rb.erb
42
- - lib/generators/no_fly_list/templates/tag_parser.rb
42
+ - lib/generators/no_fly_list/templates/tag_transformer.rb
43
43
  - lib/generators/no_fly_list/templates/tagging_model.rb.erb
44
44
  - lib/generators/no_fly_list/transformer_generator.rb
45
45
  - lib/no_fly_list.rb