{"id":18319768,"url":"https://github.com/redding/sanford","last_synced_at":"2025-04-05T22:31:35.688Z","repository":{"id":5506501,"uuid":"6706566","full_name":"redding/sanford","owner":"redding","description":"Handler-based Sanford TCP protocol server","archived":false,"fork":false,"pushed_at":"2017-08-17T15:31:00.000Z","size":550,"stargazers_count":1,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-21T12:59:21.275Z","etag":null,"topics":["ruby","sanford","server","tcp","tcp-server"],"latest_commit_sha":null,"homepage":"https://github.com/redding/sanford","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/redding.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-11-15T15:11:50.000Z","updated_at":"2018-10-20T00:40:12.000Z","dependencies_parsed_at":"2022-08-19T14:01:02.784Z","dependency_job_id":null,"html_url":"https://github.com/redding/sanford","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redding%2Fsanford","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redding%2Fsanford/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redding%2Fsanford/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redding%2Fsanford/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/redding","download_url":"https://codeload.github.com/redding/sanford/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411236,"owners_count":20934650,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ruby","sanford","server","tcp","tcp-server"],"created_at":"2024-11-05T18:14:11.981Z","updated_at":"2025-04-05T22:31:35.243Z","avatar_url":"https://github.com/redding.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sanford\n\nSanford TCP protocol server for hosting services. Define servers for services. Setup handlers for the services. Run the server as a daemon.\n\nSanford uses [Sanford::Protocol](https://github.com/redding/sanford-protocol) to communicate with clients.  Check out [AndSon](https://github.com/redding/and-son) for a Ruby Sanford protocol client.\n\n## Usage\n\n```ruby\n# define a server\nclass MyServer\n  include Sanford::Server\n\n  port 8000\n  pid_file '/path/to/server.pid'\n\n  # define some services\n  router do\n    service 'get_user', 'MyServer::GetUser'\n  end\n\nend\n\n# define handlers for the services\nclass MyServer::GetUser\n  include Sanford::ServiceHandler\n\n  def run!\n    # process the service call and build a response\n    # the return value of this method will be used as the response data\n  end\nend\n\n```\n\n## Servers\n\nTo define a Sanford server, include the mixin `Sanford::Server` on a class and use the DSL to configure it. A few options can be set:\n\n* `name` - (string) A name for the server, this is used to set the process name\nand in logging.\n* `ip` - (string) A hostname or IP address for the server to bind to; default: `'0.0.0.0'`.\n* `port` - (integer) The port number for the server to bind to.\n* `pid_file` - (string) Path to where you want the pid file to be written.\n* `logger`- (logger) A logger for Sanford to use when handling requests; default: `Logger.new`.\n\n## Services\n\n```ruby\nclass MyServer\n  include Sanford::Server\n\n  router do\n    service 'get_user', 'MyServer::GetUser'\n  end\nend\n```\n\nServices are defined on servers via a router block.  Each named service maps to a 'service handler' class.  The service name is used to 'route' requests to handler classes.\n\nWhen defining services handlers, it's typical to organize them all under a common namespace. Use `service_handler_ns` to define a default namespace for all handler classes under the version:\n\n```ruby\nclass MyServer\n  include Sanford::Server\n\n  router do\n    service_handler_ns 'MyServer'\n\n    service 'get_user',     'GetUser'\n    service 'get_article',  'GetArticle'\n    service 'get_comments', '::OtherServices::GetComments'\n  end\nend\n```\n\n## Service Handlers\n\nDefine handlers by mixing in `Sanford::ServiceHandler` on a class and defining a `run!` method:\n\n```ruby\nclass MyServer::GetUser\n  include Sanford::ServiceHandler\n\n  def run!\n    # process the service call and generate a response\n    # the return value of this method will be used as\n    # the response data returned to the client\n  end\nend\n```\n\nThis is the most basic way to define a service handler. In addition to this, the `init!` method can be overwritten. This will be called after an instance of the service handler is created. The `init!` method is intended as a hook to add constructor logic. The `initialize` method should not be overwritten.\n\nIn addition to these, there are some helpers methods that can be used in your `run!` method:\n\n* `request`: returns the request object the host received\n* `params`: returns the params payload from the request object\n* `halt`: stop processing and return response data with a status code and message\n\n```ruby\nclass MyServer::GetUser\n  include Sanford::ServiceHandler\n\n  def run!\n    User.find(params['user_id']).attributes\n  rescue NotFoundException =\u003e e\n    halt :not_found, :message =\u003e e.message, :data =\u003e request.params\n  rescue Exception =\u003e e\n    halt :error, :message =\u003e e.message\n  end\nend\n```\n\n## Running Servers\n\nTo run a server, Sanford needs a config file to be defined:\n\n```ruby\nrequire 'my_server'\nrun MyServer.new\n```\n\nThis file works like a rackup file. You require in your server and call `run`\npassing an instance of the server. To use these files, Sanford comes with a CLI:\n\n* `sanford CONFIG_FILE start` - spin up a background process running the server.\n* `sanford CONFIG_FILE stop` - shutdown the background process running the server gracefully.\n* `sanford CONFIG_FILE restart` - \"hot restart\" the process running the server.\n* `sanford CONFIG_FILE run` - starts the server, but doesn't daemonize it (runs in the current ruby process). Convenient when using the server in a development environment.\n\nSanford will use the configuration of your server to either start a process or manage an existing one. A servers ip and port can be overwritten using environment variables:\n\n```bash\nsanford my_server.sanford start # starts a process for `MyServer`\nSANFORD_IP=\"1.2.3.4\" SANFORD_PORT=13001 sanford my_server.sanford start # run the same server on a custom ip and port\n```\n\nThis allows running multiple instances of the same server on ips and ports that are different than its configuration if needed.\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredding%2Fsanford","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fredding%2Fsanford","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredding%2Fsanford/lists"}