Module: Fragmentary::Fragment
- Defined in:
- lib/fragmentary/fragment.rb
Defined Under Namespace
Modules: ClassMethods, NeedsKey, NeedsRecordId, NeedsUserId, NeedsUserType
Class Method Summary collapse
Instance Method Summary collapse
- #cache_exist? ⇒ Boolean
- #cache_store ⇒ Object
-
#child(options) ⇒ Object
Note that this method can be called in two different contexts.
-
#child_search_key ⇒ Object
Instance Methods —————-.
-
#content ⇒ Object
Typically used along with #cache_exist? when testing from the console.
- #delete_cache ⇒ Object
-
#delete_cache_tree ⇒ Object
Recursively delete the cache entry for this fragment and all of its children Does NOT destroy the fragment or its children.
- #delete_matched_cache ⇒ Object
-
#destroy(options = {}) ⇒ Object
delete the associated cache content before destroying the fragment.
- #existing_child(options) ⇒ Object
-
#fragment_key ⇒ Object
This emulates the result of passing the fragment object to AbstractController::Caching::Fragments#combined_fragment_cache_key when the cache helper method invokes controller.read_fragment from the view.
-
#record_type ⇒ Object
If this fragment’s class needs a record_id, it will also have a record_type.
-
#request ⇒ Object
Returns a Request object that can be used to send a server request for the fragment content.
-
#request_method ⇒ Object
Request-related methods…
- #request_options ⇒ Object
- #request_parameters ⇒ Object
-
#request_queues ⇒ Object
Though each fragment is typically associated with a particular user_type, touching a root fragment will send page requests for the path associated with the fragment to queues for all relevant user_types for this fragment class.
- #requestable? ⇒ Boolean
- #set_indexed_children ⇒ Object
- #touch(*args, no_request: false) ⇒ Object
-
#touch_or_destroy ⇒ Object
Touch this fragment and all descendants that have entries in the cache.
-
#touch_tree(no_request: false) ⇒ Object
Recursively touch the fragment and all of its children.
Class Method Details
.base_class ⇒ Object
5 6 7 |
# File 'lib/fragmentary/fragment.rb', line 5 def self.base_class @base_class end |
.included(base) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/fragmentary/fragment.rb', line 9 def self.included(base) @base_class = base base.class_eval do include ActionView::Helpers::CacheHelper belongs_to :parent, :class_name => name belongs_to :root, :class_name => name has_many :children, :class_name => name, :foreign_key => :parent_id, :dependent => :destroy belongs_to :user # Don't touch the parent when we create the child - the child was created by # renderng the parent, which occured because the parent was touched, thus # triggering the current request. Touching it again would result in a # redundant duplicate request. after_commit :touch_parent, :on => [:update, :destroy] attr_accessor :indexed_children # Set cache timestamp format to :usec instead of :nsec because the latter is greater precision than Postgres supports, # resulting in mismatches between timestamps on a newly created fragment and one retrieved from the database. # Probably not needed for Rails 5, which uses :usec by default. self. = :usec end base.instance_eval do class << self; attr_writer :record_type, :key_name; end end base.extend ClassMethods ActionView::Base.send :include, FragmentsHelper end |
Instance Method Details
#cache_exist? ⇒ Boolean
543 544 545 546 |
# File 'lib/fragmentary/fragment.rb', line 543 def cache_exist? # expand_cache_key calls cache_key and prepends "views/" cache_store.exist?(fragment_key) end |
#cache_store ⇒ Object
492 493 494 |
# File 'lib/fragmentary/fragment.rb', line 492 def cache_store self.class.cache_store end |
#child(options) ⇒ Object
Note that this method can be called in two different contexts. One is as part of rendering the parent fragment, which means that the parent was obtained using either Fragment.root or a previous invocation of this method. In this case, the children will have already been loaded and indexed. The second is when the child is being rendered on its own, e.g. inserted by ajax into a parent that is already on the page. In this case the children won’t have already been loaded or indexed.
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 |
# File 'lib/fragmentary/fragment.rb', line 442 def child() if child = [:child] raise ArgumentError, "You passed a child fragment to a parent it's not a child of." unless child.parent_id == self.id child else existing = .delete(:existing) # root_id and parent_id are passed from parent to child. For all except root fragments, root_id is stored explicitly. = {:root_id => root_id || id} # record_id is passed from parent to child unless it is required to be provided explicitly. .merge!(:record_id => record_id) unless [:type].constantize.needs_record_id? klass, search_attributes, = Fragment.base_class.attributes(.reverse_merge()) # Try to find the child within the children loaded previously select_attributes = search_attributes.merge(:type => klass.name) if child_search_key and keyed_children = indexed_children.try(:[], select_attributes[child_search_key]) # If the key was found we don't need to include it in the attributes used for the final selection select_attributes.delete(child_search_key) end # If there isn't a key or there isn't set of previously indexed_children (e.g. the child is being rendered # on its own), we just revert to the regular children association. fragment = (keyed_children || children).to_a.find{|child| select_attributes.all?{|key, value| child.send(key) == value}} # If we didn't find an existing child, create a new record unless only an existing record was requested unless fragment or existing fragment = klass.new(search_attributes.merge()) children << fragment # Saves the fragment and sets the parent_id attribute end # Load the grandchildren, so they'll each be available later. Index them if a search key is available. if fragment fragment_children = fragment.children fragment.set_indexed_children if fragment.child_search_key end fragment end end |
#child_search_key ⇒ Object
Instance Methods
423 424 425 |
# File 'lib/fragmentary/fragment.rb', line 423 def child_search_key self.class.child_search_key end |
#content ⇒ Object
Typically used along with #cache_exist? when testing from the console. Note that both methods will only return correct results for fragments associated with the application_root_url (either root or children) corresponding to the particular console session in use. i.e. you can’t see into the production cache from a prerelease console session and vice versa.
553 554 555 |
# File 'lib/fragmentary/fragment.rb', line 553 def content cache_store.read(fragment_key) end |
#delete_cache ⇒ Object
513 514 515 |
# File 'lib/fragmentary/fragment.rb', line 513 def delete_cache cache_store.delete(fragment_key) end |
#delete_cache_tree ⇒ Object
Recursively delete the cache entry for this fragment and all of its children Does NOT destroy the fragment or its children
519 520 521 522 |
# File 'lib/fragmentary/fragment.rb', line 519 def delete_cache_tree children.each(&:delete_cache_tree) delete_cache if cache_exist? end |
#delete_matched_cache ⇒ Object
509 510 511 |
# File 'lib/fragmentary/fragment.rb', line 509 def delete_matched_cache cache_store.delete_matched(Regexp.new("#{self.class.model_name.cache_key}/#{id}")) end |
#destroy(options = {}) ⇒ Object
delete the associated cache content before destroying the fragment
503 504 505 506 507 |
# File 'lib/fragmentary/fragment.rb', line 503 def destroy( = {}) .delete(:delete_matches) ? delete_matched_cache : delete_cache @no_request = .delete(:no_request) # stored for use in #touch_parent via the after_commit callback super() end |
#existing_child(options) ⇒ Object
433 434 435 |
# File 'lib/fragmentary/fragment.rb', line 433 def existing_child() child(.merge(:existing => true)) end |
#fragment_key ⇒ Object
This emulates the result of passing the fragment object to AbstractController::Caching::Fragments#combined_fragment_cache_key when the cache helper method invokes controller.read_fragment from the view. The result can be passed to ActiveSupport::Cache methods #read, #write, #fetch, #delete, and #exist?
560 561 562 |
# File 'lib/fragmentary/fragment.rb', line 560 def fragment_key ['views', self] end |
#record_type ⇒ Object
If this fragment’s class needs a record_id, it will also have a record_type. If not, we copy the record_id from the parent, if it has one.
482 483 484 |
# File 'lib/fragmentary/fragment.rb', line 482 def record_type @record_type ||= self.class.needs_record_id? ? self.class.record_type : self.parent.record_type end |
#request ⇒ Object
Returns a Request object that can be used to send a server request for the fragment content
593 594 595 |
# File 'lib/fragmentary/fragment.rb', line 593 def request requestable? ? @request ||= Request.new(request_method, request_path, request_parameters, ) : nil end |
#request_method ⇒ Object
Request-related methods… Note: subclasses that define request_path need to also define self.request_path and should define the instance method in terms of the class method. Likewise, those that define request_parameters also need to defined self.request_parameters and define the instance method in terms of the class method. Subclasses generally don’t need to define request_method or request_options, but may need to define self.request_options. The instance method version request_options is defined in terms of the class method below.
Also… subclasses that define request_path also need to define self.request, but not the instance method request since that is defined below in terms of its constituent request arguments. The reason is that the class method self.request generally takes a parameter (e.g. a record_id or a key), and this is used in different ways depending on the class, whereas the instance method takes the same form regardless of the class.
576 577 578 |
# File 'lib/fragmentary/fragment.rb', line 576 def request_method self.class.request_method end |
#request_options ⇒ Object
584 585 586 |
# File 'lib/fragmentary/fragment.rb', line 584 def self.class. end |
#request_parameters ⇒ Object
580 581 582 |
# File 'lib/fragmentary/fragment.rb', line 580 def request_parameters self.class.request_parameters # -> nil end |
#request_queues ⇒ Object
Though each fragment is typically associated with a particular user_type, touching a root fragment will send page requests for the path associated with the fragment to queues for all relevant user_types for this fragment class.
488 489 490 |
# File 'lib/fragmentary/fragment.rb', line 488 def request_queues self.class.request_queues end |
#requestable? ⇒ Boolean
588 589 590 |
# File 'lib/fragmentary/fragment.rb', line 588 def requestable? @requestable ||= respond_to? :request_path end |
#set_indexed_children ⇒ Object
427 428 429 430 431 |
# File 'lib/fragmentary/fragment.rb', line 427 def set_indexed_children return unless child_search_key obj = Hash.new {|h, indx| h[indx] = []} @indexed_children = children.each_with_object(obj) {|child, collection| collection[child.send(child_search_key)] << child } end |
#touch(*args, no_request: false) ⇒ Object
496 497 498 499 500 |
# File 'lib/fragmentary/fragment.rb', line 496 def touch(*args, no_request: false) @no_request = no_request # stored for use in #touch_parent via the after_commit callback request_queues.each{|key, hsh| hsh.each{|key2, queue| queue << request}} if request && !no_request super(*args) end |
#touch_or_destroy ⇒ Object
Touch this fragment and all descendants that have entries in the cache. Destroy any that don’t have cache entries.
533 534 535 536 537 538 539 540 541 |
# File 'lib/fragmentary/fragment.rb', line 533 def touch_or_destroy if cache_exist? children.each(&:touch_or_destroy) # if there are children, this will be touched automatically once they are. touch(:no_request => true) unless children.any? else destroy(:no_request => true) # will also destroy all children because of :dependent => :destroy end end |
#touch_tree(no_request: false) ⇒ Object
Recursively touch the fragment and all of its children
525 526 527 528 529 |
# File 'lib/fragmentary/fragment.rb', line 525 def touch_tree(no_request: false) children.each{|child| child.touch_tree(:no_request => no_request)} # If there are children, we'll have already touched this fragment in the process of touching them. touch(:no_request => no_request) unless children.any? end |