Module: Servicy
- Defined in:
- lib/client.rb,
lib/api.rb,
lib/formats.rb,
lib/service.rb,
lib/servicy.rb,
lib/transports.rb,
lib/formats/json.rb,
lib/load_balancer.rb,
lib/server/server.rb,
lib/transport/messages.rb,
lib/load_balancer/random.rb,
lib/server/service_searcher.rb,
lib/transport/tcp_transport.rb,
lib/transport/null_transport.rb,
lib/load_balancer/round_robin.rb,
lib/transport/in_memory_transport.rb
Overview
A transport which does nothing
Defined Under Namespace
Modules: ExtraMethods, Formats Classes: API, Client, Format, InMemoryTransport, LoadBalancer, NilTransport, RandomLoadBalancer, RoundRobinLoadBalancer, Server, Service, ServiceSearcher, TCPTransport, Transport
Class Method Summary collapse
- .config ⇒ Object
-
.configure(&block) ⇒ Object
Use this method to configure Servicy for other services.
-
.included(mod) ⇒ Object
After this method gets done running, you either have the state of the world exactly the same as it was, or your object is replaced in the global Object scope with a new object that will proxy methods to a remote service provider.
-
.logger ⇒ Object
Get a logger instance that we can do something useful with.
- .make_service_listener(mod) ⇒ Object
Class Method Details
.config ⇒ Object
160 161 162 |
# File 'lib/servicy.rb', line 160 def self.config @_config ||= configure end |
.configure(&block) ⇒ Object
Use this method to configure Servicy for other services
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/servicy.rb', line 139 def self.configure(&block) conf = Configurable.new # Give some reasonable defaults conf.server.host = 'localhost' conf.server.transport = Servicy::Transport.InMemory.new conf.server.load_balancer = Servicy::RoundRobinLoadBalancer.new conf.server.logger_stream = File.open(File.join('/var', 'log', 'servicy_server.log'), 'a') conf.server.config_file = File.("~/.servicy.conf") # To set something for a given object, you would do something like: # conf.transport.port = 1234 conf.client.transport = Servicy::Transport.InMemory.new conf.transport.format = Servicy::Formats.JSON conf = yield conf if block_given? # Set the options @_config = conf end |
.included(mod) ⇒ Object
After this method gets done running, you either have the state of the world exactly the same as it was, or your object is replaced in the global Object scope with a new object that will proxy methods to a remote service provider.
To define a service provider is a bit more work, and less auto-magic because the exact details of the infrastructure and architecture of your setup will be very different from others. So Servicy doesn’t make assumptions about how you want to do things. That said, it’s relatively simple:
# In some server class
Servicy::Server.new(...).go!
# In some service provider, possibly on a different box
api = Servicy::Client.create_api_for(MyServiceClass)
Servicy::Client.register_service(api)
FIXME: @__api, and it’s ilk, get overridden in the even that there is more than one.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/servicy.rb', line 40 def self.included(mod) make_service_listener(mod) # Attempt to find the service client = Servicy::Client.new(config.server.transport) raise 'go to bottom' unless client.connected? # Create a new api object @__api = Servicy::Client.create_api_for(mod) port = Servicy.config.send(mod.to_s.to_sym).port Servicy.logger.debug("Chose port, #{port} for service, #{mod.to_s}") @__api.const_set("PORT", port) unless port.nil? # TODO: for the other things as well @__service = client.find_service(@__api.search_query) raise 'go to bottom' unless @__service # If all went well, then we can wrap the object to call to the remote # service. client = Servicy::Client.new(config.client.transport.class.new(port: port)) @__api.set_remote(client) # ... and delegate that ish. We do it by killing off the original object, # and replacing it with our own. Object.send(:remove_const, mod.name.to_sym) Object.const_set(mod.name.to_sym, @__api) rescue => e # Something went wrong, just give up silently, but don't do anything # else; use this object as a regular-ass object. This is to allow you to # use your API objects as normal objects in an environment where the # object is bundled as part of a gem (,say, ) and there is no remote # handler for it: use it locally end |
.logger ⇒ Object
Get a logger instance that we can do something useful with.
13 14 15 16 17 18 19 |
# File 'lib/servicy.rb', line 13 def self.logger @logger ||= begin stream = config.server.logger_stream stream = stream || File.open(File.join('/var', 'log', 'servicy_server.log'), 'a') Logger.new(stream) end end |
.make_service_listener(mod) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/servicy.rb', line 74 def self.make_service_listener(mod) # If we didn't manage to connect to a remote client, then we are, by # definition, the provider. We should start the transports api_server, and # dispatch requests here. mod.send(:define_singleton_method, :start_servicy_server) do begin Thread.new do begin port = Servicy.config.send(self.to_s.to_sym).port || Servicy.config.server.port transport = Servicy.config.service.transport transport = transport.nil? ? Servicy.config.server.transport : transport client = Servicy::Client.new(transport.class.new(port: port)) client.transport.start_api do |request| method = nil begin method = self.method(request.method_name.to_sym) rescue method = self.instance_method(request.method_name.to_sym) method = method.bind(self.new) end things = transport.unformat(request.args) args = method.parameters.map do |param| things[param.last.to_s] end Servicy.logger.info "Calling #{method} with #{args.inspect}" begin result = method.call(*args) Servicy::Transport::Message.api_response(result) rescue => e Servicy::Transport::Message.error(e.to_s + "\n" + e.backtrace.join("\n")) end end client.transport.stop rescue => e Servicy::Transport::Message.error(e.to_s + "\n" + e.backtrace.join("\n")) end end Servicy.logger.info "Creating #{self.to_s} api" api = Servicy::Client.create_api_for(self) port = Servicy.config.send(mod.to_s.to_sym).port port = port.nil? ? Servicy.config.server.port : port api.const_set 'PORT', port # TODO: the other things, too Servicy.logger.debug("Chose port, #{port} for service, #{mod.to_s}") server_port = Servicy.config.server.port transport = Servicy.config.server.transport client = Servicy::Client.new(transport.class.new(port: server_port)) Servicy.logger.info "Registering api, #{api.to_s}" client.register_service api rescue => e Servicy.logger.error "An error occurred with the service: #{e.to_s}\n#{e.backtrace.join("\n")}" end end rescue => e # Again, just let it die silently. There is no reason to make things not # work locally. Servicy.logger.error(e) end |