gitmqueue 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e42924f796ddd596ee51c69e7d837da4988ce63db27688998e43260570ad9ec
4
- data.tar.gz: 8314d9f06183362f39f4831dc8442dfaaeb879bb23860bab6bc8cdf9ea7ebb98
3
+ metadata.gz: fef08ee9fcfc3c24da047d2c1a0aa5e760127bf47ab665e7962590c2be239744
4
+ data.tar.gz: c49766c95c5d7b3848c54d1a06cacbc594abaaaab88004129602a0824a70756b
5
5
  SHA512:
6
- metadata.gz: c9459c7b8990fe2c62f24d5bdcdd0c50be3e1afe14fb6dc5ac920aace0ecc79581eeccf57d892bd32f29d7585bcbea895fa0e3b909e850796bb25d5075cb4c14
7
- data.tar.gz: 541f339f68307c55e27cbc182e716dcd591c8d9bc5a018be4e7a7563d1a7bec1239ac85303b2c419791a7142cade0d1b6fc586875adb29717903aefafe34a915
6
+ metadata.gz: 0b987755d7ff9dc3133c7a4ba8e67ede3fbfdd9967a44b955f60e775ba8ffeb2db4a5d6682c1296a28b233b7f9de80103cc3245476261fc04cedd6482e7334db
7
+ data.tar.gz: feb8ddc0b1f77d4ad37d61ba88e46acad0791f576d05fefb7bdd1a216214211989ad4f7735ad93f2ad4de54ec30ccabf6a3482d4c036f0aaf9c2e86e1d08b564
@@ -2,13 +2,21 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  gitmqueue (0.1.0)
5
+ listen (~> 3.2.0)
5
6
  rugged (~> 0.28)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
11
  diff-lcs (1.3)
12
+ ffi (1.11.3)
13
+ listen (3.2.0)
14
+ rb-fsevent (~> 0.10, >= 0.10.3)
15
+ rb-inotify (~> 0.9, >= 0.9.10)
11
16
  rake (10.5.0)
17
+ rb-fsevent (0.10.3)
18
+ rb-inotify (0.10.0)
19
+ ffi (~> 1.0)
12
20
  rspec (3.9.0)
13
21
  rspec-core (~> 3.9.0)
14
22
  rspec-expectations (~> 3.9.0)
data/README.md CHANGED
@@ -1,34 +1,38 @@
1
1
  # GitMQueue : Git Message Queue
2
2
 
3
- Hey, Good idea bad pitch, lets use Git as a messaging queue! lets use github to
3
+ Hey, Crazy idea bad pitch, lets use Git as a messaging queue! lets use github to
4
4
  synchronize a cluster of them together.
5
5
 
