Constellation Ruby Runtime API Proposal. Clifford Heath. (c) Copyright 2008. Notes: ====== 1) Concept here means any ValueType, EntityType, or objectified FactType (whether objectified explicitly or implicitly). Each Concept is represented by a Ruby class with extensions. Instance means an instance of any Concept. 2) All Concepts belong to a Vocabulary, and all Instances to a Constellation, which pertains to one Vocabulary. 3) No fact objects are created for binary functional dependencies. This means the API isn't immune to changes in uniqueness that affect FDs, because a role will go from single-valued to multi-valued, and those have a different API. 4) No representation of constraints is shown here yet. 5) The Query API isn't cooked. It contains mixed data and metadata. 6) This API, instantiated for the Metamodel, is the design-time API. Metadata objects ================ Concept: #verbalise() -> Return CQL definition for concept #vocabulary() -> Return the Vocabulary object #roles(:sym) -> Return the role named :sym #roles() -> Return the role array #binary(args) -> Define a role this concept's instance may play once only The other role player gets a matching role injected whose cardinality is either one or many. #reading(arg) -> Add a reading to this concept (it's an objectified fact type) Entity: #identifying_roles -> A array of Symbols naming the identifying roles ValueType: #length(n), #scale(n) -> Get/set length and scale Vocabulary: #concept() -> return a hash of all concepts, by Ruby class basename #concept(str) -> return a concept, by Ruby class basename #constraints() #adopt(klass, constellation, value) -> Convert value into an instance of klass in the constellation ... Instance objects ================ Instance: #verbalise() -> Return CQL definition for concept instance, see below #class() -> Return the Concept class #query() -> Return the QueryVariable for the role player of the invoked fact type #constellation() -> Return the Constellation this instance belongs to #mutable() -> Return true if the query allows this instance to be modified For a FD (single-valued) Role: #() -> Return the associated Instance #=(value) -> Assign the associated Instance. For a non-FD Role: #() -> An array of instances which supports: #verbalise #mutable These actions are not supported yet, but we need to find a way to: - access the original role values - detect whether changed from original - revert Value Instance (use String, Integer, Date, etc?): #verbalise() -> Return "#{self.class.name} #{inspect}" Entity Instance (normal Class): #verbalise() -> Return CQL definition for concept e.g. "Person = entity known by given-Name 'Fred' and family-Name 'Fly'" Constellation: #vocabulary() -> Return the Vocabulary this constellation is built from #(values) -> Find or make a new Instance (concept_name is class name) #() -> Return a collection of all Instances of that concept #instances() -> Return a hash by Concept (Ruby class). Each hash contains all instances of that class in this Constellation. #verbalise() -> Return a dump of all instances and all their single-values roles Constellation database API: #query() -> Return the Query head node #session() -> Return the database Session this was fetched from #transaction() -> Return the transaction sequence-number when we were fetched #validate_internal() -> Do all possible validity checks before saving #save() -> Save all changes #digest() -> Return a unique digest of all content Query objects ============= Query: #verbalise() -> Return CQL definition for query #vocabulary() -> Return the Vocabulary object #fact_types()[n] -> Return a QueryFactType from the array #variables()[] -> Return a QueryVariable from the array QueryVariable: #concept() -> The Concept which plays the roles of this variable #each() -> Iterate over all distinct instances in this constellation #adjective() -> adjective to disambiguate this variable ("-" in front or back) QueryFactType: #fact_type() -> the FactType being invoked #variables()[] -> QueryVariables that play a part in this FactType invocation #negative() -> The query is satisfied only if no fact instances match #aggregation() -> identifies form of aggregation over the fact population #each() -> Iterate over all distinct facts in this constellation QueryCondition: (Comparison operator between two expressions) ... QueryExpression: ... Examples ======== For example, if you have: Order = entity known by Id; ... Line = entity known by Order and Ordinal-line; Line is for Product; Line is for Quantity; Line costs total-Amount; Order has many Line; To insert, run a query against the empty population, yielding a blank constellation into which you can insert, then save. After fetching an order, you'd be able to write: line = some_order.line.add() # some_order is implicit player, ordinal is auto-assigned line.product = some_product line.quantity = 10 line.total_amount = 24.3 ... line.constellaton.save or maybe even: some_order.line.add(:product => some_product, :quantity => 10, total_amount = 24.3) Traversal within the constellation: e.g. order.line[0].line.product.id (etc). The 2nd .line may be omitted here for binaries as the FactProxy has an implicit Role