Class: Store::Digest::Object
- Inherits:
-
Object
- Object
- Store::Digest::Object
- Defined in:
- lib/store/digest/object.rb
Overview
Store entry object class.
Defined Under Namespace
Instance Attribute Summary collapse
-
#charset ⇒ Object
Returns the value of attribute charset.
-
#ctime ⇒ Object
Returns the value of attribute ctime.
-
#digests ⇒ Object
readonly
XXX come up with a policy for these that isn’t stupid, plus input sanitation.
-
#dtime ⇒ Object
Returns the value of attribute dtime.
-
#encoding ⇒ Object
Returns the value of attribute encoding.
-
#flags ⇒ Object
Returns the value of attribute flags.
-
#language ⇒ Object
Returns the value of attribute language.
-
#mtime ⇒ Object
Returns the value of attribute mtime.
-
#ptime ⇒ Object
Returns the value of attribute ptime.
-
#size ⇒ Object
readonly
XXX come up with a policy for these that isn’t stupid, plus input sanitation.
-
#type ⇒ Object
Returns the value of attribute type.
Class Method Summary collapse
Instance Method Summary collapse
-
#algorithms ⇒ Array
Return the algorithms used in the object.
-
#cache? ⇒ false, true
Returns whether the object is cache.
-
#charset_checked? ⇒ false, true
Returns true if the character set has been checked.
-
#charset_valid? ⇒ false, true
Returns true if the character set has been checked and is valid.
-
#content ⇒ #read
Returns the content stored in the object.
-
#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) ⇒ Symbol?
(also: #[])
Return a particular digest.
-
#encoding_checked? ⇒ false, true
Returns true if the content encoding (e.g. gzip, deflate) has been checked.
-
#encoding_valid? ⇒ false, true
Returns true if the content encoding has been checked and is valid.
- #fresh=(state) ⇒ Object
-
#fresh? ⇒ true, false
Determine (or set) whether the object is “fresh”, i.e.
-
#initialize(content = nil, digests: {}, size: 0, type: 'application/octet-stream', charset: nil, language: nil, encoding: nil, ctime: nil, mtime: nil, ptime: nil, dtime: nil, flags: 0, strict: true, fresh: false) ⇒ Store::Digest::Object
constructor
Create a new object, naively recording whatever is handed.
- #scan(content = nil, digests: URI::NI.algorithms, mtime: nil, type: nil, charset: nil, language: nil, encoding: nil, blocksize: BLOCKSIZE, strict: true, fresh: nil, &block) ⇒ Object
-
#scanned? ⇒ false, true
Determines if the object has been scanned.
-
#syntax_checked? ⇒ false, true
Returns true if the blob’s syntax has been checked.
-
#syntax_valid? ⇒ false, true
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 ⇒ Object
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? ⇒ false, true
Returns true if the content type has been checked and is valid.
Constructor Details
#initialize(content = nil, digests: {}, size: 0, type: 'application/octet-stream', charset: nil, language: nil, encoding: nil, ctime: nil, mtime: nil, ptime: nil, dtime: nil, flags: 0, strict: true, fresh: false) ⇒ Store::Digest::Object
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/store/digest/object.rb', line 256 def initialize content = nil, digests: {}, size: 0, type: 'application/octet-stream', charset: nil, language: nil, encoding: nil, ctime: nil, mtime: nil, ptime: nil, dtime: nil, flags: 0, strict: true, fresh: false # snag this immediately @fresh = !!fresh # check input on content @content = case content when nil then nil when IO, StringIO, Proc then content when String then StringIO.new content when Pathname then -> { content..open('rb') } when -> x { %i[read seek pos].all? { |m| x.respond_to? m } } content else raise ArgumentError, "Cannot accept content given as #{content.class}" end # check input on digests @digests = case digests when Hash # hash must be clean digests.map do |k, v| raise ArgumentError, 'Digest keys must be symbol-able' unless k.respond_to? :to_sym k = k.to_sym raise ArgumentError, 'Digest values must be URI::NI' unless v.is_a? URI::NI raise ArgumentError, 'Digest key must match value algorithm' unless k == v.algorithm [k.to_sym, v.dup.freeze] end.to_h when nil then {} # empty hash when Array # only accepts array of URI::NI digests.map do |x| raise ArgumentError, "Digests given as array can only be URI::NI, not #{x}" \ unless x.is_a? URI::NI [x.algorithm, x.dup.freeze] end.to_h when URI::NI then { digests.algorithm => digests.dup.freeze } else # everything else is invalid raise ArgumentError, "Cannot coerce digests given as #{digests.inspect}" end # ctime, mtime, ptime, dtime should be all nil or nonnegative # integers or Time or DateTime b = binding %i[ctime mtime ptime dtime].each do |k| v = coerce_time(b.local_variable_get(k), k) instance_variable_set "@#{k}", v end # set the flags @flags = Flags.from(flags || 0) @size = case size when nil then 0 when Numeric raise ArgumentError, 'size must be non-negative' if size < 0 size.to_i else raise ArgumentError, 'size must be nil or Numeric' end # the following can be strings or symbols: 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 end |
Instance Attribute Details
#charset ⇒ Object
Returns the value of attribute charset.
345 346 347 |
# File 'lib/store/digest/object.rb', line 345 def charset @charset end |
#ctime ⇒ Object
Returns the value of attribute ctime.
345 346 347 |
# File 'lib/store/digest/object.rb', line 345 def ctime @ctime end |
#digests ⇒ Object (readonly)
XXX come up with a policy for these that isn’t stupid, plus input sanitation
344 345 346 |
# File 'lib/store/digest/object.rb', line 344 def digests @digests end |
#dtime ⇒ Object
Returns the value of attribute dtime.
345 346 347 |
# File 'lib/store/digest/object.rb', line 345 def dtime @dtime end |
#encoding ⇒ Object
Returns the value of attribute encoding.
345 346 347 |
# File 'lib/store/digest/object.rb', line 345 def encoding @encoding end |
#flags ⇒ Object
Returns the value of attribute flags.
345 346 347 |
# File 'lib/store/digest/object.rb', line 345 def flags @flags end |
#language ⇒ Object
Returns the value of attribute language.
345 346 347 |
# File 'lib/store/digest/object.rb', line 345 def language @language end |
#mtime ⇒ Object
Returns the value of attribute mtime.
345 346 347 |
# File 'lib/store/digest/object.rb', line 345 def mtime @mtime end |
#ptime ⇒ Object
Returns the value of attribute ptime.
345 346 347 |
# File 'lib/store/digest/object.rb', line 345 def ptime @ptime end |
#size ⇒ Object (readonly)
XXX come up with a policy for these that isn’t stupid, plus input sanitation
344 345 346 |
# File 'lib/store/digest/object.rb', line 344 def size @size end |
#type ⇒ Object
Returns the value of attribute type.
345 346 347 |
# File 'lib/store/digest/object.rb', line 345 def type @type end |
Class Method Details
.scan(content, digests: URI::NI.algorithms, mtime: nil, type: nil, language: nil, charset: nil, encoding: nil, blocksize: BLOCKSIZE, strict: true, fresh: false, &block) ⇒ Object
349 350 351 352 353 354 355 |
# File 'lib/store/digest/object.rb', line 349 def self.scan content, digests: URI::NI.algorithms, mtime: nil, type: nil, language: nil, charset: nil, encoding: nil, blocksize: BLOCKSIZE, strict: true, fresh: false, &block self.new.scan content, digests: digests, mtime: mtime, type: type, language: language, charset: charset, encoding: encoding, blocksize: blocksize, strict: strict, fresh: fresh, &block end |
Instance Method Details
#algorithms ⇒ Array
Return the algorithms used in the object.
460 461 462 |
# File 'lib/store/digest/object.rb', line 460 def algorithms (@digests || {}).keys.sort end |
#cache? ⇒ false, true
Returns whether the object is cache.
508 509 510 |
# File 'lib/store/digest/object.rb', line 508 def cache? !!@flags.cache end |
#charset_checked? ⇒ false, true
Returns true if the character set has been checked.
528 529 530 |
# File 'lib/store/digest/object.rb', line 528 def charset_checked? 0 != @flags.to_i & CHARSET_CHECKED end |
#charset_valid? ⇒ false, true
Returns true if the character set has been checked and is valid.
534 535 536 |
# File 'lib/store/digest/object.rb', line 534 def charset_valid? 0 != @flags.to_i & (CHARSET_CHECKED|CHARSET_VALID) end |
#content ⇒ #read
Returns the content stored in the object.
479 480 481 482 |
# File 'lib/store/digest/object.rb', line 479 def content io = @content.is_a?(Proc) ? @content.call : @content io = io ? IOWrapper.new(self, io) : io end |
#content? ⇒ false, true
Determines if there is content embedded in the object.
486 487 488 |
# File 'lib/store/digest/object.rb', line 486 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).
583 584 585 |
# File 'lib/store/digest/object.rb', line 583 def deleted? !!@dtime end |
#digest(symbol) ⇒ Symbol? Also known as: []
Return a particular digest. Returns nil if there is no match.
467 468 469 470 471 |
# File 'lib/store/digest/object.rb', line 467 def digest symbol raise ArgumentError, "This method takes a symbol" unless symbol.respond_to? :to_sym digests[symbol.to_sym] end |
#encoding_checked? ⇒ false, true
Returns true if the content encoding (e.g. gzip, deflate) has been checked.
541 542 543 |
# File 'lib/store/digest/object.rb', line 541 def encoding_checked? 0 != @flags.to_i & ENCODING_CHECKED end |
#encoding_valid? ⇒ false, true
Returns true if the content encoding has been checked and is valid.
547 548 549 |
# File 'lib/store/digest/object.rb', line 547 def encoding_valid? 0 != @flags.to_i & (ENCODING_CHECKED|ENCODING_VALID) end |
#fresh=(state) ⇒ Object
454 455 456 |
# File 'lib/store/digest/object.rb', line 454 def fresh= state @fresh = !!state end |
#fresh? ⇒ true, false
Determine (or set) whether the object is “fresh”, i.e. whether it is new (or restored), or had been previously been in the store.
450 451 452 |
# File 'lib/store/digest/object.rb', line 450 def fresh? !!@fresh end |
#scan(content = nil, digests: URI::NI.algorithms, mtime: nil, type: nil, charset: nil, language: nil, encoding: nil, blocksize: BLOCKSIZE, strict: true, fresh: nil, &block) ⇒ Object
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 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/store/digest/object.rb', line 357 def scan content = nil, digests: URI::NI.algorithms, mtime: nil, type: nil, charset: nil, language: nil, encoding: nil, blocksize: BLOCKSIZE, strict: true, fresh: nil, &block # update freshness if there is something to update @fresh = !!fresh unless fresh.nil? # we put all the scanning stuff in here content = case content when nil then self.content when IO, StringIO then content when String then StringIO.new content when Pathname then content.open('rb') when Proc then content.call when -> x { %i[read seek pos].all? { |m| x.respond_to? m } } content else raise ArgumentError, "Cannot scan content of type #{content.class}" end content.binmode if content.respond_to? :binmode # sane default for mtime @mtime = coerce_time(mtime || @mtime || (content.respond_to?(:mtime) ? content.mtime : Time.now(in: ?Z)), :mtime) # eh, *some* code reuse 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 digests = case digests when Array then digests when Symbol then [digests] else raise ArgumentError, 'Digests must be one or more symbol' end raise ArgumentError, "Invalid digest list #{digests - URI::NI.algorithms}" unless (digests - URI::NI.algorithms).empty? # set up the contexts digests = digests.map { |d| [d, URI::NI.context(d)] }.to_h # sample for mime type checking sample = StringIO.new '' @size = 0 while buf = content.read(blocksize) @size += buf.size sample << buf if sample.pos < SAMPLE digests.values.each { |ctx| ctx << buf } block.call buf if block_given? end # seek the content back to the front and store it content.seek 0, 0 @content = content # set up the digests @digests = digests.map do |k, v| [k, URI::NI.compute(v, algorithm: k).freeze] end.to_h.freeze # ensure there is the most generic of possible types type ||= 'application/octet-stream'.freeze # obtain the sampled content type ts = MimeMagic.by_magic(sample) || MimeMagic.default_type(sample) if content.respond_to? :path # may as well use the path if it's available and more specific ps = MimeMagic.by_path(content.path.to_s) # XXX the need to do ts.to_s is a bug in mimemagic ts = ps if ps and ps.descendant_of?(ts.to_s) end # set the type to ts if it is more specific @type = ts.descendant_of?(type.to_s) ? ts.to_s.freeze : type.to_s.dup.downcase.freeze self end |
#scanned? ⇒ false, true
Determines if the object has been scanned.
500 501 502 |
# File 'lib/store/digest/object.rb', line 500 def scanned? !@digests.empty? end |
#syntax_checked? ⇒ false, true
Returns true if the blob’s syntax has been checked.
553 554 555 |
# File 'lib/store/digest/object.rb', line 553 def syntax_checked? 0 != @flags.to_i & SYNTAX_CHECKED end |
#syntax_valid? ⇒ false, true
Returns true if the blob’s syntax has been checked and is valid.
559 560 561 |
# File 'lib/store/digest/object.rb', line 559 def syntax_valid? 0 != @flags.to_i & (SYNTAX_CHECKED|SYNTAX_VALID) end |
#to_h(content: false) ⇒ Hash
Return the object as a hash. Omits the content by default.
590 591 592 593 594 595 596 |
# File 'lib/store/digest/object.rb', line 590 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 ⇒ Object
Outputs a human-readable string representation of the object.
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 |
# File 'lib/store/digest/object.rb', line 599 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.
492 493 494 495 496 |
# File 'lib/store/digest/object.rb', line 492 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.
516 517 518 |
# File 'lib/store/digest/object.rb', line 516 def type_checked? 0 != @flags.to_i & TYPE_CHECKED end |
#type_valid? ⇒ false, true
Returns true if the content type has been checked and is valid.
522 523 524 |
# File 'lib/store/digest/object.rb', line 522 def type_valid? 0 != @flags.to_i & (TYPE_CHECKED|TYPE_VALID) end |