6
- Lets get the obvious out of the way, You already know that we use Git to keep
7
- track of our code changes, and we need a central point to pull and push these
8
- changes (although it's designed to be decentralized), this is why we use
9
- github/lab/bitbucket...etc.
6
+ ![Screenshot-2019-11-24_22-41-55](https://user-images.githubusercontent.com/54403/69502136-09672400-0f0c-11ea-8707-df3164411c4e.png)
10
7
 
11
- Alright, now lets introduce another idea: Git commits doesn't need to include
12
- changes, so you don't have to create files then commit these files to
8
+
9
+ Lets get the obvious out of the way.
10
+
11
+ 1. You already know that we use Git to keep track of our code changes, If you're a
12
+ developer you most probably use it every day, and you probably know how to share
13
+ your code changes with everybody else using some central repo server like Github
14
+ or Bitbucket.
15
+
16
+ 1. Alright, now lets introduce another idea: **Git commits doesn't need to include
17
+ changes**, You don't have to create files then commit these files to
13
18
  your git repo, you can create an empty commit with a command like this:
14
19
 
15
20
  ```
16
21
  git commit --allow-empty -m "commit message"
17
22
  ```
18
23
 
19
- OK, so the commit message can be any text right? that means you can have JSON
24
+ 1. OK, so the commit message can be any text right? that means you can have JSON
20
25
  messages or YAML or any other machine readable format as a commit message.
21
26
 
22
- So now imagine this scenario:
27
+ ## Now imagine this use case:
23
28
 
24
- 1. Create a repo on github
29
+ 1. Create a repository on github
25
30
  1. Clone it to your server
26
31
  1. Have your application commit a `message` to this repo every time you need to
27
32
  1. Push the repo every period say 10 seconds
28
33
  1. Clone same repo to another server
29
- 1. Have an application there (consumer) pull periodically
30
- 1. Have Another thread there that checks for new commits and for each one of
31
- them extracts the message and does the job
34
+ 1. Have an application pull periodically and checks for new commits and for each
35
+ one of them extracts the message and process it
32
36
  1. Put a git tag on the last processed message with that consumer identifier
33
37
  1. Push that label to keep track of the last processed message (pull it when you
34
38
  deploy that consumer again)
@@ -36,14 +40,14 @@ So now imagine this scenario:
36
40
  Now you have a queue of messages that can't be altered, communicated between
37
41
  servers and all data saved in a git repository.
38
42
 
39
- This idea is flexible and can be modified for several use cases:
43
+ This idea is flexible and can be altered for several use cases:
40
44
 
41
- 1. the central server doesn't have to be a github or a like, you can use your
45
+ 1. the central server doesn't have to be Github or a like, you can use your
42
46
  own server as a git server.
43
47
  1. You can have more than one queue for messages by creating many branches each
44
48
  for a type of messages or a different messages purpose.
45
49
  1. You can have the same branch consumed from different servers each message
46
- processed multiple times in different ways like RabbitMQ fan out strategy
50
+ processed multiple times in different ways like RabbitMQ fanout strategy
47
51
  1. You can keep browse your message queue with any git client UI or CLI
48
52
  1. You can clean your servers git local repo so that it deletes old commit
49
53
  history to save space.
@@ -66,3 +70,105 @@ This idea can as simple as invoking git commands from your application with a
66
70
  system call, or use libgit to manipulate the repository for you, or even a small
67
71
  server application that communicate over a socket, or a repo service that
68
72
  communicate over even HTTP, there are so many ways to implement that concept.
73
+
74
+ The current repository is an implementation of this concept in Ruby.
75
+
76
+ # Installation
77
+
78
+ Install the gem with
79
+ ```
80
+ gem install gitmqueue
81
+ ```
82
+
83
+ or add gitmqueue to your `Gemfile`
84
+ ```
85
+ gem 'gitmqueue', '~> 0.1'
86
+ ```
87
+
88
+
89
+ # Producing messages
90
+
91
+ First create an instance of `GitMQueue::Storage`, to specify your storage place.
92
+
93
+ ```ruby
94
+ storage = GitMQueue::Storage.new('/tmp/gitmqueue')
95
+ ```
96
+
97
+ This is where your git repository will live, message producer will use this
98
+ storage instance to write messages to any branch you wish.
99
+
100
+ ```ruby
101
+ producer = GitMQueue::Producer.new(storage: storage, branch: 'master')
102
+ ```
103
+
104
+ Now you can use `producer` to publish message to this branch.
105
+
106
+ ```ruby
107
+ producer.publish('Hello, world!')
108
+ ```
109
+
110
+ It will create a commit with `Hello, world!` as a message in the `master`
111
+ branch.
112
+
113
+ You can have as many producers as you wish for any number of branches, make sure
114
+ to have one producer per branch to prevent race conditions between producers.
115
+
116
+ # Consuming messages
117
+
118
+ You need to create storage instance as we did with the producer, then
119
+ instanciate your consumer
120
+
121
+ ```ruby
122
+ consumer = GitMQueue::Consumer.new(storage: storage, name: 'consumer', branch:
123
+ 'master')
124
+ ```
125
+
126
+ And start the consume process, this function will take a block of code that
127
+ accept one parameter which is your message, this function is non-blocking
128
+ it will execute the block of code each time a new commit appear in the branch
129
+
130
+ ```ruby
131
+ consumer.consume do |message|
132
+ puts message
133
+ end
134
+ ```
135
+
136
+ After your block finish execution the consumer will tag this commit with a git
137
+ tag, the tag name will be the consumer name, if your consumer name is
138
+ `consumer1`, the tag will be `consumer1`, as git tags are global to all the
139
+ repository you need to make the consumer name unique not just for the branch but
140
+ for all the repository, so if you want to consume 2 branches from one consumer
141
+ you'll have to choose to different names so that tags are not overriden be every
142
+ consumer instance, you can adopt a naming scheme that involve the branch name,
143
+ while implementing this gem we had that name tag name as `branch.consumer` but
144
+ then thought it'll be better for the user to make this decision to allow for a
145
+ more flexible naming, the tag will be overritten after each time the consumer
146
+ process a message.
147
+
148
+ # Pushing and Pulling your data
149
+
150
+ As for now there is no way to push/pull from the gem, so this part is up to you,
151
+ until it's implemented in the gem.
152
+
153
+ # Gem executables
154
+
155
+ ## gitmqueue-produce
156
+
157
+ Starts a process that waits you to enter any line and publish that as a message
158
+
159
+ Usage:
160
+
161
+ ```
162
+ gitmqueue-produce /path/to/rpo branch-name
163
+ ```
164
+
165
+ ## gitmqueue-consume
166
+
167
+ Starts a process that waits for messages, each message will be written to the
168
+ terminal
169
+
170
+ Usage:
171
+
172
+ ```
173
+ gitmqueue-consume /path/to/repo branch-name consumer-name
174
+ ```
@@ -13,8 +13,10 @@ consumer = GitMQueue::Consumer.new(
13
13
  branch: branch
14
14
  )
15
15
 
16
+ consumer.consume { |m| puts m }
17
+
16
18
  begin
17
- consumer.consume { |m| puts m }
19
+ sleep
18
20
  rescue Interrupt
19
21
  puts 'Bye.'
20
22
  end
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ['lib']
29
29
 
30
30
  spec.add_dependency 'rugged', '~> 0.28'
31
+ spec.add_dependency 'listen', '~> 3.2.0'
31
32
  spec.add_development_dependency 'bundler', '~> 2.0'
32
33
  spec.add_development_dependency 'rake', '~> 10.0'
33
34
  spec.add_development_dependency 'rspec', '~> 3.0'
@@ -1,21 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'listen'
4
+
3
5
  module GitMQueue
4
6
  class Consumer
5
7
  def initialize(storage:, name:, branch:)
6
8
  @storage = storage
7
9
  @branch = branch
8
10
  @name = name
9
- @label = "#{branch}.#{name}"
11
+ end
12
+
13
+ def branch_file
14
+ @branch_file ||= File.join(@storage.path, 'refs', 'heads')
10
15
  end
11
16
 
12
17
  def consume(&block)
13
- @storage.wait_branch @branch
18
+ @block = block
19
+ listener.start
20
+ end
21
+
22
+ def stop
23
+ listener.stop
24
+ end
25
+
26
+ private
14
27
 
15
- loop do
16
- commit = @storage.poll(@branch, @label)
17
- block.call commit.message
18
- @storage.tag(@label, commit)
28
+ def listener
29
+ @listener ||= Listen.to(branch_file, only: /#{Regexp.escape(@branch)}/) do
30
+ commits = @storage.commits(@branch, @name)
31
+ commits.each do |commit|
32
+ @block.call commit.message
33
+ @storage.tag(@name, commit)
34
+ end
19
35
  end
20
36
  end
21
37
  end
@@ -5,10 +5,7 @@ require 'rugged'
5
5
 
6
6
  module GitMQueue
7
7
  class Storage
8
- WAIT_NON_EXISTING_BRANCH = 1 # seconds
9
- WAIT_FOR_COMMIT = 1 # seconds
10
-
11
- attr_reader :repo
8
+ attr_reader :repo, :path
12
9
 
13
10
  def initialize(path)
14
11
  @path = path
@@ -27,34 +24,22 @@ module GitMQueue
27
24
  Rugged::Tree::Builder.new(repo).write
28
25
  end
29
26
 
30
- def poll(branch, tag)
31
- wait_for_commit(branch, tag)
32
- walker(branch, tag).first
33
- end
34
-
35
- def tag(label, commit)
36
- repo.tags.create(label, commit, true)
37
- end
38
-
39
- def wait_branch(branch)
40
- sleep WAIT_NON_EXISTING_BRANCH until repo.branches.exist?(branch)
41
- end
42
-
43
- private
44
-
45
- attr_reader :path
46
-
47
- def walker(branch, tag)
27
+ def commits(branch, tag)
48
28
  walker = Rugged::Walker.new(repo)
49
29
  walker.sorting(Rugged::SORT_TOPO | Rugged::SORT_REVERSE)
50
30
  walker.push(branch(branch).target)
51
31
 
52
32
  tag = repo.tags[tag]
53
33
  walker.hide(tag.target) if tag
54
-
55
34
  walker
56
35
  end
57
36
 
37
+ def tag(label, commit)
38
+ repo.tags.create(label, commit, true)
39
+ end
40
+
41
+ private
42
+
58
43
  def read_or_create_repo
59
44
  FileUtils.mkdir_p(path) unless File.exist?(path)
60
45
  begin
@@ -63,9 +48,5 @@ module GitMQueue
63
48
  Rugged::Repository.init_at(path, :bare)
64
49
  end
65
50
  end
66
-
67
- def wait_for_commit(branch, tag)
68
- sleep WAIT_FOR_COMMIT until branch(branch).target.oid != repo.tags[tag]&.target&.oid
69
- end
70
51
  end
71
52
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GitMQueue
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitmqueue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emad Elsaid
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-24 00:00:00.000000000 Z
11
+ date: 2019-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rugged
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.28'
27
+ - !ruby/object:Gem::Dependency
28
+ name: listen
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.2.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement