Crate scaffolding_core

source ·
Expand description

Object-oriented programming (OOP) has been around since the 1960s and was first introduced in the late 1950s in artificial intelligence by an MMIT group. It is no wonder then that over the years, the concept of objects being represented by classes and attributes with inheritanted behavior.

Rust addresses this design by providing structures, traits, and implementations. However, the native ability to extend a class (like in other languages) makes OOP a bit of a challenge. To address this gap, Scaffolding utilizes Rust’s procedural macros to mimic the ability to
extend a class - both data structure and behavior.

§Scaffolding Concept

  1. A class that extends the “Scaffolding class” should inherate all the “parent” data structure and behavior, as well as append the “child” specific data structure and behavior from the generic type being extended.
  2. The developer should have the flexibility to adopt the default “parent” characteristics or overwrite them as desired.
  3. There are common class attributes that are required in order to manage it using CRUD
  • id - The unique identifier of the object.
  • created_dtm - The unix epoch (UTC) representation of when the object was created
  • modified_dtm - The unix epoch (UTC) representation of when the object was last updated
  • inactive_dtm - The unix epoch (UTC) representation of when the object was/will be considered obsolete
  • expired_dtm - The unix epoch (UTC) representation of when the object was/will be ready for deletion
  • activity - The list of actions performed on the object
  1. There is common class behaviors that are required in order to manage it using CRUD
  • The id is not optional. It must be either provided or automatically generated during instantiation. This can be done by calling the Scaffolding trait’s id() method
  • The created_dtm is not optional. It must be either provided or automatically generated during instantiation. This can be done by calling one of the Scaffolding trait’s many datetime related methods, (e.g.: now())
  • The modified_dtm is not optional. It must be either provided or automatically generated during instantiation or updates to the object. This can be done by calling one of the Scaffolding trait’s many datetime related methods, (e.g.: now())
  • The inactive_dtm is not optional. It must be either provided or automatically generated during instantiation or updates to the object. This can be done by calling one of the Scaffolding trait’s many datetime related methods, (e.g.: add_months() in conjuctions with now())
  • The expire_dtm is not optional. It must be either provided or automatically generated during instantiation or updates to the object. This can be done by calling one of the Scaffolding trait’s many datetime related methods, (e.g.: never())
  • The activity is required and by default is an empty list of activity

§Example

Add Scaffolding to a struct and impl ::new() using macros and defaults

extern crate scaffolding_core;

use scaffolding_core::*;
use scaffolding_macros::*;
use serde_derive::{Deserialize, Serialize};
// Required for scaffolding metadata functionality
use std::collections::BTreeMap;

// (1) Define the structure - Required
#[scaffolding_struct("addresses","metadata","notes","tags")]
#[derive(Debug, Clone, Deserialize, Serialize, Scaffolding, ScaffoldingAddresses, ScaffoldingNotes, ScaffoldingTags)]
struct MyEntity {
    a: bool,
    b: String,
}

impl MyEntity {
    // (2) Define the constructor - Optional
    //     Note: Any of the Scaffodling attributes that are set here
    //           will not be overwritten when generated. For example
    //           the `id` attribute, if uncommented, would be ignored.
    #[scaffolding_fn("addresses","metadata","notes","tags")]
    fn new(arg: bool) -> Self {
        let msg = format!("You said it is {}", arg);
        Self {
            // id: "my unique identitifer".to_string(),
            a: arg,
            b: msg
        }
    }

    fn my_func(&self) -> String {
        "my function".to_string()
    }
}

let mut entity = MyEntity::new(true);

/* scaffolding attributes */
assert_eq!(entity.id.len(), "54324f57-9e6b-4142-b68d-1d4c86572d0a".len());
assert_eq!(entity.created_dtm, defaults::now());
assert_eq!(entity.modified_dtm, defaults::now());
// becomes inactive in 90 days
assert_eq!(entity.inactive_dtm, defaults::add_days(defaults::now(), 90));
// expires in 3 years
assert_eq!(entity.expired_dtm, defaults::add_years(defaults::now(), 3));

/* use the activity log functionality  */
// (1) Log an activity
entity.log_activity("cancelled".to_string(), "The customer has cancelled their service".to_string());
// (2) Get activities
assert_eq!(entity.get_activity("cancelled".to_string()).len(), 1);

/* use the addresses functionality */
// (1) Add an address
let addrShippingId = entity.insert_address(
    "shipping".to_string(),
    "acmes company".to_string(),
    "14 Main Street".to_string(),
    "Big City, NY 038845".to_string(),
    "USA".to_string(),
    "USA".to_string(),
);
let addrBillingId = entity.insert_address(
    "billing".to_string(),
    "acmes company".to_string(),
    "14 Main Street".to_string(),
    "Big City, NY 038845".to_string(),
    "USA".to_string(),
    "USA".to_string(),
);
let addrHomeId = entity.insert_address(
    "home".to_string(),
    "Peter Petty".to_string(),
    "23 Corner Lane".to_string(),
    "Tiny Town, VT 044567".to_string(),
    "USA".to_string(),
    "USA".to_string(),
);
// (2) Find addresses based on the category
let shipping_addresses = entity.search_addresses_by_category("shipping".to_string());
// (3) Remove an address
entity.remove_address(addrBillingId);

/* use the notes functionality */
// (1) Insert a note
let note_id = entity.insert_note(
  "fsmith".to_string(),
  "This was updated".as_bytes().to_vec(),
  None,
);
// (2) Modify the note
entity.modify_note(
  note_id.clone(),
  "fsmith".to_string(),
  "This was updated again".as_bytes().to_vec(),
  Some("private".to_string()),
);
// (3) Read the note's content
let read_note = entity.get_note(note_id.clone()).unwrap().content_as_string().unwrap();
println!("{}", read_note);
// (4) Search for notes that contain the word `updated`
let search_results = entity.search_notes("updated".to_string());
assert_eq!(search_results.len(), 1);
// (5) Delete the note
entity.remove_note(note_id);

/* use the metadata functionality */
entity.metadata.insert("field_1".to_string(), "myvalue".to_string());
assert_eq!(entity.metadata.len(), 1);

// manage tags
entity.add_tag("tag_1".to_string());
entity.add_tag("tag_2".to_string());
entity.add_tag("tag_3".to_string());
assert!(entity.has_tag("tag_1".to_string()));
entity.remove_tag("tag_2".to_string());
assert_eq!(entity.tags.len(), 2);

// extended attributes
assert_eq!(entity.a, true);
assert_eq!(entity.b, "You said it is true");

// extended behavior
assert_eq!(entity.my_func(), "my function");

Modules§

  • The defaults module provides the methods for creating deafult values for the Scaffolding common attributes

Structs§

Traits§