falconer 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/Gemfile +3 -3
- data/Gemfile.lock +1 -1
- data/Guardfile +3 -3
- data/README.md +1 -0
- data/Rakefile +4 -4
- data/lib/falconer/class_methods.rb +22 -22
- data/lib/falconer/rack.rb +17 -11
- data/lib/falconer/rails.rb +18 -18
- data/lib/falconer/store/memory.rb +12 -12
- data/lib/falconer/version.rb +1 -1
- data/lib/falconer.rb +3 -3
- data/spec/falconer/class_methods_spec.rb +47 -47
- data/spec/falconer/rack_spec.rb +62 -51
- data/spec/falconer/rails_spec.rb +24 -24
- data/spec/falconer/store/memory_spec.rb +45 -45
- data/spec/falconer_spec.rb +11 -11
- metadata +11 -10
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/Guardfile
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
guard 'minitest' do
|
5
5
|
|
6
6
|
# with Minitest::Spec
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
watch(%r|^spec/(.*)_spec\.rb|)
|
8
|
+
watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
9
|
+
watch(%r|^spec/spec_helper\.rb|) { "spec" }
|
10
10
|
|
11
11
|
end
|
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -3,14 +3,14 @@ $:.unshift File.expand_path("../lib", __FILE__)
|
|
3
3
|
require 'falconer/version'
|
4
4
|
|
5
5
|
task :build do
|
6
|
-
|
6
|
+
system "gem build falconer.gemspec"
|
7
7
|
end
|
8
8
|
|
9
9
|
require 'rake/testtask'
|
10
10
|
Rake::TestTask.new do |t|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
t.pattern = 'spec/**/*_spec.rb'
|
12
|
+
t.libs << 'spec'
|
13
|
+
t.libs << 'lib'
|
14
14
|
end
|
15
15
|
|
16
16
|
desc "Run Specs"
|
@@ -1,30 +1,30 @@
|
|
1
1
|
module Falconer
|
2
|
-
|
2
|
+
class << self
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
# Triggers a new event to be sent to the Falconer Client
|
5
|
+
#
|
6
|
+
# @param [String] event the name of the event to be triggered
|
7
|
+
# @param [Object] data the data associated with the event
|
8
|
+
# This data will be transimitted through JSON encoding
|
9
|
+
def trigger(event, data)
|
10
|
+
store.add_event(event, data)
|
11
|
+
end
|
12
12
|
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
# Returns all queued events and empties queue
|
15
|
+
#
|
16
|
+
# @return [Array] list of event tuples ([event, data])
|
17
|
+
def flush
|
18
|
+
returned_events = store.events
|
19
|
+
store.clear_events
|
20
|
+
returned_events
|
21
|
+
end
|
22
22
|
|
23
|
-
|
23
|
+
protected
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
def store
|
26
|
+
@store ||= Falconer::Store::Memory.new
|
27
|
+
end
|
28
28
|
|
29
|
-
|
29
|
+
end
|
30
30
|
end
|
data/lib/falconer/rack.rb
CHANGED
@@ -1,17 +1,23 @@
|
|
1
1
|
require 'json'
|
2
2
|
|
3
3
|
class Falconer::Rack
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
def call(env)
|
9
|
+
unless env['PATH_INFO'] == '/@falconer-poll'
|
10
|
+
status, headers, response = @app.call(env)
|
11
|
+
else
|
12
|
+
status = 200
|
13
|
+
headers = {'Content-Length'=> 0}
|
14
|
+
response = ''
|
15
|
+
end
|
10
16
|
|
11
|
-
|
12
|
-
|
13
|
-
|
17
|
+
if env[Falconer::ACCEPT_HEADER_ENV]
|
18
|
+
headers[Falconer::EVENTS_HEADER] = Falconer.flush.to_json
|
19
|
+
end
|
14
20
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
21
|
+
[status, headers, response]
|
22
|
+
end
|
23
|
+
end
|
data/lib/falconer/rails.rb
CHANGED
@@ -3,26 +3,26 @@ require 'falconer'
|
|
3
3
|
|
4
4
|
module Falconer::Rails
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
module ControllerMethods
|
7
|
+
protected
|
8
|
+
def falconer
|
9
|
+
Falconer
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Engine < ::Rails::Engine
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
initializer 'falconer.add_middleware' do |app|
|
16
|
+
app.middleware.insert_after ::Rack::Lock, Falconer::Rack
|
17
|
+
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
initializer 'falconer.controller_methods' do
|
20
|
+
ActiveSupport.on_load :action_controller do
|
21
|
+
include Falconer::Rails::ControllerMethods
|
22
|
+
helper_method :falconer
|
23
|
+
end
|
24
|
+
end
|
25
25
|
|
26
|
-
|
26
|
+
end
|
27
27
|
end
|
28
28
|
|
@@ -2,20 +2,20 @@ require 'falconer/store'
|
|
2
2
|
|
3
3
|
class Falconer::Store::Memory
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
def initialize
|
6
|
+
@events = []
|
7
|
+
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
def add_event(event, data=nil)
|
10
|
+
@events << [event, data]
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
def events
|
14
|
+
@events.dup
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def clear_events
|
18
|
+
@events = []
|
19
|
+
end
|
20
20
|
|
21
21
|
end
|
data/lib/falconer/version.rb
CHANGED
data/lib/falconer.rb
CHANGED
@@ -5,8 +5,8 @@ require 'falconer/store/memory'
|
|
5
5
|
require 'falconer/rack'
|
6
6
|
|
7
7
|
module Falconer
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
ACCEPT_HEADER = 'x-falconer-accept-events'
|
9
|
+
EVENTS_HEADER = 'x-falconer-events'
|
10
|
+
ACCEPT_HEADER_ENV = "HTTP_#{ACCEPT_HEADER.upcase.gsub('-','_')}"
|
11
11
|
end
|
12
12
|
|
@@ -1,67 +1,67 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Falconer, 'class methods' do
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
it 'should respond to trigger' do
|
5
|
+
Falconer.must_respond_to :trigger
|
6
|
+
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
it 'should respond to flush' do
|
9
|
+
Falconer.must_respond_to :flush
|
10
|
+
end
|
11
11
|
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
describe 'flush' do
|
14
|
+
describe 'with no triggered events' do
|
15
|
+
it 'should return an empty array' do
|
16
|
+
Falconer.flush.must_equal []
|
17
|
+
end
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
describe 'with a single triggered event' do
|
21
|
+
before do
|
22
|
+
Falconer.flush
|
23
|
+
Falconer.trigger 'event-one', {'some' => 'data'}
|
24
|
+
end
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
it 'should return an array with one event' do
|
27
|
+
Falconer.flush.size.must_equal 1
|
28
|
+
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
it 'should return the event as a size 2 tuple' do
|
31
|
+
Falconer.flush[0].size.must_equal 2
|
32
|
+
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
it 'should return an event as an array with name and data' do
|
35
|
+
Falconer.flush.must_equal [['event-one', {'some' => 'data'}]]
|
36
|
+
end
|
37
37
|
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
it 'should clear the data after flushed' do
|
40
|
+
Falconer.flush
|
41
|
+
Falconer.flush.must_equal []
|
42
|
+
end
|
43
|
+
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
45
|
+
describe 'with several triggered events' do
|
46
|
+
before do
|
47
|
+
Falconer.flush
|
48
|
+
Falconer.trigger 'event-one', {'one' => 1}
|
49
|
+
Falconer.trigger 'event-two', {'two' => 2}
|
50
|
+
Falconer.trigger 'event-three', {'three' => 3}
|
51
|
+
end
|
52
52
|
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
54
|
+
it 'should return events in first-in-first-out order' do
|
55
|
+
Falconer.flush.must_equal [
|
56
|
+
['event-one', {'one' => 1}] ,
|
57
|
+
['event-two', {'two' => 2}] ,
|
58
|
+
['event-three', {'three' => 3}]
|
59
|
+
]
|
60
60
|
|
61
|
-
|
61
|
+
end
|
62
62
|
|
63
|
-
|
63
|
+
end
|
64
64
|
|
65
|
-
|
65
|
+
end
|
66
66
|
|
67
67
|
end
|
data/spec/falconer/rack_spec.rb
CHANGED
@@ -1,57 +1,68 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Falconer::Rack do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
Rack::Builder.new do
|
8
|
+
use Falconer::Rack
|
9
|
+
run Rack::Lobster.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def falconer_accept_header
|
14
|
+
{"HTTP_#{Falconer::ACCEPT_HEADER.upcase.gsub '-', '_'}" => '1'}
|
15
|
+
end
|
16
|
+
|
17
|
+
def events_header
|
18
|
+
last_response.headers[Falconer::EVENTS_HEADER]
|
19
|
+
end
|
20
|
+
|
21
|
+
before do
|
22
|
+
Falconer.flush
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'falconer-request' do
|
26
|
+
it 'should return a falconer event header' do
|
27
|
+
get '/', {}, falconer_accept_header
|
28
|
+
events_header.wont_be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return a JSON encoded array' do
|
32
|
+
Falconer.trigger 'foo', {:bar => 1}
|
33
|
+
get '/', {}, falconer_accept_header
|
34
|
+
events = JSON.parse events_header
|
35
|
+
events.size.must_equal 1
|
36
|
+
events[0].must_equal ['foo', {'bar'=> 1}]
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'polling endpoint' do
|
43
|
+
it 'should return 200 for the polling acition' do
|
44
|
+
Falconer.trigger 'foo', {:bar => 1}
|
45
|
+
get '/@falconer-poll', {}, falconer_accept_header
|
46
|
+
last_response.body.must_equal ''
|
47
|
+
events = JSON.parse events_header
|
48
|
+
events.size.must_equal 1
|
49
|
+
events[0].must_equal ['foo', {'bar'=> 1}]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'normal request' do
|
54
|
+
it 'should return 200' do
|
55
|
+
get '/'
|
56
|
+
last_response.status.must_be :==, 200
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should not send falconer events header' do
|
60
|
+
get '/'
|
61
|
+
last_response.headers[Falconer::EVENTS_HEADER].must_be_nil
|
62
|
+
last_response.headers['content-type'].wont_be_nil
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
55
66
|
|
56
67
|
end
|
57
68
|
|
data/spec/falconer/rails_spec.rb
CHANGED
@@ -3,35 +3,35 @@ require 'falconer/rails'
|
|
3
3
|
|
4
4
|
describe Falconer::Rails do
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
describe "Middleware" do
|
7
|
+
it 'inserts Falconer::Rack after Rack::Lock' do
|
8
|
+
Rails.configuration.middleware.must_include Falconer::Rack
|
9
|
+
end
|
10
|
+
end
|
11
11
|
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
describe 'Controller extensions' do
|
14
|
+
before do
|
15
|
+
@controller = ApplicationController.new
|
16
|
+
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
it 'should have a falconer method' do
|
19
|
+
@controller.must_respond_to :falconer
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
it 'should return the Falconer Class for falconer' do
|
23
|
+
@controller.send(:falconer).must_equal Falconer
|
24
|
+
end
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
it 'should be private' do
|
27
|
+
lambda {
|
28
|
+
@controller.falconer
|
29
|
+
}.must_raise NoMethodError
|
30
|
+
end
|
31
31
|
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
it 'should expose falconer as helper method' do
|
34
|
+
@controller.send(:_helper_methods).must_include :falconer
|
35
|
+
end
|
36
|
+
end
|
37
37
|
end
|
@@ -1,50 +1,50 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Falconer::Store::Memory do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
4
|
+
before do
|
5
|
+
@store = Falconer::Store::Memory.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'empty store' do
|
9
|
+
it 'should be empty' do
|
10
|
+
@store.events.must_equal []
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'with one event' do
|
15
|
+
before do
|
16
|
+
@store.add_event 'name-1', 'data-1'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should have one event' do
|
20
|
+
@store.events.must_equal [['name-1', 'data-1']]
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should clear events' do
|
24
|
+
@store.events.must_equal [['name-1', 'data-1']]
|
25
|
+
@store.clear_events
|
26
|
+
@store.events.must_equal []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'with multiple events' do
|
31
|
+
before do
|
32
|
+
@events = [
|
33
|
+
['event-1', 'data1'] ,
|
34
|
+
['event-2', 'data2'] ,
|
35
|
+
['event-3', 'data3']
|
36
|
+
]
|
37
|
+
|
38
|
+
@events.each do |(event, data)|
|
39
|
+
@store.add_event event, data
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should have all events in order' do
|
45
|
+
@store.events.must_equal @events
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
49
|
|
50
50
|
end
|
data/spec/falconer_spec.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Falconer do
|
4
|
-
|
4
|
+
describe 'constants' do
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
it 'should define the events header' do
|
7
|
+
Falconer::EVENTS_HEADER.must_equal 'x-falconer-events'
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
it 'should define the accept events header' do
|
11
|
+
Falconer::ACCEPT_HEADER.must_equal 'x-falconer-accept-events'
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
it 'should define the accept events header env key' do
|
15
|
+
Falconer::ACCEPT_HEADER_ENV.must_equal 'HTTP_X_FALCONER_ACCEPT_EVENTS'
|
16
|
+
end
|
17
17
|
|
18
|
-
|
18
|
+
end
|
19
19
|
|
20
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: falconer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03
|
12
|
+
date: 2012-04-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
16
|
-
requirement: &
|
16
|
+
requirement: &82338080 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '1.5'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *82338080
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rails
|
27
|
-
requirement: &
|
27
|
+
requirement: &82337800 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.2.2
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *82337800
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: sqlite3
|
38
|
-
requirement: &
|
38
|
+
requirement: &82337610 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *82337610
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rake
|
49
|
-
requirement: &
|
49
|
+
requirement: &82337360 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,13 +54,14 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *82337360
|
58
58
|
description: Falconer Event Endpoint for Ruby and Rack
|
59
59
|
email: jim@jimhoskins.com
|
60
60
|
executables: []
|
61
61
|
extensions: []
|
62
62
|
extra_rdoc_files: []
|
63
63
|
files:
|
64
|
+
- .gitignore
|
64
65
|
- .travis.yml
|
65
66
|
- CHANGELOG.md
|
66
67
|
- Gemfile
|