Coder Social home page Coder Social logo

lingo's People

Contributors

leftiness avatar

Stargazers

 avatar

Watchers

 avatar  avatar

lingo's Issues

I'll need a config.toml

Separate from secret.toml.

I'll want a default config that gets read if the provided config path doesn't exist. config.default.toml? Don't commit someone's personal config.toml to git.

Rethink actor structure

I kind of half-assed the message passing actor concept. It's supposed to be concurrent with references to a parent. Like nobody knows if you're actually going to get a response or not. If your child actor does eventually respond with the hello world, then you act upon that by ending the process. Maybe you also decide that you'll end the process without a hello world if there isn't a response in a certain amount of time, but it's not like tell() synchronously returns a response like I made it.

Pseudo code:

Main {
  &parent
  greeter
}

Main::init(&parent) {
  self.parent = parent
  self.greeter = Greeter::init(&self)
  self.greeter.tell(Greeter::Greet)
}

Main::tell(request) {
  match request {
    Greeter::Done => parent.tell(Main::Done)
  }
}

Greeter {
  &parent
}

Greeter::init(&parent) {
  self.parent = parent
}

Greeter::tell(request) {
  match request {
    Greeter::Greet => {
      println!("hello world")
      self.parent.tell(Greeter::Done)
    }
  }
}

After main tells his parent that he's done, that means that the process should end. Ofc that means main needs a parent somehow.

Then there's this state machine stuff. I like the idea of saying view.update(&oldState, &newState) and having him decide whether to update the UI, but I feel like I don't have a very good implementation right now.

I should definitely have one separate "thing" which handles all of the state. Maybe that's an actor. Idk right now. Then there would be an entirely separate thing which handles drawing the UI. The parent "thing" would start them both up. So if the state tells the main parent that he updated, the main parent would pass that down to the ui. The ui would then be able to ask the parent for state properties, and the parent would ask the state for those properties, and those would be returned to the ui.

I like the idea of having a state machine which says that you can't go to chat until you've gone through the configuration loading state, but I feel like I haven't done a good job of mixing it with actors.

Kind of rambling... time for breakfast...

Figure out Hipchat API code

Haven't done the Hipchat API stuff yet.

Hipchat has a polling API. I'll have a thread running in the background asking Hipchat if there are new messages. When it finds some, it'll send a message to any registered listeners.

The state machine would be a registered listener if it is in a listening state. Then it'll handle updating the UI etc from there.

So the chat and the API polling are separate.

Make a real logger

One that writes to a file instead of stdout. Put the log somewhere that XDG standard wants it.

state::State.error could be a stack

state::State.error could be a stack. Then I'd show one error at a time at the top of the screen or something. When they close the error, show the next. If there isn't another, hide the error text box.

figure out impure and async functionality

Reading a secret file imports a function from a library. Inserting into a database for example would need an external system. Not pure.

