github-fs 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d1616fb6a15a1deb942a3a51dae8f2180a0cf8c6
4
+ data.tar.gz: ab0f6caf968fcd89b87187998dfe0ea2f009d3ba
5
+ SHA512:
6
+ metadata.gz: db7346a7fdb1f91bf90ebad5f1748ea60ebac725e344430520a67ddbd9c85dd411d747c1baa70dac87bdc6b1bbc597b3cf3a9bbbae8694cf7681a68dfb8aab5d
7
+ data.tar.gz: 859d86b7b716cb2c372aa5e804e7d62a61716a8240db169afbfda400ed30e26325b2c2d8ff42e70d2ebba9095d728e64c2cc5553a2ea6f9400ee1123a8738da3
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ spec/credentials.yml
16
+ githubfs-test/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in github-fs.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Architects.io, Inc. and Jonathan Soeder
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # Github Filesystem
2
+
3
+ This is a ruby wrapper that intends to provide an easy `File`-esque
4
+ wrapper round the Github API's contents endpoints.
5
+
6
+ This will allow you to create, read, find, update, and delete files using a familiar API based off of ruby's own `File` and `Dir` classes.
7
+
8
+ ### TODO
9
+
10
+ - [x] Implement File.open("path", "a+") mode
11
+ - [x] Implement File.open("path", "a") mode
12
+ - [x] Implement File.open("path", "w") mode
13
+ - [x] Implement File.open("path", "w+") mode
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ #!/usr/bin/env rake
4
+ begin
5
+ require 'bundler/setup'
6
+ rescue LoadError
7
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
8
+ end
9
+
10
+ Bundler::GemHelper.install_tasks
11
+
12
+ Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
13
+
14
+ require 'rspec/core'
15
+ require 'rspec/core/rake_task'
16
+
17
+ desc "Run all specs in spec directory (excluding plugin specs)"
18
+
19
+ RSpec::Core::RakeTask.new(:spec)
20
+
21
+ task :default => :spec
data/github-fs.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'github-fs/version.rb'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "github-fs"
8
+ spec.version = GHFS::VERSION
9
+ spec.authors = ["Jonathan Soeder"]
10
+ spec.email = ["jonathan.soeder@gmail.com"]
11
+ spec.summary = %q{Work with the Github Contents API the way you would a normal file object}
12
+ spec.description = %q{Creating, updating, deleting files on the github api using the same APIs as you would working with files your local file system.}
13
+ spec.homepage = "https://github.com/architects/github-fs"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'octokit'
22
+ spec.add_dependency 'rack'
23
+ spec.add_dependency 'activesupport', '> 3.2.0'
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "> 3.0.0"
28
+ spec.add_development_dependency "pry"
29
+ end
data/lib/github-fs.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'pathname'
2
+ require 'singleton'
3
+ require 'yaml'
4
+ require 'octokit'
5
+ require 'github-fs/version'
6
+ require 'github-fs/util'
7
+ require 'github-fs/test_helpers'
8
+ require 'github-fs/config'
9
+ require 'github-fs/file'
10
+ require 'github-fs/dir'
11
+ require 'rack'
12
+ require 'active_support/core_ext'
13
+
14
+ module GHFS
15
+ NotFound = Class.new(Exception)
16
+ ReadOnly = Class.new(Exception)
17
+
18
+ def self.config
19
+ GHFS::Config.instance
20
+ end
21
+
22
+ def self.api
23
+ @api ||= Octokit::Client.new(access_token: config.github_access_token)
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ module GHFS
2
+ class Config
3
+ include Singleton
4
+
5
+ def self.method_missing(meth, *args, &block)
6
+ if instance.respond_to?(meth)
7
+ instance.send(meth, *args, &block)
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ def branch= value
14
+ @branch = value
15
+ end
16
+
17
+ def branch
18
+ @branch || "master"
19
+ end
20
+
21
+ def github_access_token= value
22
+ @github_access_token = value
23
+ end
24
+
25
+ def github_access_token
26
+ @github_access_token || raise("Github access token is not set")
27
+ end
28
+
29
+ def repository= value
30
+ @repository = value
31
+ end
32
+
33
+ def repository
34
+ @repository || raise("Github repository is not set")
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,10 @@
1
+ module GHFS
2
+ class Dir
3
+ class << self
4
+ def [](path)
5
+ # TODO
6
+ # Implement
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,132 @@
1
+ module GHFS
2
+ class File
3
+ class << self
4
+ def open(path, mode=nil, options={}, &block)
5
+ file = new(path, mode, options)
6
+ yield(file) if block_given?
7
+ file
8
+ end
9
+ end
10
+
11
+ attr_reader :path, :mode, :options, :repository, :branch
12
+
13
+ def initialize(path, mode="w", options={})
14
+ @path = path
15
+ @mode = mode || "w"
16
+ @options = options
17
+ @repository = options.fetch(:repository) { GHFS.config.repository }
18
+ @branch = options.fetch(:branch) { GHFS.config.branch }
19
+ @contents = options[:contents] || options[:content]
20
+ end
21
+
22
+ def contents
23
+ @contents || read
24
+ end
25
+
26
+ def read
27
+ @contents ||= begin
28
+ encoded = api_entity.content rescue nil
29
+
30
+ if encoded.to_s.length > 0
31
+ Base64.decode64(encoded)
32
+ end
33
+ end
34
+ end
35
+
36
+ def delete
37
+ raise GHFS::NotFound if new_file?
38
+ raise GHFS::ReadOnly if read_only?
39
+ GHFS.api.delete_contents(repo, path, "Deleting #{ path }", branch: branch)
40
+ end
41
+
42
+ def unlink
43
+ delete
44
+ end
45
+
46
+ def write(contents)
47
+ read
48
+
49
+ if append?
50
+ @contents = @contents.to_s
51
+ @contents += contents
52
+ else
53
+ @contents = contents
54
+ end
55
+
56
+ @contents
57
+ ensure
58
+ with_contents_persisted
59
+ @contents
60
+ end
61
+
62
+ def puts(contents)
63
+ contents = "#{ contents }\n" unless contents.to_s.match(/\n$/m)
64
+ write(contents)
65
+ end
66
+
67
+ def api_entity
68
+ @api_entity ||= GHFS.api.contents(repository, path: path, ref: ref)
69
+ rescue Octokit::NotFound
70
+ @new_file = true
71
+ rescue
72
+ nil
73
+ end
74
+
75
+ def with_contents_persisted
76
+ case
77
+ when read_only?
78
+ raise GHFS::ReadOnly
79
+ when !should_create? && new_file?
80
+ raise GHFS::NotFound
81
+ when new_file? && should_create?
82
+ @new_file = nil
83
+ message = "Creating #{ path }"
84
+ GHFS.api.create_contents(repository, path, message, encoded_contents, branch: branch)
85
+ when !new_file?
86
+ @new_file = nil
87
+ message = "Updating #{ path }"
88
+ GHFS.api.update_contents(repository, path, message, latest_sha, encoded_contents, branch: branch)
89
+ end
90
+ end
91
+
92
+ def encoded_contents
93
+ # the octokit gem handles encoding for us
94
+ contents.to_s
95
+ end
96
+
97
+ def new_file?
98
+ api_entity unless @api_entity
99
+ !@new_file.nil?
100
+ end
101
+
102
+ def latest_sha
103
+ !new_file? && api_entity && api_entity.sha
104
+ end
105
+
106
+ def exists?
107
+ !new_file?
108
+ end
109
+
110
+ def read_only?
111
+ mode == "r"
112
+ end
113
+
114
+ def append?
115
+ mode == "a" || mode == "a+"
116
+ end
117
+
118
+ def writable?
119
+ append? || mode == "w" || mode == "w+"
120
+ end
121
+
122
+ def should_create?
123
+ mode == "w+" || mode == "a+"
124
+ end
125
+
126
+ def ref
127
+ options.fetch(:ref) do
128
+ branch || options[:commit] || options[:tag] || "master"
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,6 @@
1
+ module GHFS
2
+ module TestHelpers
3
+ def self.test_branch
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,43 @@
1
+ module GHFS
2
+ module Util
3
+ def self.use_temporary_branch
4
+ GHFS.config.branch = temporary_branch
5
+ create_branch(temporary_branch)
6
+ end
7
+
8
+ def self.remove_temporary_branch
9
+ if temporary_branch_exists?
10
+ remove_branch(temporary_branch)
11
+ end
12
+ end
13
+
14
+ def self.temporary_branch_exists?
15
+ !@temporary_branch.nil?
16
+ end
17
+
18
+ def self.temporary_branch
19
+ @temporary_branch ||= "temp-branch-#{ rand(36**36).to_s(36).slice(0,8) }"
20
+ end
21
+
22
+ def self.create_branch(name, repo=nil)
23
+ if repo ||= GHFS.config.repository
24
+ master_branch = GHFS.api.branch(repo, "master")
25
+ master_sha = master_branch.commit.sha
26
+ refs = GHFS.api.create_ref(repo, "heads/#{ name }", master_sha)
27
+
28
+ ref = Array(refs).flatten.last
29
+ ref && ref.commit && ref.commit.sha
30
+ end
31
+ end
32
+
33
+ def self.remove_branch(name, repo=nil)
34
+ if repo ||= GHFS.config.repository
35
+ names = GHFS.api.branches(repo).map(&:name)
36
+
37
+ if names.any? {|n| n == name }
38
+ GHFS.api.delete_branch(repo, name)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module GHFS
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+
3
+ describe GHFS::Config do
4
+ it "requires a github access token" do
5
+ expect(GHFS.config.github_access_token).not_to be_nil
6
+ end
7
+ end
@@ -0,0 +1,120 @@
1
+ require "spec_helper"
2
+
3
+ describe GHFS::File do
4
+ let(:file) do
5
+ GHFS::File.open("files/existing.txt")
6
+ end
7
+
8
+ let(:new_file) { GHFS::File.open("files/new.txt") }
9
+
10
+ describe "File Modes" do
11
+ it "knows if it should create a file that doesnt exist" do
12
+ %w(w+ a+).each do |mode|
13
+ file = GHFS::File.open("modes.txt", mode)
14
+ expect(file).to be_should_create
15
+ end
16
+ end
17
+
18
+ it "has modes which will not create new files" do
19
+ %w(w a).each do |mode|
20
+ file = GHFS::File.open("modes.txt", mode)
21
+ expect(file).not_to be_should_create
22
+ end
23
+ end
24
+
25
+ it "has append modes" do
26
+ file = GHFS::File.open("modes.txt", "a+")
27
+ expect(file).to be_append
28
+ end
29
+
30
+ it "has writable modes" do
31
+ %w(w a w+ a+).each do |mode|
32
+ file = GHFS::File.open("modes.txt", mode)
33
+ expect(file).to be_writable
34
+ expect(file).not_to be_read_only
35
+ end
36
+ end
37
+
38
+ it "has read only modes" do
39
+ file = GHFS::File.open("modes.txt", "r")
40
+ expect(file).not_to be_writable
41
+ expect(file).to be_read_only
42
+ end
43
+ end
44
+
45
+ describe "Flags" do
46
+ it "lets me know if a file exists" do
47
+ expect(file).to be_exists
48
+ end
49
+
50
+ it "lets me know if the file is a new file" do
51
+ expect(new_file).to be_new_file
52
+ expect(new_file).not_to be_exists
53
+ end
54
+ end
55
+
56
+ describe "Reading" do
57
+ it "lets me read the contents from a file" do
58
+ expect(file.read).to match("This is some existing content")
59
+ end
60
+ end
61
+
62
+ describe "Writing" do
63
+ it "fails if i attempt to perform an operation on a missing file and do not specify a create flag" do
64
+ expect(lambda do
65
+ GHFS::File.open("missing.txt", "a") do |fh|
66
+ fh.write("shit")
67
+ end
68
+ end).to raise_error(GHFS::NotFound)
69
+ end
70
+
71
+ it "lets me append to an existing file" do
72
+ GHFS::File.open("append-existing.txt", "w+") do |fh|
73
+ fh.write("begin")
74
+ end
75
+
76
+ file = GHFS::File.open("append-existing.txt", "a") do |fh|
77
+ fh.write("end")
78
+ end
79
+
80
+ content = file.read
81
+
82
+ expect(content).to include("begin")
83
+ expect(content).to include("end")
84
+ end
85
+
86
+ it "lets me append to or create a file" do
87
+ needle = rand(36**36).to_s(36)
88
+
89
+ expect(lambda do
90
+ GHFS::File.open("append-create.txt", "a+") do |fh|
91
+ fh.write("appending #{ needle }")
92
+ end
93
+ end).not_to raise_error
94
+
95
+ file = GHFS::File.open("append-create.txt","r")
96
+ expect(file.read).to include(needle)
97
+ end
98
+
99
+ it "lets me modify the contents of a file" do
100
+ content = file.read
101
+ needle = rand(36**36).to_s(36)
102
+ file.write("#{ content }\n#{ needle }")
103
+
104
+ modified = GHFS::File.open("files/existing.txt")
105
+ expect(modified.read).to match(content)
106
+ expect(modified.read).to match(needle)
107
+ end
108
+
109
+ it "lets me modify the contents of or create a file" do
110
+ brand_new = GHFS::File.open("files/brand-spanking-new.txt","w+") do |fh|
111
+ fh.write("this is some brand new stuff")
112
+ end
113
+
114
+ expect(brand_new).not_to be_new_file
115
+ expect(brand_new).to be_exists
116
+ expect(brand_new.latest_sha).not_to be_nil
117
+ end
118
+ end
119
+
120
+ end
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+
3
+ describe GHFS do
4
+ it "should have access to the github api" do
5
+ expect(GHFS.api.user.login).not_to be_nil
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ require 'github-fs'
2
+ require 'pry'
3
+
4
+
5
+ RSpec.configure do |config|
6
+ config.mock_with :rspec
7
+ config.order = :random
8
+
9
+ config.before(:suite) do
10
+ cfg = YAML.load(IO.read(File.dirname(__FILE__) + "/credentials.yml"))
11
+
12
+ GHFS::Config.repository = cfg[:repository] || cfg["repository"]
13
+ GHFS::Config.github_access_token = cfg[:github_access_token] || cfg["github_access_token"]
14
+
15
+ GHFS::Util.use_temporary_branch
16
+ end
17
+
18
+ config.after(:suite) do
19
+ GHFS::Util.remove_temporary_branch
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: github-fs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Soeder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: octokit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.2.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">"
88
+ - !ruby/object:Gem::Version
89
+ version: 3.0.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">"
95
+ - !ruby/object:Gem::Version
96
+ version: 3.0.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Creating, updating, deleting files on the github api using the same APIs
112
+ as you would working with files your local file system.
113
+ email:
114
+ - jonathan.soeder@gmail.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - Gemfile
121
+ - LICENSE.txt
122
+ - README.md
123
+ - Rakefile
124
+ - github-fs.gemspec
125
+ - lib/github-fs.rb
126
+ - lib/github-fs/config.rb
127
+ - lib/github-fs/dir.rb
128
+ - lib/github-fs/file.rb
129
+ - lib/github-fs/test_helpers.rb
130
+ - lib/github-fs/util.rb
131
+ - lib/github-fs/version.rb
132
+ - spec/lib/github-fs/config_spec.rb
133
+ - spec/lib/github-fs/file_spec.rb
134
+ - spec/lib/github_fs_spec.rb
135
+ - spec/spec_helper.rb
136
+ homepage: https://github.com/architects/github-fs
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.2.2
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: Work with the Github Contents API the way you would a normal file object
160
+ test_files:
161
+ - spec/lib/github-fs/config_spec.rb
162
+ - spec/lib/github-fs/file_spec.rb
163
+ - spec/lib/github_fs_spec.rb
164
+ - spec/spec_helper.rb
165
+ has_rdoc: