Class: NoFlyList::TaggingProxy
- Inherits:
-
Object
- Object
- NoFlyList::TaggingProxy
- Extended by:
- ActiveModel::Naming
- Includes:
- ActiveModel::Conversion, ActiveModel::Validations, Enumerable
- Defined in:
- lib/no_fly_list/tagging_proxy.rb
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
-
#tag_model ⇒ Object
readonly
Returns the value of attribute tag_model.
-
#transformer ⇒ Object
readonly
Returns the value of attribute transformer.
Instance Method Summary collapse
-
#add(*tags) ⇒ TaggingProxy
Adds one or more tags to the current tag list.
- #add!(*tags) ⇒ Object
-
#additions ⇒ Array<String>
Returns tags that will be added (not in database but in pending changes).
-
#changed? ⇒ Boolean
private
Determines if tags have changed from database state.
-
#clear ⇒ TaggingProxy
Clears all tags.
-
#clear! ⇒ TaggingProxy
Forces clearing all tags by destroying records.
-
#coerce(other) ⇒ Array
Handles numeric coercion.
- #count ⇒ Integer
-
#empty? ⇒ Boolean
Checks if tag list is empty.
-
#include?(tag) ⇒ Boolean
Checks if a tag exists in the list.
-
#initialize(model, tag_model, context, transformer: "ApplicationTagTransformer", restrict_to_existing: false, limit: nil) ⇒ TaggingProxy
constructor
Creates a new tagging proxy.
- #inspect ⇒ String
- #method_missing(method_name, *args) ⇒ Object
-
#persisted? ⇒ Boolean
private
Required by ActiveModel::Validations.
-
#removals ⇒ Array<String>
Returns tags that will be removed (in database but not in pending changes).
-
#remove(*tags) ⇒ TaggingProxy
Removes one or more tags from the current tag list.
- #remove!(tag) ⇒ Object
- #resolve_transformer(trans) ⇒ Object
- #respond_to_missing?(method_name, _include_private = false) ⇒ Boolean
-
#save ⇒ Boolean
True if the proxy is valid.
-
#save! ⇒ Boolean
True if the proxy is valid and the changes were saved.
- #size ⇒ Integer
- #to_a ⇒ Array<String>
- #to_ary ⇒ Object
- #to_s ⇒ String
-
#transformer_name ⇒ String
The name of the parser used to transform tags.
Constructor Details
#initialize(model, tag_model, context, transformer: "ApplicationTagTransformer", restrict_to_existing: false, limit: nil) ⇒ TaggingProxy
Creates a new tagging proxy
21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 21 def initialize(model, tag_model, context, transformer: "ApplicationTagTransformer", restrict_to_existing: false, limit: nil) @model = model @tag_model = tag_model @context = context @transformer = resolve_transformer(transformer) @restrict_to_existing = restrict_to_existing @limit = limit @pending_changes = nil # Use nil to indicate no changes yet @clear_operation = false end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 55 def method_missing(method_name, *args) if current_list.respond_to?(method_name) current_list.send(method_name, *args) else case method_name.to_s when /\A(.+)_list=\z/ set_list(::Regexp.last_match(1), args.first) when /\A(.+)_list\z/ get_list(::Regexp.last_match(1)) else super end end end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
9 10 11 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 9 def context @context end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
9 10 11 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 9 def model @model end |
#tag_model ⇒ Object (readonly)
Returns the value of attribute tag_model.
9 10 11 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 9 def tag_model @tag_model end |
#transformer ⇒ Object (readonly)
Returns the value of attribute transformer.
9 10 11 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 9 def transformer @transformer end |
Instance Method Details
#add(*tags) ⇒ TaggingProxy
Adds one or more tags to the current tag list
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 229 def add(*) return self if limit_reached? @clear_operation = false = if .size == 1 && .first.is_a?(String) transformer.(.first) else .flatten.map { |tag| transformer.(tag) }.flatten end return self if .empty? # Initialize @pending_changes with database values if not yet initialized @pending_changes = current_list_from_database if @pending_changes.nil? @pending_changes = @pending_changes + @pending_changes.uniq! mark_record_dirty self end |
#add!(*tags) ⇒ Object
249 250 251 252 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 249 def add!(*) add(*) save end |
#additions ⇒ Array<String>
Returns tags that will be added (not in database but in pending changes)
185 186 187 188 189 190 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 185 def additions return [] if @clear_operation return [] if @pending_changes.nil? @pending_changes - current_list_from_database end |
#changed? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Determines if tags have changed from database state
51 52 53 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 51 def changed? @clear_operation || (!@pending_changes.nil? && @pending_changes != current_list_from_database) end |
#clear ⇒ TaggingProxy
Clears all tags
289 290 291 292 293 294 295 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 289 def clear @clear_operation = true @pending_changes = [] mark_record_dirty if current_list_from_database.any? model.write_attribute("#{@context}_count", 0) if setup[:counter_cache] self end |
#clear! ⇒ TaggingProxy
Forces clearing all tags by destroying records
302 303 304 305 306 307 308 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 302 def clear! @model.send(@context.to_s).destroy_all @pending_changes = [] @clear_operation = false @model.update_column("#{@context}_count", 0) if setup[:counter_cache] self end |
#coerce(other) ⇒ Array
Handles numeric coercion
78 79 80 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 78 def coerce(other) [ other, to_a ] end |
#count ⇒ Integer
148 149 150 151 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 148 def count # Always return the database count for count operations @model.send(@context.to_s).count end |
#empty? ⇒ Boolean
Checks if tag list is empty
319 320 321 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 319 def empty? current_list.empty? end |
#include?(tag) ⇒ Boolean
Checks if a tag exists in the list
313 314 315 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 313 def include?(tag) current_list.include?(tag.to_s.strip) end |
#inspect ⇒ String
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 205 def inspect if @clear_operation = current_list_from_database "#<#{self.class.name} tags=[] changes=[CLEARING ALL (#{.size}): #{.inspect}] transformer_with=#{transformer_name}>" elsif !@pending_changes.nil? add_list = additions remove_list = removals changes = [] changes << "+#{add_list.inspect}" if add_list.any? changes << "-#{remove_list.inspect}" if remove_list.any? changes_str = changes.join(", ") "#<#{self.class.name} tags=#{current_list.inspect} changes=[#{changes_str}] transformer_with=#{transformer_name}>" else "#<#{self.class.name} tags=#{current_list.inspect} transformer_with=#{transformer_name}>" end end |
#persisted? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Required by ActiveModel::Validations
327 328 329 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 327 def persisted? false end |
#removals ⇒ Array<String>
Returns tags that will be removed (in database but not in pending changes)
194 195 196 197 198 199 200 201 202 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 194 def removals if @clear_operation current_list_from_database elsif @pending_changes.nil? [] else current_list_from_database - @pending_changes end end |
#remove(*tags) ⇒ TaggingProxy
Removes one or more tags from the current tag list
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 261 def remove(*) @clear_operation = false # Initialize @pending_changes with database values if not yet initialized @pending_changes = current_list_from_database if @pending_changes.nil? old_list = @pending_changes.dup = if .size == 1 && .first.is_a?(String) transformer.(.first) else .flatten.map { |tag| tag.to_s.strip } end @pending_changes = @pending_changes - mark_record_dirty if @pending_changes != old_list self end |
#remove!(tag) ⇒ Object
280 281 282 283 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 280 def remove!(tag) remove(tag) save end |
#resolve_transformer(trans) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 35 def resolve_transformer(trans) const = trans const = const.constantize if const.is_a?(String) unless const.respond_to?(:parse_tags) && const.respond_to?(:recreate_string) warn "NoFlyList: transformer #{trans.inspect} is invalid. Falling back to DefaultTransformer" const = NoFlyList::DefaultTransformer end const rescue NameError warn "NoFlyList: transformer #{trans.inspect} not found. Falling back to DefaultTransformer" NoFlyList::DefaultTransformer end |
#respond_to_missing?(method_name, _include_private = false) ⇒ Boolean
70 71 72 73 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 70 def respond_to_missing?(method_name, _include_private = false) current_list.respond_to?(method_name) || method_name.to_s =~ /\A(.+)_list(=)?\z/ end |
#save ⇒ Boolean
Returns true if the proxy is valid.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 87 def save return true unless changed? return false unless valid? # Prevent recursive validation @saving = true begin model.class.transaction do # Always save parent first if needed if model.new_record? && !model.save errors.add(:base, "Failed to save parent record") raise ActiveRecord::Rollback end # Clear existing tags old_count = model.send(context_taggings).count model.send(context_taggings).delete_all # Update counter model.update_column("#{@context}_count", 0) if setup[:counter_cache] # Create new tags pending_list.each do |tag_name| tag = find_or_create_tag(tag_name) next unless tag attributes = { tag: tag, context: @context.to_s.singularize } if setup[:polymorphic] attributes[:taggable_type] = model.class.name attributes[:taggable_id] = model.id end # Use create! to ensure we catch any errors model.send(context_taggings).create!(attributes) end end # Update counter to match the actual count model.update_column("#{@context}_count", pending_list.size) if setup[:counter_cache] refresh_from_database true rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved => e errors.add(:base, e.) false ensure @saving = false end end |
#save! ⇒ Boolean
Returns true if the proxy is valid and the changes were saved.
142 143 144 145 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 142 def save! valid? || raise(ActiveModel::ValidationError, self) save end |
#size ⇒ Integer
154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 154 def size # For size, return the database count if we've had a validation error if !valid? count # Otherwise show pending changes elsif @clear_operation 0 elsif !@pending_changes.nil? @pending_changes.size else count end end |
#to_a ⇒ Array<String>
169 170 171 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 169 def to_a current_list end |
#to_ary ⇒ Object
82 83 84 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 82 def to_ary current_list end |
#to_s ⇒ String
174 175 176 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 174 def to_s transformer.recreate_string(current_list) end |
#transformer_name ⇒ String
Returns The name of the parser used to transform tags.
179 180 181 |
# File 'lib/no_fly_list/tagging_proxy.rb', line 179 def transformer_name @transformer_name ||= transformer.name end |