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
Classes: Flags
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 ⇒ IO
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
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 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 |
# File 'lib/store/digest/object.rb', line 238 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.
327 328 329 |
# File 'lib/store/digest/object.rb', line 327 def charset @charset end |
#ctime ⇒ Object
Returns the value of attribute ctime.
327 328 329 |
# File 'lib/store/digest/object.rb', line 327 def ctime @ctime end |
#digests ⇒ Object (readonly)
XXX come up with a policy for these that isn’t stupid, plus input sanitation
326 327 328 |
# File 'lib/store/digest/object.rb', line 326 def digests @digests end |
#dtime ⇒ Object
Returns the value of attribute dtime.
327 328 329 |
# File 'lib/store/digest/object.rb', line 327 def dtime @dtime end |
#encoding ⇒ Object
Returns the value of attribute encoding.
327 328 329 |
# File 'lib/store/digest/object.rb', line 327 def encoding @encoding end |
#flags ⇒ Object
Returns the value of attribute flags.
327 328 329 |
# File 'lib/store/digest/object.rb', line 327 def flags @flags end |
#language ⇒ Object
Returns the value of attribute language.
327 328 329 |
# File 'lib/store/digest/object.rb', line 327 def language @language end |
#mtime ⇒ Object
Returns the value of attribute mtime.
327 328 329 |
# File 'lib/store/digest/object.rb', line 327 def mtime @mtime end |
#ptime ⇒ Object
Returns the value of attribute ptime.
327 328 329 |
# File 'lib/store/digest/object.rb', line 327 def ptime @ptime end |
#size ⇒ Object (readonly)
XXX come up with a policy for these that isn’t stupid, plus input sanitation
326 327 328 |
# File 'lib/store/digest/object.rb', line 326 def size @size end |
#type ⇒ Object
Returns the value of attribute type.
327 328 329 |
# File 'lib/store/digest/object.rb', line 327 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
331 332 333 334 335 336 337 |
# File 'lib/store/digest/object.rb', line 331 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.
442 443 444 |
# File 'lib/store/digest/object.rb', line 442 def algorithms (@digests || {}).keys.sort end |
#cache? ⇒ false, true
Returns whether the object is cache.
487 488 489 |
# File 'lib/store/digest/object.rb', line 487 def cache? !!@flags.cache end |
#charset_checked? ⇒ false, true
Returns true if the character set has been checked.
507 508 509 |
# File 'lib/store/digest/object.rb', line 507 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.
513 514 515 |
# File 'lib/store/digest/object.rb', line 513 def charset_valid? 0 != @flags.to_i & (CHARSET_CHECKED|CHARSET_VALID) end |
#content ⇒ IO
Returns the content stored in the object.
459 460 461 |
# File 'lib/store/digest/object.rb', line 459 def content @content.is_a?(Proc) ? @content.call : @content end |
#content? ⇒ false, true
Determines if there is content embedded in the object.
465 466 467 |
# File 'lib/store/digest/object.rb', line 465 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).
562 563 564 |
# File 'lib/store/digest/object.rb', line 562 def deleted? !!@dtime end |
#digest(symbol) ⇒ Symbol? Also known as: []
Return a particular digest. Returns nil if there is no match.
449 450 451 452 453 |
# File 'lib/store/digest/object.rb', line 449 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.
520 521 522 |
# File 'lib/store/digest/object.rb', line 520 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.
526 527 528 |
# File 'lib/store/digest/object.rb', line 526 def encoding_valid? 0 != @flags.to_i & (ENCODING_CHECKED|ENCODING_VALID) end |
#fresh=(state) ⇒ Object
436 437 438 |
# File 'lib/store/digest/object.rb', line 436 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.
432 433 434 |
# File 'lib/store/digest/object.rb', line 432 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
339 340 341 342 343 344 345 346 347 348 349 350 351 352 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 |
# File 'lib/store/digest/object.rb', line 339 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.
479 480 481 |
# File 'lib/store/digest/object.rb', line 479 def scanned? !@digests.empty? end |
#syntax_checked? ⇒ false, true
Returns true if the blob’s syntax has been checked.
532 533 534 |
# File 'lib/store/digest/object.rb', line 532 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.
538 539 540 |
# File 'lib/store/digest/object.rb', line 538 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.
569 570 571 572 573 574 575 |
# File 'lib/store/digest/object.rb', line 569 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.
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 |
# File 'lib/store/digest/object.rb', line 578 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.
471 472 473 474 475 |
# File 'lib/store/digest/object.rb', line 471 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.
495 496 497 |
# File 'lib/store/digest/object.rb', line 495 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.
501 502 503 |
# File 'lib/store/digest/object.rb', line 501 def type_valid? 0 != @flags.to_i & (TYPE_CHECKED|TYPE_VALID) end |