Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic implementation #3

Merged
merged 11 commits into from
Mar 23, 2017
Merged

Conversation

sadovnik
Copy link
Contributor

This is a basic implementation of concept of running RPC-server with the tests within single process. There are four simple tests that are implemented and tested on anycable-go 0.3.0. I need to get some feedback to understand am I going on the right direction or not. : )

@sadovnik sadovnik force-pushed the basic-implementation branch 4 times, most recently from 160c121 to 1219a9e Compare March 17, 2017 20:02
@sadovnik sadovnik force-pushed the basic-implementation branch from 1219a9e to 1ff37c5 Compare March 17, 2017 20:04
#!/usr/bin/env ruby

require 'bundler/setup'
require 'docopt'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you choose docopt? We can use OptionParse, which is a common practice. Also we can avoid the additional dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that declarative approach is significant when it comes to building user interfaces. That's why I choose Docopt. It requires you to specify the actual help message that will be displayed to the end user to declare argument parsing rules.

Also we can avoid the additional dependency.

Docopt is relatively compact (16KB) and it has well-established API.

We can use OptionParse, which is a common practice.

Why is this important? Could you please expand on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I don't know any project that uses it

  • A lot of popular Ruby projects use OptionParser (Sidekiq, Rubocop to name a few)

  • It has not been maintained for a rather long time (https://ossert.evilmartians.io/docopt)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know any project that uses it

A lot of popular Ruby projects use OptionParser (Sidekiq, Rubocop to name a few)

That's true: https://rubygems.org/gems/docopt/reverse_dependencies

It has not been maintained for a rather long time

https://ossert.evilmartians.io/docopt: Active Years Age: 4+ years

🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Active Years Age" takes into an account issues and PRs. Yep, kind of confusing.

For the other hand, the commits history shows the real situation. As well as the state of open issues and PRs.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW the main Ossert application is to aggregate several metrics to come up to a more balanced grade of maintenance quality. So it is not very good to use one isolated metric to make your decision.

Anyway I'll try to find better name for "Active Years Age" in order to frustrate nobody.

class Client
attr_reader :state

def initialize(cable_url, debug = false, ignore_message_types = [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move debug option into a general Anycablebility config.

We can also extract other options into a config and use it everywhere (e.g. Anycablebility.config.redis_url, etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also extract other options into a config and use it everywhere (e.g. Anycablebility.config.redis_url, etc).

Should we use Anyway for this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it's already here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand what's the benefit of using Anyway for such task. I mean who will want to use Anycablebility inside a Rails app or configure it via environment variables?

I'm also not sure why should we introduce global state here instead of just passing config around. This makes no sense to represent our components as objects if they will rely on some global state, isn't it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Command line arguments would be enough.

Let's introduce a global Logger instead (say, Anycablebility.logger). Or we can even use Anycable.logger, btw.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's introduce a global Logger instead (say, Anycablebility.logger). Or we can even use Anycable.logger, btw.

Could you please explain what goal do we want achieve with it? We want to config logger once, instead of configuring it in every component separately?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to config logger once, instead of configuring it in every component separately?

Exactly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@palkan does the last commit satisfies this requirement?


run_websocket_client

Waiter.wait(20) { @state == :welcomed }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

20 seconds? Maybe, 5 could be enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. Also, maybe we should make it configurable?

attr_reader :state

def initialize(cable_url, debug = false, ignore_message_types = [])
@logger = Logger.new(debug ? STDOUT : '/dev/null')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to use IO::NULL instead of '/dev/null'

logger = Logger.new(STDOUT)
logger.level = debug ? Logger::DEBUG : Logger::WARN

rpc = Rpc.instance.configure(redis, debug, logger)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of the global/shared logger is to get rid of these parameters: debug, logger.

Let's do the following:

  • introduce a Logging module:
module Anycablebility
  module Logging
    PREFIX = 'AC'
    
    class << self
      attr_writer :logger
    end

    def logger
      Anycablebility.logger
    end
    
   # helper method
   # later we'll be able to add a colorising here, depending on the level
    def log(level, message)
      return unless logger
      logger.send(level.to_sym, PREFIX) { message }
    end
  end
end
  • Then in #run method:
Anycablebility.logger = Logger.new(STDOUT).tap do |logger|
  logger.level = debug ? Logger::DEBUG : Logger::WARN
end
  • When configuring AnyCable:
Anycable.logger = Anycablebility.logger
  • And anywhere you need a logger:
include Logging

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# helper method
# later we'll be able to add a colorising here, depending on the level
def log(level, message)
  return unless logger
  logger.send(level.to_sym, PREFIX) { message }
end

There's a Logger#formatter=. Maybe we should take advantage of it for that purpose?

Copy link
Contributor Author

@sadovnik sadovnik Mar 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class << self
  attr_writer :logger
end

Why do we need this? This is an attribute writer on Logging itself. We need an attr_accessor :logger on the Anycablebility instead. Am I missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a Logger#formatter=. Maybe we should take advantage of it for that purpose?

Great idea 👍
And we can do that later, let's skip for now.

attr_accessor :logger on the Anycablebility instead

Yep, you're right

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Could you please take a look at how I require stuff? I'm not sure I do it the right way. I have PHP background so I require components where do I need them, like PHP programmers use their classes and constants. Is that right paradigm? Or we should make some kind of tree with requires like rubocop does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although this kind of global logger makes some parts of code less verbose but also less elegant. There's gotta be a practical yet elegant way of doing that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please take a look at how I require stuff?

LGTM

@palkan
Copy link
Member

palkan commented Mar 23, 2017

Let's merge it and I'll play with it for a while and propose future improvements.

@palkan palkan merged commit 44a209f into anycable:master Mar 23, 2017
@palkan palkan mentioned this pull request Mar 23, 2017
@sadovnik
Copy link
Contributor Author

@palkan let's discuss and list test cases to do.

@sadovnik sadovnik deleted the basic-implementation branch October 13, 2017 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants