Class: Store::Digest::Entry
- Inherits:
-
Object
- Object
- Store::Digest::Entry
- Defined in:
- lib/store/digest/entry.rb,
lib/store/digest/readwrapper.rb
Overview
for the symbol
Defined Under Namespace
Classes: Flags
Constant Summary collapse
- TYPE_CHECKED =
flag constants
1 << 0
- TYPE_VALID =
1 << 1
- CHARSET_CHECKED =
1 << 2
- CHARSET_VALID =
1 << 3
- ENCODING_CHECKED =
1 << 4
- ENCODING_VALID =
1 << 5
- SYNTAX_CHECKED =
1 << 6
- SYNTAX_VALID =
1 << 7
- IS_CACHE =
1 << 8
- STRINGIO_MAX =
2**16
Instance Attribute Summary collapse
-
#charset ⇒ Object
readonly
Returns the value of attribute charset.
-
#ctime ⇒ Object
readonly
Returns the value of attribute ctime.
-
#dtime ⇒ Object
readonly
Returns the value of attribute dtime.
-
#encoding ⇒ Object
readonly
Returns the value of attribute encoding.
-
#flags ⇒ Object
Returns the value of attribute flags.
-
#language ⇒ Object
readonly
Returns the value of attribute language.
-
#mtime ⇒ Object
readonly
Returns the value of attribute mtime.
-
#ptime ⇒ Object
readonly
Returns the value of attribute ptime.
-
#store ⇒ Object
readonly
Returns the value of attribute store.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
Class Method Summary collapse
-
.coerce_digests(digests, algorithms: nil, empty: false, normative: nil) ⇒ Array<Symbol>, Hash{Symbol=>URI::NI}
This will take an array or hash or individual symbol or string or URI::NI object and try to coerce it into something it can use.
-
.scan(content, store: nil, digests: URI::NI.algorithms, mtime: nil, type: nil, language: nil, charset: nil, encoding: nil, blocksize: BLOCKSIZE, &block) ⇒ Store::Digest::Entry
Preemptively scan a blob and return an entry.
-
.scan_raw(content, algorithms: URI::NI.algorithms, blocksize: BLOCKSIZE, type: false) {|a| ... } ⇒ Array(Hash{Symbol=>URI::NI}, Integer)
Scan a blob and return the digests and byte count.
Instance Method Summary collapse
-
#add(store = nil) ⇒ Object
Add this entry to a Store::Digest instance.
-
#algorithms ⇒ Array
Return the algorithms used in the object.
-
#cache=(value) ⇒ void
Assigns the cache status.
-
#cache? ⇒ false, true
Returns whether the object is cache.
-
#charset_checked? ⇒ false, true
Returns true if the character set has been checked.
-
#charset_valid? ⇒ nil, ...
Returns true if the character set has been checked and is valid.
-
#close ⇒ self
No-op of IO#close.
-
#content ⇒ self?
Returns the content stored in the object.
-
#content=(content) ⇒ Object
Reset the content (and unset the scanned state).
-
#content? ⇒ false, true
Determines if there is content embedded in the object.
-
#deleted? ⇒ false, true
Just a plain old predicate to determine whether the blob has been deleted from the store (but implicitly the metadata record remains).
-
#digest(symbol) ⇒ URI::NI?
(also: #[])
Return a particular digest.
-
#digests ⇒ Hash
Get the digest hash.
-
#each(sep = $/, limit = nil, chomp: false) {|chunk| ... } ⇒ self
Iterate over the blob contents.
-
#encoding_checked? ⇒ false, true
Returns true if the content encoding (e.g. gzip, deflate) has been checked.
-
#encoding_valid? ⇒ nil, ...
Returns true if the content encoding has been checked and is valid.
-
#gets(sep = $/, chomp = false) ⇒ String
Emulate IO#gets.
-
#initialize(content = nil, store: nil, digests: nil, mtime: nil, type: nil, charset: nil, encoding: nil, language: nil, flags: 0, cache: false, strict: false, scan: false, &block) ⇒ Store::Digest::Entry
constructor
Create a new object, naively recording whatever it is handed.
- #inspect ⇒ Object
-
#open(*args) ⇒ self
No-op of IO#open for parity.
- #pos ⇒ Object (also: #tell)
- #pos=(position) ⇒ Object
-
#read(length = nil, buffer = nil) ⇒ String?
Emulate IO#read.
-
#remove(store = nil, forget: false) ⇒ Object
Remove this entry from a store.
-
#rewind ⇒ 0
Emulate IO#rewind.
-
#scan ⇒ self
Scan the blob if it hasn't already been scanned (idempotent).
-
#scan! ⇒ self
Scan the blob unconditionally.
-
#scanned? ⇒ false, true
Determines if the object has been scanned.
- #seek(offset, whence = IO::SEEK_SET) ⇒ Object
-
#size ⇒ Integer
Get the byte size.
-
#stale? ⇒ Boolean
If the entry is flagged as cache and the expiry time is in the past, then the entry is stale.
-
#stored? ⇒ nil, ...
Determine (if possible) if the object is in the store.
-
#syntax_checked? ⇒ false, true
Returns true if the blob's syntax has been checked.
-
#syntax_valid? ⇒ nil, ...
Returns true if the blob's syntax has been checked and is valid.
-
#to_h(content: false) ⇒ Hash
Return the object as a hash.
-
#to_s ⇒ String
Outputs a human-readable string representation of the object.
-
#type_charset ⇒ String
Returns the type and charset, suitable for an HTTP header.
-
#type_checked? ⇒ false, true
Returns true if the content type has been checked.
-
#type_valid? ⇒ nil, ...
Returns true if the content type has been checked and is valid.
Constructor Details
#initialize(content = nil, store: nil, digests: nil, mtime: nil, type: nil, charset: nil, encoding: nil, language: nil, flags: 0, cache: false, strict: false, scan: false, &block) ⇒ Store::Digest::Entry
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/store/digest/entry.rb', line 353 def initialize content = nil, store: nil, digests: nil, mtime: nil, type: nil, charset: nil, encoding: nil, language: nil, flags: 0, cache: false, strict: false, scan: false, &block # set the associated store, if one is passed in if store raise 'Store must be an instance of Store::Digest' unless store.is_a? Store::Digest @store = store end now = Time.now(in: ?Z) # this sets the empty digest hash and the scanning state to false self.content = content if content # we do this little ballet because `content=` may set mtime and type @mtime = mtime || @mtime || now type ||= @type || MimeMagic[nil] # the following can be strings or symbols: b = binding TOKENS.keys.each do |k| if x = b.local_variable_get(k) x = if strict coerce_token(x, k) else coerce_token(x, k) rescue nil end instance_variable_set "@#{k}", x.freeze if x end end # warn "wtf #{@type.inspect}" # we let the empty through digests = coerce_digests digests, empty: true if digests.is_a? Hash @digests = digests @algorithms = digests.empty? ? algorithms : digests.keys.to_set @scanned = !digests.empty? elsif !digests.empty? @algorithms = digests.to_set end # we use this for `#get` if block hash = block.call @content raise TypeError, "Block return value must be Hash, not #{hash.class}" unless hash.is_a? Hash # @scanned = true if hash[:digests] hash, content: true elsif @content.nil? raise ArgumentError, 'Must initialize with either content, or a block, or both' end # just make sure the times are in @ctime ||= now @mtime ||= mtime || @ctime @ptime ||= @ctime # set the flags @flags ||= Flags.from(flags || 0) if cache raise NotImplementedError, 'Associated store does not support caching' if @store and !@store.can_cache? @flags.cache = !!cache @dtime = compute_cache cache end # scan preemptively if so directed scan! if scan end |
Instance Attribute Details
#charset ⇒ Object (readonly)
Returns the value of attribute charset.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def charset @charset end |
#ctime ⇒ Object (readonly)
Returns the value of attribute ctime.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def ctime @ctime end |
#dtime ⇒ Object (readonly)
Returns the value of attribute dtime.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def dtime @dtime end |
#encoding ⇒ Object (readonly)
Returns the value of attribute encoding.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def encoding @encoding end |
#flags ⇒ Object
Returns the value of attribute flags.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def flags @flags end |
#language ⇒ Object (readonly)
Returns the value of attribute language.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def language @language end |
#mtime ⇒ Object (readonly)
Returns the value of attribute mtime.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def mtime @mtime end |
#ptime ⇒ Object (readonly)
Returns the value of attribute ptime.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def ptime @ptime end |
#store ⇒ Object (readonly)
Returns the value of attribute store.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def store @store end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
431 432 433 |
# File 'lib/store/digest/entry.rb', line 431 def type @type end |
Class Method Details
.coerce_digests(digests, algorithms: nil, empty: false, normative: nil) ⇒ Array<Symbol>, Hash{Symbol=>URI::NI}
This will take an array or hash or individual symbol or string or URI::NI object and try to coerce it into something it can use.
- Individual strings/symbols/URI::NI objects will get wrapped in an array.
- Strings will be scanned for conformance to RFC6920 and transformed into URI::NI objects if they match, otherwise they will be turned into symbols and matched against the repertoire of hash algorithms.
- If a URI::NI object isn't valid (e.g., not the full length, algorithm not supported), this will raise an error; likewise if the symbol is not in the repertoire of algorithms.
- Arrays must contain all the same kind of thing (strings, symbols, URI::NI objects)
- Hash keys must coerce to symbols (via
#to_s,#to_sym) that match the repertoire of algorithms. - Hash values must either be a string representing the decimal, base64, or hexadecimal digest of a length corresponding to the algorithm in the key, or a string representing an RFC6920 URI, or a URI::NI.
- (Base64 strings may be padded or not, and use the standard non-URL-safe representation, or not)
- Strings will then subsequently be transformed into URI::NI objects.
- Hash values that are (either already or coerced into) URI::NI objects must be valid and their algorithms must match the hash key with which they are associated.
The input (and thus the output) has two "moods":
- Anticipative: "These are the digest algorithms we want to see hashes for."
- Normative: "These are the hashes we already have for the input, and it should match them when scanned."
In general inputs that coerce to arrays (except arrays whose
contents coerce to URI::NI objects, which in turn will coerce to
hashes) are considered anticipative, whereas inputs that coerce to
hashes are considered normative. The return value will depend on
the adjudicated intent: Array for anticipative, Hash for
normative. The caller should inspect the return value to see which
it is, because the difference is whether a subsequent scan of the
content is intended to verify it (normative) or not (anticipative).
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 |
# File 'lib/store/digest/entry.rb', line 492 def self.coerce_digests digests, algorithms: nil, empty: false, normative: nil algorithms ||= URI::NI.algorithms # handle nil digests = [] if digests.nil? # first we coerce into an array; note hashes respond to `#to_a` digests = [digests] unless digests.respond_to? :to_a raise ArgumentError, 'Digest list can\'t be empty' if !empty and digests.empty? if digests.is_a? Hash # digests = digests[:digests] if digests.key? :digests out = digests.map do |k, v| # keys must go to symbols; symbols must be valid k = k.to_s.downcase.to_sym unless k.is_a? Symbol raise ArgumentError, "#{k} is not a supported algorithm in this configuration" unless algorithms.include? k # this should raise on any invalid values v = URI::NI.ingest k, v # then we assert that the result itself is valid raise ArgumentError, "Hash URI #{v} is invalid" unless v.valid? [k, v] end.to_h # warn out # note we are explicitly looking to see if normative is false # rather than nil return normative == false ? out.keys : out end # otherwise it should be an array so we'll make it into a set digests = digests.to_a.map do |thing| case thing when Symbol then thing when URI then URI::NI.ingest thing else # whatever it is, it should now be a string thing = thing.to_s if %r{^(?i:ni|https?)://}.match?(thing) and uri = URI::NI.ingest(thing) uri else # turn it into a symbol thing.strip.downcase.to_sym end end end.uniq # warn digests.inspect if digests.all? { |d| d.is_a? URI::NI } # we are expressly asking for anticipative if normative is literally false return digests.map(&:algorithm) if normative == false # otherwise if these are all digest URIs then this is normative; # return as a hash return digests.map do |d| raise ArgumentError, "#{d} is not a supported algorithm" unless algorithms.include? d.algorithm [d.algorithm.to_sym, d] end.to_h elsif digests.all? { |d| d.is_a? Symbol } raise ArgumentError, 'Normative expressly normative' if normative return digests end # if we get here, it's an error raise ArgumentError, 'Input must coerce to either all URIs or all Symbols' end |
.scan(content, store: nil, digests: URI::NI.algorithms, mtime: nil, type: nil, language: nil, charset: nil, encoding: nil, blocksize: BLOCKSIZE, &block) ⇒ Store::Digest::Entry
Preemptively scan a blob and return an entry.
735 736 737 738 739 740 741 |
# File 'lib/store/digest/entry.rb', line 735 def self.scan content, store: nil, digests: URI::NI.algorithms, mtime: nil, type: nil, language: nil, charset: nil, encoding: nil, blocksize: BLOCKSIZE, &block self.new content, store: store, digests: digests, mtime: mtime, type: type, language: language, charset: charset, encoding: encoding, scan: blocksize, &block end |
.scan_raw(content, algorithms: URI::NI.algorithms, blocksize: BLOCKSIZE, type: false) {|a| ... } ⇒ Array(Hash{Symbol=>URI::NI}, Integer)
The content is assumed to be at position zero.
Scan a blob and return the digests and byte count.
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 |
# File 'lib/store/digest/entry.rb', line 590 def self.scan_raw content, algorithms: URI::NI.algorithms, blocksize: BLOCKSIZE, type: false, &block # this will raise if it can't be coerced content = Store::Digest::ReadWrapper.coerce content # coerce digests digests = begin case algorithms when Array, -> x { x.respond_to? :to_a } algorithms.to_a.map(&:to_sym) when Symbol, -> x { x.respond_to? :to_sym } [algorithms.to_sym] else raise ArgumentError end rescue ArgumentError, TypeError, NoMethodError raise ArgumentError, "Digest algorithms must be coercible to an Array of Symbols" end # oh this shouldn't be empty btw raise ArgumentError, 'Algorithm list should not be empty' if digests.empty? # double-check if the digests are supported raise ArgumentError, "Unsupported digest algorithm(s) #{digests - URI::NI.algorithms}" unless (digests - URI::NI.algorithms).empty? # now queue up the contexts digests = digests.map { |d| [d, URI::NI.context(d)] }.to_h # we'll just make a uniform sequence to cycle through, why not procs = digests.values.map { |u| -> buf { u << buf } } procs << block if block if type sample = StringIO.new procs << -> buf do sample << buf # take this out of the loop if we have enough procs.pop if sample.pos >= SAMPLE end end bytes = 0 while buf = content.read(blocksize) buf = buf.to_s.b # ensure these are bytes we're reading bytes += buf.size procs.each { |b| b.call buf } end # apparently i do this because i painted myself into a corner with # URI::NI and/or past me previously discovered that there is much # more to the hash state than just the digest itself and forgot to # tell later-past me when i discovered it a second time around digests = digests.map do |k, v| [k, URI::NI.compute(v, algorithm: k).freeze] end.to_h # return the gathered information; everything else is out of band out = { digests: digests, size: bytes } if sample # felt cute lol out[:type] = %i[by_magic default_type].lazy.filter_map do |m| sample.rewind MimeMagic.send m, sample end.first end out end |
Instance Method Details
#add(store = nil) ⇒ Object
This entry will become associated with the store if it isn't already. If this entry has already been scanned, it will be scanned again.
Add this entry to a Store::Digest instance.
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 |
# File 'lib/store/digest/entry.rb', line 670 def add store = nil raise ArgumentError, 'no store associated with the entry and none passed in' if [store, @store].all?(&:nil?) # use the internal store if one is not supplied store ||= @store raise TypeError, 'Argument must be an instance of Store::Digest' unless store.is_a? Store::Digest # do this if not scanned unless scanned? && store.has?(self) # ok add the thing hash = store.send :add_raw, @content, ** hash, content: true end # set the internal store if one is supplied and not present; do # this after because calling store.has? will cause the record to # be scanned, potentially against the very store, so it would be # scanned by the same store twice. @store ||= store self end |
#algorithms ⇒ Array
Return the algorithms used in the object.
944 945 946 |
# File 'lib/store/digest/entry.rb', line 944 def algorithms @algorithms ||= (@store || URI::NI).algorithms.to_set end |
#cache=(value) ⇒ void
This method returns an undefined value.
Assigns the cache status.
1052 1053 1054 |
# File 'lib/store/digest/entry.rb', line 1052 def cache= value @flags.cache = !!value end |
#cache? ⇒ false, true
Returns whether the object is cache.
1042 1043 1044 |
# File 'lib/store/digest/entry.rb', line 1042 def cache? !!@flags.cache end |
#charset_checked? ⇒ false, true
Returns true if the character set has been checked.
1079 1080 1081 |
# File 'lib/store/digest/entry.rb', line 1079 def charset_checked? @flags.charset_checked end |
#charset_valid? ⇒ nil, ...
Returns true if the character set has been checked and is valid.
1087 1088 1089 1090 |
# File 'lib/store/digest/entry.rb', line 1087 def charset_valid? return nil unless @flags.charset_checked @flags.charset_valid end |
#close ⇒ self
No-op of IO#close.
922 923 924 925 |
# File 'lib/store/digest/entry.rb', line 922 def close rewind self end |
#content ⇒ self?
This is a vestigial method since Store::Digest::Entry now proxies IO calls.
Returns the content stored in the object.
987 988 989 |
# File 'lib/store/digest/entry.rb', line 987 def content self if @content end |
#content=(content) ⇒ Object
Reset the content (and unset the scanned state).
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 |
# File 'lib/store/digest/entry.rb', line 1003 def content= content @digests = {} @scanned = false @content = Store::Digest::ReadWrapper.coerce content, thunk: true if @content.respond_to?(:path) and path = @content.path # warn MimeMagic.by_path path @type = MimeMagic.by_path path end @mtime = @content.respond_to?(:stat) ? @content.stat.mtime : Time.now(in: ?Z) end |
#content? ⇒ false, true
Determines if there is content embedded in the object.
995 996 997 |
# File 'lib/store/digest/entry.rb', line 995 def content? !!@content end |
#deleted? ⇒ false, true
Just a plain old predicate to determine whether the blob has been deleted from the store (but implicitly the metadata record remains).
1156 1157 1158 |
# File 'lib/store/digest/entry.rb', line 1156 def deleted? stale? or @dtime && !cache? end |
#digest(symbol) ⇒ URI::NI? Also known as: []
Return a particular digest. Returns nil if there is no match.
972 973 974 975 976 |
# File 'lib/store/digest/entry.rb', line 972 def digest symbol raise ArgumentError, "This method takes a symbol" unless symbol.respond_to? :to_sym digests[symbol.to_sym] end |
#digests ⇒ Hash
Get the digest hash.
952 953 954 955 |
# File 'lib/store/digest/entry.rb', line 952 def digests scan @digests end |
#each(sep = $/, limit = nil, chomp: false) {|chunk| ... } ⇒ self
Iterate over the blob contents.
845 846 847 848 849 |
# File 'lib/store/digest/entry.rb', line 845 def each sep = $/, limit = nil, chomp: false, &block scan dereference? @content.each(sep, limit, chomp: chomp, &block) end |
#encoding_checked? ⇒ false, true
Returns true if the content encoding (e.g. gzip, deflate) has been checked.
1097 1098 1099 |
# File 'lib/store/digest/entry.rb', line 1097 def encoding_checked? @flags.encoding_checked end |
#encoding_valid? ⇒ nil, ...
Returns true if the content encoding has been checked and is valid.
1105 1106 1107 1108 |
# File 'lib/store/digest/entry.rb', line 1105 def encoding_valid? return nil unless @flags.encoding_checked @flags.encoding_valid end |
#gets(sep = $/, chomp = false) ⇒ String
Emulate IO#gets.
868 869 870 871 872 |
# File 'lib/store/digest/entry.rb', line 868 def gets sep = $/, chomp = false scan dereference? @content.gets sep, chomp end |
#inspect ⇒ Object
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 |
# File 'lib/store/digest/entry.rb', line 1202 def inspect text = if scanned? ds = digests.values.map(&:to_s).sort.join ', ' "size=#{size} type=#{type} (#{})" else "(not scanned)" end "<#{self.class} #{text}>" end |
#open(*args) ⇒ self
Once the blob is scanned, an internal file handle is opened and stays open.
No-op of IO#open for parity.
913 914 915 916 |
# File 'lib/store/digest/entry.rb', line 913 def open *args rewind self end |
#pos ⇒ Object Also known as: tell
880 881 882 883 884 |
# File 'lib/store/digest/entry.rb', line 880 def pos scan dereference? @content.pos end |
#pos=(position) ⇒ Object
888 889 890 891 892 |
# File 'lib/store/digest/entry.rb', line 888 def pos= position scan dereference? @content.pos = position end |
#read(length = nil, buffer = nil) ⇒ String?
Emulate IO#read.
857 858 859 860 861 862 |
# File 'lib/store/digest/entry.rb', line 857 def read length = nil, buffer = nil scan dereference? # this should be set by scan @content.read length, buffer end |
#remove(store = nil, forget: false) ⇒ Object
Remove this entry from a store. Dissociates the entry from the store in the process. Will not signal if the entry wasn't in the store to begin with.
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 |
# File 'lib/store/digest/entry.rb', line 705 def remove store = nil, forget: false raise ArgumentError, 'no store associated with the entry and none passed in' if [store, @store].all?(&:nil?) store ||= @store raise TypeError, 'store must be a Store::Digest instance' unless store.is_a? Store::Digest # eliminate the relationship @store = nil if @store.equal? store rm = forget ? :forget : true # this circumvents `private`; ignore return value store.send :get_raw, digests[store.primary], remove: rm self end |
#rewind ⇒ 0
Emulate IO#rewind.
898 899 900 901 902 903 904 |
# File 'lib/store/digest/entry.rb', line 898 def rewind scan dereference? # content should be rewindable after a scan @content.rewind end |
#scan ⇒ self
Scan the blob if it hasn't already been scanned (idempotent).
747 748 749 750 |
# File 'lib/store/digest/entry.rb', line 747 def scan scan! if @content && !scanned? self end |
#scan! ⇒ self
Scan the blob unconditionally. May raise an error if the byte size or digests are asserted in the constructor and don't match the scan.
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 |
# File 'lib/store/digest/entry.rb', line 761 def scan! raise Store::Digest::Error::Deleted, 'Entry has no content' unless @content if @store # we use the store if one is associated hash = @store.send :add_raw, @content, ** @content = hash[:content] elsif @content.respond_to? :rewind and seekable?(@content) # we don't need a temporary file; we'll just reuse this file handle @content.rewind hash = self.class.scan_raw @content, algorithms: @algorithms, type: true @content.rewind else # start with a stringio tmp = StringIO.new lam = -> buf do tmp << buf # check if it's too big if tmp.size >= STRINGIO_MAX # make an actual file file = Tempfile.create anonymous: true, binmode: true # put the string into it tmp.rewind file << tmp.read # reassign tmp with the file tmp = file # reassign lam with this condition removed so we don't # needlessly test it over and over with every iteration lam = -> buf { file << buf } end end # now we wrap lam in another block so it picks up the reassignment hash = self.class.scan_raw( @content, algorithms: @algorithms, type: true) { |buf| lam.call buf } tmp.rewind @content = tmp end # i suppose this is where the integrity is checked if @scanned # size raise Store::Digest::Error::Integrity, "Scanned size #{hash[:size]} does not match asserted #{@size}" if hash[:size] != @size # digests (@digests.keys & hash[:digests].keys).each do |k| scanned = hash[:digests][k] asserted = @digests[k] raise Store::Digest::Error::CryptographicIntegrity, "Scanned digest #{scanned} does not match asserted #{asserted}" if scanned != asserted end # XXX also do content type?? end hash # unconditionally set this now @scanned = true self end |
#scanned? ⇒ false, true
Determines if the object has been scanned.
835 836 837 |
# File 'lib/store/digest/entry.rb', line 835 def scanned? !!@scanned end |
#seek(offset, whence = IO::SEEK_SET) ⇒ Object
874 875 876 877 878 |
# File 'lib/store/digest/entry.rb', line 874 def seek offset, whence = IO::SEEK_SET scan dereference? @content.seek offset, whence end |
#size ⇒ Integer
Get the byte size.
961 962 963 964 |
# File 'lib/store/digest/entry.rb', line 961 def size scan @size end |
#stale? ⇒ Boolean
If the entry is flagged as cache and the expiry time is in the past, then the entry is stale.
1146 1147 1148 |
# File 'lib/store/digest/entry.rb', line 1146 def stale? cache? && @dtime && @dtime < Time.now(in: ?Z) end |
#stored? ⇒ nil, ...
Determine (if possible) if the object is in the store. Returns
nil if no store is associated with the entry, otherwise it will
query the store.
933 934 935 936 937 938 |
# File 'lib/store/digest/entry.rb', line 933 def stored? # warn @digests scan # warn scanned? @store.has?(digests) if @store end |
#syntax_checked? ⇒ false, true
Returns true if the blob's syntax has been checked.
1114 1115 1116 |
# File 'lib/store/digest/entry.rb', line 1114 def syntax_checked? @flags.syntax_checked end |
#syntax_valid? ⇒ nil, ...
Returns true if the blob's syntax has been checked and is valid.
1122 1123 1124 1125 |
# File 'lib/store/digest/entry.rb', line 1122 def syntax_valid? return nil unless @flags.syntax_checked @flags.syntax_valid end |
#to_h(content: false) ⇒ Hash
Return the object as a hash. Omits the content by default.
1165 1166 1167 1168 1169 1170 1171 |
# File 'lib/store/digest/entry.rb', line 1165 def to_h content: false main = %i[content digests] main.shift unless content (main + MANDATORY + OPTIONAL + [:flags]).map do |k| [k, send(k).dup] end.to_h end |
#to_s ⇒ String
Outputs a human-readable string representation of the object.
1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 |
# File 'lib/store/digest/entry.rb', line 1177 def to_s out = "#{self.class}\n Digests:\n" # disgorge the digests digests.values.sort { |a, b| a.to_s <=> b.to_s }.each do |d| out << " #{d}\n" end # now the fields MANDATORY.each { |m| out << " #{LABELS[m]}: #{send m}\n" } OPTIONAL.each do |o| val = send o out << " #{LABELS[o]}: #{val}\n" if val end # now the validation statuses out << "Validation:\n" FLAG.each_index do |i| x = flags.to_i >> (3 - i) & 3 out << (" %-16s: %s\n" % [FLAG[i], STATE[x]]) end out end |
#type_charset ⇒ String
Returns the type and charset, suitable for an HTTP header.
1020 1021 1022 1023 1024 |
# File 'lib/store/digest/entry.rb', line 1020 def type_charset out = type.to_s out += ";charset=#{charset}" if charset out end |
#type_checked? ⇒ false, true
Returns true if the content type has been checked.
1062 1063 1064 |
# File 'lib/store/digest/entry.rb', line 1062 def type_checked? @flags.type_checked end |
#type_valid? ⇒ nil, ...
Returns true if the content type has been checked and is valid.
1070 1071 1072 1073 |
# File 'lib/store/digest/entry.rb', line 1070 def type_valid? return nil unless @flags.type_checked @flags.type_valid end |