class Card

Cards are wiki-inspired building blocks.

This documentation is for developers who want to understand:

1. how ruby Card objects work, and
2. how to extend them.

It assumes that you’ve already read the introductory text in {file:README.md}.

Throughout this document we will refer to @card as an instance of a Card object.

## Names

There are four important card identifiers, sometimes called “marks”. Every card has a unique name, key, and id. Some cards also have a codename.

@card.name     # The name, a Card::Name object, is the most recognizable card
               # mark.
@card.key      # The key, a String, is a simple lower-case name variant.
@card.id       # The id is an Integer.
@card.codename # The codename, a Symbol, is the name by which a card can be
               # referred to in code.

All names with the same key (including the key itself) are considered variants of each other. No two cards can have names with the same key. {Card::Name} objects inherit from Strings but add many other methods for common card name patterns, eg ‘“A+B”.to_name.right => “B”`.

Setting a card’s name, eg ‘@card.name = “New Name”`, will automatically update the key.

## Type

Every card has a type, and every type itself has an associated card. For example, Paula’s type might be User, so there is also a User card.

The type may be accessed in several ways:

    @card.type_id      # returns id of type card [Integer]
    @card.type_name    # returns name of type card [Card::Name]
    @card.type_code    # returns codename of type card [Symbol]
    @card.type_card    # returns Cardtype card associated with @card's type [Card]

- {Set::All::Type Common type methods}

## Content

There are two primary methods for accessing a card’s content:

@card.db_content   # the content as it appears in the database
@card.content      # the "official" content, which may be different from

db_content when db_content is overridden with a structure rule.

- {Content Processing card content}
- {Set::All::Content Common content methods}

## Fetch

The two main ways to retrieve cards are fetching (retrieving cards one at a time) and querying (retrieving lists of cards). More on querying below.

Any of the above marks (name, key, id, codename) can be used to fetch a card, eg:

@card1 = Card.fetch "Garden" # returns the card with the name "Garden" (or, more

precisely, with the key “garden”)

@card2 = Card.fetch 100      # returns the card with the id 100
@card3 = Card.fetch :help    # returns the card with the codename help

The fetch API will first try to find the card in the cache and will only look in the database if necessary.

The ‘Card[]` shortcut will return the same results but does not support the full range of advanced options and will not return virtual cards (cards that can be constructed from naming patterns but are not actually in the database).

# equivalent to the above but more concise
@card1 = Card["Garden"]
@card2 = Card[100]
@card3 = Card[:help]

Better still, you can use the ‘#card` method on Strings, Integers, Symbols, and Arrays

# equivalent to the above but even more concise
@card1 = "Garden".card
@card2 = 100.card
@card3 = :help.card

The ‘#card_id`, `#cardname`, and `#codename` methods work on all the same objects and provide convenient shortcuts for quickly fetching and returning card attributes.

- {Card::Fetch::CardClass More on fetching.}

## Query

Card queries find and return lists of cards, eg:

    Card.search type_id: 4 # returns an Array of cards with the type_id of 4.

- {Card::Query More on queries}

## Views and Events

Views and events are a _Shark’s_ primary tools for manipulating cards. Views customize card presentation, while events customize card transactions. Or, if you like, views and events respectively alter cards in space and time.

Both views and events are defined in {Cardio::Mod mods}, short for modules or modifications.

- {Set::Format::AbstractFormat#view More on views}
- {Set::Event::Api#event More on events}

## Accounts and Permissions

Card code is always executed in the context of a given user account. Permissions for that account are automatically checked when running a query, performing an action, or rendering a view. A typical query, for example, can only return cards that the current user has permission to read.

You can see the current user with ‘Card::Auth.current`. The permissions of a proxy user can be temporarily assumed using `Card::Auth#as`.

{Card::Auth More on accounts}

codename retrieval methods for cards

Public Instance Methods

codename() click to toggle source

@return [Symbol]

Calls superclass method
# File lib/card/codename.rb, line 5
def codename
  super&.to_sym
end
codename?() click to toggle source

@return [True/False]

# File lib/card/codename.rb, line 10
def codename?
  codename.present?
end
deserialize_for_active_job!(attr) click to toggle source
# File lib/card/set/event/delayed_event.rb, line 86
def deserialize_for_active_job! attr
  attr.each do |attname, val|
    instance_variable_set("@#{attname}", val)
  end
  include_set_modules
end
log_event_call(event) click to toggle source
# File lib/card/set/event.rb, line 174
def log_event_call event
  Rails.logger.debug "#{name}: #{event}"
  # puts "#{name}: #{event}"
  # puts "#{Card::Director.to_s}".green
end
rescuing_if_integration(is_integration) { || ... } click to toggle source
# File lib/card/set/event.rb, line 163
def rescuing_if_integration is_integration, &block
  is_integration ? rescuing_integration(&block) : yield
end
rescuing_integration() { || ... } click to toggle source

one failed integration event should not harm others.

# File lib/card/set/event.rb, line 168
def rescuing_integration
  Error.rescue_card(self) { yield }
ensure
  true
end
serializable_attributes() click to toggle source

attributes that ActiveJob can handle

supercard and superleft are excluded, because it caused issues to have them in delayed job but not fully restored (set modules not included, attributes not retained, etc.) Since we’re supposed to have an actual left by the integrate_with_delay stage, it’s not clear that they’re needed. But if we revisit and find they are needed, then we clearly need to make sure that they are fully restored. At a bare minimum they would need to include set modules.

# File lib/card/set/event/delayed_event.rb, line 10
def serializable_attributes
  self.class.action_specific_attributes + set_specific.keys -
    %i[supercard superleft subcards]
end

Private Instance Methods

deserialize_hash_value(value) click to toggle source
# File lib/card/set/event/delayed_event.rb, line 147
def deserialize_hash_value value
  value.transform_values do |v|
    deserialize_value v[:value], v[:type]
  end
end
deserialize_value(val, type) click to toggle source
# File lib/card/set/event/delayed_event.rb, line 134
def deserialize_value val, type
  case type
  when "symbol"
    val.to_sym
  when "time"
    DateTime.parse val
  when "hash"
    deserialize_hash_value val
  else
    val
  end
end
perform_delayed_job_args(event) click to toggle source
# File lib/card/set/event/delayed_event.rb, line 99
def perform_delayed_job_args event
  [Card::Director.act&.id,
   self,
   serialize_for_active_job,
   Card::Env.serialize,
   Card::Auth.serialize,
   event.simple_method_name]
end
serialize_for_active_job() click to toggle source
# File lib/card/set/event/delayed_event.rb, line 108
def serialize_for_active_job
  serializable_attributes.each_with_object({}) do |name, hash|
    hash[name] = instance_variable_get("@#{name}")
  end
end
serialize_hash_value(value) click to toggle source
# File lib/card/set/event/delayed_event.rb, line 130
def serialize_hash_value value
  value.transform_values { |v| serialize_value(v) }
end
serialize_value(value) click to toggle source
# File lib/card/set/event/delayed_event.rb, line 114
def serialize_value value
  # ActiveJob doesn't accept symbols and Time as arguments
  case value
  when Symbol
    { value: value.to_s, type: "symbol" }
  when Time
    { value: value.to_s, type: "time" }
  when Hash
    { value: serialize_hash_value(value), type: "hash" }
  when ActionController::Parameters
    serialize_value value.to_unsafe_h
  else
    { value: value }
  end
end
set_delayed_job_args(event) click to toggle source
# File lib/card/set/event/delayed_event.rb, line 95
def set_delayed_job_args event
  { queue: event.name, priority: event.priority }
end