I had an idea for a structure where I would receive an impure request, take immediate preparatory steps (like a spinning clock to show that I'm async loading), send off the request to an actor, and then call state machine's work done. Forget about it. The actor may or may not eventually call back, telling the state machine about the result of the request. If he doesn't call back, the app could say "it's been thirty seconds since that request. Something seems wrong." If he calls back, it would be with a message sent to the top of the state machine. Then that message would be handled like any other.

Maybe I want to take this approach whenever the state machine needs to do something that isn't pure and immediately effective.

Pass a message with a reference to a state property

Example is the secret_path property of the Start state being passed to the Load state. I want to pass &str instead of cloning the string, but then Response needs a lifetime, and these lifetimes need to be specified, and I'm not sure how it'll work... I have to figure this out because eventually I'll have a state full of hipchat messages, and I don't want to clone all of those...

Make application more durable

In the actor pattern, a parent knows when his child panics, and he just restarts him. With what I have now, I remove an offline_listener from the dispatcher's subscribers if the tx.send() fails... So I'm just ignoring it...

Maybe state transitions should be message driven as well

It might work.

The State struct is Messageable, too. If I sent a Request::TransitionToLoading (or better name lol), I could handle it on the top level State struct and not send it down to the lower level state handler.

The response would be... Response::NewState(State) or something? Can a struct call its own From() implementation and return the result?

Maybe all state actions should happen in an actor?

For example, I moved some config stuff into a config actor. I have a chat state now. Maybe I should also move all chat functionality into a chat actor.

I was originally thinking that an actor would be used when a piece of functionality needed to be shared among states, but maybe all functionality in general should be in an actor, and a state should just be a collection of actors. Transitioning between some states could mean getting some values from an actor and giving it to another. Maybe discarding some actors. Maybe making some new ones.

figure out bigger application structure

Thoughts:

  • lingo_core (lib): state machine containing config, messages, information about present application state
  • lingo_hipchat (lib): given secrets, given a reference to core::State::tell, polls for messages, calls that function when messages are found, receives requests with its own tell() function for Hipchat resources, responds on core::tell
  • lingo_shell (bin): starts lingo_core (and lingo_hipchat if it detects an installed binary), creates a lingo_core::State, tells State when user presses keys to do things (moves in UI, requests to add a room, etc), creates a lingo_hipchat instance, gives State::tell function reference to lingo_hipchat to say that messages have arrived
  • lingo_gtk (bin, not planned): gui client could exist and do similar things to shell client

Initialize the application with a struct instead of a string

Entering the Initialization state should require providing a struct. The struct would contain two strings (at least) which would point to config.toml and secret.toml. Then transitioning to the LoadConfiguration state would involve loading those files and parsing them.

Figure out testing

inject dependencies, can rust do default arguments for that pattern of injection?

Let's see if I can do tests without the idea of mocks and stubs. Those don't translate well to Rust. Message driven should help. Maybe I'll wrap libraries in a messenger to isolate the need to inject dependencies, too.

state::State struct should implement its own clone

state::State struct should implement its own clone. Inside, it will only clone relevant state. For example, if it has 3000 messages, and it can only display 10 on the UI, it will only clone the 10 that it can display.

Rename some things for consistency

Don't do this:

errors::load::LoadError
states::load::LoadState

Do this instead:

errors::load::Error
states::load::State

The importer can rename things if he needs to.

Move config stuff into an actor

Both states::Load and states::Chat need the config stuff. Read configs. Ask for the values in the config. Maybe even write to the config. I'm going to do that stuff in an actors::Config.

Set up the UI

Lots of notes improperly written in #3, where I was supposed to be figuring out the things that I did for #30.

Add basic state for chatting

Need some structs, states, etc. Properties like messages_received, active_rooms, etc. Receive requests like Request::AcceptNewMessage

Restructure the project

Don't do this:

lingo::errors::load::Error
lingo::structs::config::Config
lingo::actors::config::Config
lingo::states::Load

Do this instead:

lingo::load::Error
lingo::config::Container // previously structs::config::Config
lingo::config::Actor // previously actors::config::Config
lingo::load::State // previously states::load::Load

This structure makes it so that the path leading up to the struct name can be used as a qualifier without redundancy.

I'm not entirely sure about putting errors, states, structs, etc under one lingo::load module. It kind of makes sense to put pieces of functionality together by purpose (load) instead of sorting them by type (struct, actor, etc).

I was thinking about this because I wrote that actors::config::Config struct, and it required me to reference the structs::config::Config struct as a structs::Config instead of something that might be better like config::Container.

At the same time, I've got some files that are named the same thing that... maybe shouldn't be. The actors::Config and the structs::Config are kind of related, but they should maybe have different names.

Multithreaded event notifier

Docs: https://doc.rust-lang.org/std/sync/mpsc/

I've been writing and rewriting this concept for some time now. I've tried observers in a few different ways. I don't think that's the right way to do it. I'm not happy with it.

I want to try a different approach. I've got several types of events in mind.

  • User pressed the A key.
  • User wants to send this message.
  • A message has been received.
  • The state has updated.

I'll have one event receiver. Every event broadcaster will be given a transmitter at construction which will send events to that receiver. He'll also be given a reference to the event receiver at construction. He won't keep that reference. He'll just create a new (tx, rx). He'll register a tx with the receiver reference and then start a loop listening for events on his rx. When he receives an event on his rx, he'll broadcast to the receiver on the tx provided at construction. He'll also provide a fn(&self, m: &Message) -> bool function pointer when he registers with the receiver. That function would filter messages so he only receives the ones he wants.

Examples of broadcasters:

  • State machine
  • Hipchat polling process ("New message here")
  • Stdin reading process ("User pressed A")

Transfer ownership of state properties

What if I don't have a secret property but a secret actor? Or a database actor? Or something. I feel like I would want to transfer ownership of that actor on state transition. Can I send the owned actor in a message? It'll be a moved property because I'll still have an owned reference on my struct... How do I solve this?

Maybe just the State { state } should be private and the FooState { property } should be public. The state machine would then be able to get the properties from itself when it transitions, but anybody outside of the machine who wants access to its properties would have to tell(Request).

Log state changes

In state machine, refer to states as logged messageables. This function would just log the current state with the request, call the tell function, log the response with the altered state. This way I can track the state and actions of the application from a debug log.

Set up UI component callbacks

I feel like the component itself should be able to handle events.

  • View: "You clicked right here. That's my button. Now send this event."
  • View: "You pressed the A key. I was waiting for that. Send this event."

I could pass the Event::KeyPress events in from the view::Container. Suppose I should.

I plan to have views, which are collections of components. A view should be the one who keeps track of the component event handlers.

  • Router: "State says we're in menu. Render the menu. Menu::render(&state)."
  • Menu: "Ok, components. Here's a key press. Does anybody care? for component in self.components { component.do_event_now(&event) }."

Make a better error for loading config files

I should store something better than what io::Error.description() gives me. "entity not found" isn't enough. I at least want to make it clear that a secret toml wasn't found.

aKeyPress('a')
StateUpdate(State { error: [], preference: Preference, secret: None, last_key_press: Some('a') })
bKeyPress('b')
StateUpdate(State { error: [], preference: Preference, secret: None, last_key_press: Some('b') })
lLoadPreference
LoadPreferenceErr(FailedToLoadFile("entity not found"))
StateUpdate(State { error: [Config(FailedToLoadFile("entity not found"))], preference: Preference, secret: None, last_key_press: Some('b') })
vKeyPress('v')
StateUpdate(State { error: [Config(FailedToLoadFile("entity not found"))], preference: Preference, secret: None, last_key_press: Some('v') })

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.