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

Replace Celluloid::IO with native code (for TCP) #246

Closed
mechanicalduck opened this issue Aug 2, 2014 · 21 comments
Closed

Replace Celluloid::IO with native code (for TCP) #246

mechanicalduck opened this issue Aug 2, 2014 · 21 comments
Labels

Comments

@mechanicalduck
Copy link

Referring to issue: celluloid/celluloid-io#74
Celluloid::IO got locking issues on Windows systems.

One of guard/listen use cases is to use it on a Windows system to efficiently monitor
(with kernel-support (gem wdm), without polling)
its local file system by remote clients using shared directories on it.

As proposed by celluloid/celluloid-io repository owner @tarcieri,
celloluid-io could be replaced with native code to resolve issues as guard/listen doesn't heavily rely on it.

@mechanicalduck mechanicalduck changed the title I will post a ticket and proposing to replace Celluloid::IO with native code. Replace Celluloid::IO with native code Aug 2, 2014
@noinkling
Copy link

+1, I've spent hours trying to get the TCP forwarding to work in Windows (with Guard on a VM) but as soon as something tries to query whatever port I have it set up on, it crashes.

$ listen -v -d "." -f "127.0.0.1:23000"
DL is deprecated, please use Fiddle
Starting listen...
E, [2014-08-18T17:24:24.486692 #1456] ERROR -- : Listen::TCP::Broadcaster crashed!
ThreadError: deadlock; recursive locking
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:38:in `synchronize'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:38:in `deregister'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/monitor.rb:40:in `close'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/celluloid-io-0.15.0/lib/celluloid/io/reactor.rb:51:in `block in run_once'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:92:in `block (2 levels) in select'
        c:/Ruby200/lib/ruby/2.0.0/set.rb:232:in `each_key'
        c:/Ruby200/lib/ruby/2.0.0/set.rb:232:in `each'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:91:in `block in select'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:52:in `synchronize'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:52:in `select'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/celluloid-io-0.15.0/lib/celluloid/io/reactor.rb:49:in `run_once'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/celluloid-0.15.2/lib/celluloid/evented_mailbox.rb:53:in `receive'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/celluloid-0.15.2/lib/celluloid/actor.rb:173:in `run'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/celluloid-0.15.2/lib/celluloid/actor.rb:157:in `block in initialize'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/celluloid-0.15.2/lib/celluloid/thread_handle.rb:13:in `block in initialize'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/celluloid-0.15.2/lib/celluloid/internal_pool.rb:100:in `call'
        c:/Ruby200/lib/ruby/gems/2.0.0/gems/celluloid-0.15.2/lib/celluloid/internal_pool.rb:100:in `block in create'

@mechanicalduck
Copy link
Author

+1

@e2
Copy link
Contributor

e2 commented Sep 14, 2014

It's best to run with LISTEN_GEM_DEBUGGING=2 environment variable, because when something in listen silently crashes, all kinds of errors can happen - unrelated to the problem.

Also, master branch has unreleased fixes to handling TCP options (which I screwed up while refactoring some time ago) and showing how ports are used (socket/server).

Also, celluloid-0.16.0 was released, so I'd try upgrading it first.

@mechanicalduck
Copy link
Author

Test (celluloid/celluloid-io#74 (comment))
with environment variable LISTEN_GEM_DEBUGGING=2
and gem celluloid version 0.16.0 is used.

set LISTEN_GEM_DEBUGGING=2
listen --forward <ip-address>:4000
DL is deprecated, please use Fiddle
Starting listen...
I, [2014-09-15T03:08:20.959438 #30132]  INFO -- : Celluloid loglevel set to: 0
D, [2014-09-15T03:08:21.043442 #30132] DEBUG -- : Adapter: considering TCP ...
D, [2014-09-15T03:08:21.043442 #30132] DEBUG -- : Adapter: considering polling ...
D, [2014-09-15T03:08:21.043442 #30132] DEBUG -- : Adapter: considering optimized backend...
I, [2014-09-15T03:08:21.062444 #30132]  INFO -- : Record.build took 0.01200103759765625 seconds
D, [2014-09-15T03:08:21.063444 #30132] DEBUG -- : wdm - starting...

[Error is provoked by connecting (e.g. using telnet client)]

E, [2014-09-15T03:08:22.522527 #30132] ERROR -- : Broadcaster.run: #<ThreadError: deadlock; recursive locking>:C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:38:in `synchronize'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:38:in `deregister'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/monitor.rb:40:in `close'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:86:in `reregister'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:80:in `wait'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:70:in `wait_readable'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:23:in `block in wait_readable'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:52:in `wait'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:22:in `wait_readable'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io.rb:53:in `wait_readable'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/tcp_server.rb:15:in `accept'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/listen-2.7.9/lib/listen/tcp/broadcaster.rb:50:in `run'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/calls.rb:26:in `public_send'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/calls.rb:26:in `dispatch'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/calls.rb:122:in `dispatch'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/cell.rb:60:in `block in invoke'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/cell.rb:71:in `block in task'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/actor.rb:357:in `block in task'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/tasks.rb:57:in `block in initialize'
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/tasks/task_fiber.rb:15:in `block in create'
E, [2014-09-15T03:08:22.524527 #30132] ERROR -- : Actor crashed!
ThreadError: deadlock; recursive locking
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:38:in `synchronize'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/selector.rb:38:in `deregister'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/nio4r-1.0.0/lib/nio/monitor.rb:40:in `close'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:86:in `reregister'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:80:in `wait'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:70:in `wait_readable'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:23:in `block in wait_readable'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:52:in `wait'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/reactor.rb:22:in `wait_readable'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io.rb:53:in `wait_readable'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-io-0.16.0.pre2/lib/celluloid/io/tcp_server.rb:15:in `accept'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/listen-2.7.9/lib/listen/tcp/broadcaster.rb:50:in `run'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/calls.rb:26:in `public_send'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/calls.rb:26:in `dispatch'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/calls.rb:122:in `dispatch'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/cell.rb:60:in `block in invoke'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/cell.rb:71:in `block in task'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/actor.rb:357:in `block in task'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/tasks.rb:57:in `block in initialize'
        C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/celluloid-0.16.0.pre3/lib/celluloid/tasks/task_fiber.rb:15:in `block in create'

However, it doesn't seem to increase detail in logging.

With best regards

@e2
Copy link
Contributor

e2 commented Sep 15, 2014

@mechanicalduck, @noinkling - I'm closing this, because of #258.

If anyone could provide a separate Listen TCP server implementation (that works for all platforms), I'd move/remove the server code from Listen itself.

@e2 e2 closed this as completed Sep 15, 2014
@e2 e2 changed the title Replace Celluloid::IO with native code Replace Celluloid::IO with native code (for TCP) Mar 4, 2015
@e2 e2 added the windows label Mar 4, 2015
@joshco
Copy link

joshco commented Jun 1, 2016

Any updates on this? Is there any currently working solution to run guard in a vagrant with some listen solution on a win32 host?

@e2
Copy link
Contributor

e2 commented Jun 1, 2016

@joshco - probably best to just work inside a VM completely.

Liste 3.x doesn't use Celluloid anymore, but TCP also isn't implemented. I personally don't use Windows and there are other areas in Listen I need to work on first.

As a workaround, you could just setup a TCP server based on Listen that sends changes to a TCP listener inside Guard directly. Guard has an internal API for simulating file messages.

If someone creates a proof-of concept on the Guard side, I could set up an official Guard TCP client plugin.

@joshco
Copy link

joshco commented Jun 1, 2016

Thanks for the quick response. Moving completely inside the vagrant VM isn't an option. (too many other things become much more difficult )

I might be able to write something that uses listen and sends messages.
Can you point me to the best specification of the TCP protocol? Is that the guard internal API you are talking about?

@e2
Copy link
Contributor

e2 commented Jun 1, 2016

Can you point me to the best specification of the TCP protocol?

You can have any protocol you want since you'd be implementing both sides.

  1. Just use a standard TCPServer that broadcasts messages to connected clients whenever a file changes: https://github.com/guard/listen#usage
  2. You can use https://github.com/guard/guard-yield to create a quick ad-hoc Guard plugin to start up a TCPSocket to connect to your TCPServer from above. You just want to handle the start and stop methods of that plugin. You can hard-code the ports, etc. until you first get it working. 4000 used to be the standard port in Listen.
  3. Now, once your client is receiving packets (you can send just the path relative to the watched directory), all you need to do it tell Guard to think that file has changed locally. You can use the method: https://github.com/guard/guard/blob/8976db6/lib/guard/commands/change.rb#L25 to do that.

That file implements the Pry "change" command, which simulates a file. I'm pretty sure the async_queue_add method is here to stay forever, so you can use it without worrying.

I'm not sure about the overall speed, but it's a start. Listen 3.x is heavily optimized to be responsive and fit within "deadlines", so if you send the changes as soon as you get them, it should be enough - especially since Guard will also respond immediately to the changes.

You can use LISTEN_GEM_DEBUGGING=2 on the TCP server side to see how quickly things are happening.

Let me know if you need anything else.

@e2
Copy link
Contributor

e2 commented Jun 1, 2016

@joshco - if you want to create something more "future-oriented", make sure to include a "protocol version" in your protocol. Listen didn't do this and it was a mistake.

@joshco
Copy link

joshco commented Jun 1, 2016

this is super helpful. Starting to code, but one question
If I use the guard yield :start method, I can set up the TCP connection there, but will this be async? Does the yield expect to return?

Can it sit blocked on a socket read loop?

Or should I start up my own thread for to monitor the socket for incoming data and then just call the async_queue_add from there?

Re protocol, yup, I hear ya.

@e2
Copy link
Contributor

e2 commented Jun 1, 2016

Does the yield expect to return?

You can just start it in a thread.

Later you can stop the connection and do a .join on the thread.
You can use any object in Guard::Yield to pass, so you can store the thread and connection in a hash or array.

async_queue_add is designed to be called from anywhere - it uses Thread::Queue and the interactor+commands are handled asynchronously.

@joshco
Copy link

joshco commented Jun 1, 2016

roger

@joshco
Copy link

joshco commented Jun 1, 2016

do I want to put my tcp code in the proc that is called like run_on_additions, or do I want to put it in the do block?
What goes where? Would I be wrapping my existing guard :rspec block with this?
Do I actually need anything in the do block?

notifier = proc do |title, _, changes|
  Guard::Notifier.notify(changes * ",", title: title )
end

guard :yield, { run_on_additions: notifier, object: "Add missing specs!" } do
  watch(/^(.*)\.rb$/) { |m| "spec/#{m}_spec.rb" }
end

@e2
Copy link
Contributor

e2 commented Jun 1, 2016

You're only responding to :start and :stop.

So it's probably something like this:

data = [nil, nil]

starter = proc do |data, _, _|
  th = Thread.new(0.1) do
      data.last = TCPSocket.new('mypc', 4000); 
      while (line = data.last.gets)
         # (...)
         Guard.async_queue.......
      end
      data.last.close
   end
  data.first = th  
end

finisher = proc do |data, _, _|
  data.last.close # tcp
  data.first.join
end

guard :yield, { start: starter, stop: finisher, object: data } do
  # nothing to watch or do
end

@e2
Copy link
Contributor

e2 commented Jun 1, 2016

Or you can just go OO from the start with e.g.

guard :yield, { start: proc { |con| con.start }, stop: proc { |con| con.stop }, object: MyTCPConnection.new } do

@joshco
Copy link

joshco commented Jun 1, 2016

got it

On Wed, Jun 1, 2016 at 1:30 AM, Cezary Baginski notifications@github.com
wrote:

Or you can just go OO from the start with e.g.

guard :yield, { start: starter, stop: finisher, object: MyTCPConnection.new } do


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#246 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/AARuCqosHGOFcgHrbqMissmph-ncjgQzks5qHRkRgaJpZM4CTebD
.

_Josh Co_hen joshco@gmail.com | @joshco | LinkedIn
http://linkedin.com/in/joshrcohen/ | GitHub https://github.com/joshco
| OSDI opensupporter.org http://opensupporter.org/

Looking for a contract developer to work on APIs, integrations, servers,
WordPress? Hire me!
"If [OSDI http://opensupporter.org/] succeeds, it could usher in the next
chapter in political technology." - Washington Post http://wapo.st/1hYXX7O

@joshco
Copy link

joshco commented Jun 1, 2016

Success!

@e2
See Gist below

Your protocol versioning got me thinking deeply, and so i decided to just use HTTP. My solution also reverses the client/server model that you had because it seems to fit my mind better. The existing model always seemed strange since the guard instance would contact the listen server, but then be receiving pushed data back to it. (But then again, that's websockets style notifications. huh)

Why muck around with masrhalling payloads and metadata over raw sockets? The change notifications, which are relatively infrequent, are reasonably well modeled as webhooks. The version problem is handled RESTishly as part of the URL, which takes a JSON body.

POST /v1/change

{
 :modified=>["spec/services/lps_draft_spec.rb"],
 :added=>[],
 :removed=>[]
}

Leveraging HTTP's message framing also opens the door to other functions at different URL endpoints and versioning (and evil too!)

FS Listener

Win_listen.rb, fires up the listen gem to watch a directory and takes a URL to POST change notifications to.

Notification Receiver (Guardfile)

Using your advice on guard-yield, I embedded a sinatra server inside the Guardfile that accepts the webhooks and calls the async queue add.

https://gist.github.com/joshco/6c84fd30dfbe0118bc42570000eb268d

I will pretty this up and package it how you like for others to more easily reuse in a week or so. Right now I need to get back to the current deadline, which hopefully this fix will make faster.

@e2
Copy link
Contributor

e2 commented Jun 1, 2016

Your protocol versioning got me thinking deeply, and so i decided to just use HTTP.

I actually wanted to suggest that.

I thought it was overkill, but it actually makes sense since HTTP servers are designed to process requests quickly anyway, so the overhead isn't huge at all.

It actually doesn't matter since listen accumulates changes to avoid spamming if there are lots of disk changes. So it's only minor overhead (wait_for_delay + tcp overhead + http request overhead). You can probably reduce the wait_for_delay as much as possible, e.g. 0.01. You'll know if the value is too small if you're editor starts generating other than 1 event for saving files. So the value can be as low as your editor and editor settings allow.

Overall response time here is important, because it boosts productivity if there's less lag between changing a file and the action. (At least for me).

Leveraging HTTP's message framing also opens the door to other functions at different URL endpoints and versioning (and evil too!)

I actually think it's unreasonable to say raw TCP should be used here, unless there are numbers/benchmarks proving otherwise. The :wait_for_delay option will be the first bottleneck anyway.

My solution also reverses the client/server model that you had because it seems to fit my mind better.

Yeah, I had the same idea when I first tried to work out why Listen's TCP seemed "reversed". It doesn't really matter.

The idea to have the server in Listen comes from the fact that if you restart guard, the connection is broken. And usually the Listener should be watching for files non-stop anyway, so that's where the server should be. It seems backwards, but in fact Guard is just a client listening for changes.

Win_listen.rb, fires up the listen gem to watch a directory and takes a URL to POST change notifications to.

I think it's better to use plain Guard for the listener - especially since it already handles relative directories. It's a bit trickier if you want the Guardfile outside the project, but it's doable.

So I actually think it's best to use Guard on both sides - Guard is a thin layer on top of Listen anyway and allows more flexibility with transforming paths if needed. Guard::Yield on both sides should do the job without needing to write plugins.

I'd also consider putting BOTH the server and client functionality into a single Guard plugin.

And it would be up to the user to decide which instance is the server (accepting changes) and the client (sending changes). Also, having a server on both sides means you can e.g. pause the listener remotely and do other cool stuff, like restart Guard with a new config if a file changes.

If you like, wrap this up into a small repo using e.g. bundle gem (mostly to get your info for the gem spec, so don't worry about quality or tests yet) and I'll create a Guard project and add you in. You can also release a 0.1.0 just to make sure the gem name is available. guard-http is fine, and the more generic the better. I'll handle everything else.

Awesome work!

@joshco
Copy link

joshco commented Jun 1, 2016

I actually wanted to suggest that.

Great minds think alike.

I thought it was overkill, but it actually makes sense since HTTP servers are designed to process requests quickly anyway, so the overhead isn't huge at all.
Yeah, it also has a simplifying effect on the configuration and code needed since HTTP and the server handle payload marshaling, connection management, versioning, routing, and message exchange patterns.

Compared to the beast that emerged in the Celluloid implementation it's quite simple.

Overall response time here is important, because it boosts productivity if there's less lag between changing a file and the action. (At least for me).

Yeah, but HTTP is sufficiently quick, especially compared to the rails/rspec startup time.

The idea to have the server in Listen comes from the fact that if you restart guard, the connection is broken.

Makes sense for a session oriented protocol like TCP. Since HTTP is more transaction oriented (connections don't represent session state), restarting the receiving guard doesn't reall cause a problem. (excepting a downed guard that might miss a change notification, of course)

If you like, wrap this up into a small repo using e.g. bundle gem...

Ya I'm down to do the work to make this reusable for others.

So I actually think it's best to use Guard on both sides...
If you like, wrap this up into a small repo using...

Hmm. I'm on the fence about using Guard on the sending side. Something like a 'notify-webhook' gem would be useful as a general tool, but it's also simple and elegant for the sender side in a guard scenario.

The user doesn't have to worry about setting up a Guardfile or if both configs are part of the same Guardfile having to detect if it is supposed to be the sender/host or receiver/guest, avoid doing any test running on the sender/host side. Plus, once the notify-webhook gem/binary is installed, it can just be run immediate without config wherever the user likes, eg:

notify-webhook http://receiver:4000/v1/change

or if not watching cwd

notify-webhook -d ../otherdir http://receiver:4000/v1/change

That's not to say that there isn't some value in using it within guard (like file matching you described) and we could make that possible too, but in terms of a developer being in the "I just need to get this running asap!" mental state, leaving the Guardfile mental complexity to what they are used to (the receiver side) and the quick direct notify-webhook gem binary could be simpler.

thoughts?

@e2
Copy link
Contributor

e2 commented Jun 2, 2016

Great minds think alike.

So much so, it makes discussions almost superfluous :D

Yeah, it also has a simplifying effect on the configuration and code needed since HTTP and the server handle payload marshaling, connection management, versioning, routing, and message exchange patterns.
Yeah, but HTTP is sufficiently quick, especially compared to the rails/rspec startup time.

I can't agree more. It's hard to justify using raw TCP nowadays.

(excepting a downed guard that might miss a change notification, of course)

I agreed. That's not important if you're using an editor to create changes.

Hmm. I'm on the fence about using Guard on the sending side.

Not for the sending itself. Listen vs Guard is like TCP vs HTTP in this case.

Something like a 'notify-webhook' gem would be useful as a general tool, but it's also simple and elegant for the sender side in a guard scenario.

It's fine by me. Guard has some nice bonuses, though. It's possible to have a Guardfile config in the home directory, and just use that config in any directory. (No project-level Guardfile necessary).

The user doesn't have to worry about setting up a Guardfile

I was thinking more along the lines of just running GUARD_HTTP_SEND_TO=mypc:4001 guard init http. (Without the variable, you'll get a server setup).

notify-webhook -d ../otherdir http://receiver:4000/v1/change

I don't mind either way (Guard or not). I was thinking that it's easier to manage and update/change/test if both server and listener are handled by a single gem. So a user just does gem install guard-http and then just sets up the listener with the guard init call. The config can be even just commented out in the Guardfile for people to update (and the listener/server will restart anyway automatically as soon as the file is saved).

Also I think it's "easier" for users to follow a documentation where everything is in the README of one project. (Instead of telling them to install a different gem, etc.).

Personally I think having 2 projects to manage is overhead that isn't worth it. You can always split a project into 2 parts if needed later. Or, the plugin can just have a bin script for the listener. (Like Listen used to).

The idea is: after a project exists, people can send PRs for whatever they need.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants