rosette-client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ # encoding: UTF-8
2
+
3
+ module Rosette
4
+ module Client
5
+
6
+ class Repo
7
+ attr_reader :path
8
+
9
+ def initialize(path)
10
+ @path = path
11
+ end
12
+
13
+ def get_head
14
+ execute_in_repo('git symbolic-ref --short HEAD').strip
15
+ end
16
+
17
+ def rev_parse(ref)
18
+ commit_id = execute_in_repo("git rev-parse #{ref}").strip
19
+ commit_id ? commit_id : nil
20
+ end
21
+
22
+ private
23
+
24
+ def execute_in_repo(cmd)
25
+ Dir.chdir(path) do
26
+ `#{cmd}`
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: UTF-8
2
+
3
+ module Rosette
4
+ module Client
5
+
6
+ class Response
7
+ attr_reader :attributes
8
+
9
+ def self.from_api_response(hash)
10
+ new(hash)
11
+ end
12
+
13
+ def initialize(hash)
14
+ @attributes = hash
15
+ end
16
+
17
+ def error?
18
+ attributes.include?('error')
19
+ end
20
+
21
+ def success?
22
+ !error?
23
+ end
24
+
25
+ def method_missing(method, *args, &block)
26
+ if attributes.include?(method.to_s)
27
+ attributes[method.to_s]
28
+ end
29
+ end
30
+
31
+ # responds to everything, returns nil for any unset attributes
32
+ def respond_to?(method)
33
+ true
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'colorize'
4
+
5
+ module Rosette
6
+ module Client
7
+ class Terminal
8
+
9
+ attr_reader :stream
10
+
11
+ def initialize(stream = STDOUT)
12
+ @stream = stream
13
+ end
14
+
15
+ def say(str, color = :none)
16
+ stream.write(colorize(str, color))
17
+ stream.write("\n")
18
+ end
19
+
20
+ private
21
+
22
+ def colorize(str, color)
23
+ case color
24
+ when :none
25
+ str
26
+ else
27
+ str.colorize(color)
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+
3
+ module Rosette
4
+ module Client
5
+ VERSION = "1.0.0"
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rosette/client/cli'
4
+ require 'rosette/client/repo'
5
+
6
+ module Rosette
7
+ module Client
8
+ autoload :Api, 'rosette/client/api'
9
+ autoload :Terminal, 'rosette/client/terminal'
10
+ autoload :Commands, 'rosette/client/commands'
11
+ autoload :Response, 'rosette/client/response'
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), 'lib')
2
+ require 'rosette/client/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "rosette-client"
6
+ s.version = ::Rosette::Client::VERSION
7
+ s.authors = ["Cameron Dutro"]
8
+ s.email = ["camertron@gmail.com"]
9
+ s.homepage = "http://github.com/camertron"
10
+
11
+ s.description = s.summary = "Git command integration for the Rosette internationalization platform that manages the translatable content in the source files of a git repository."
12
+
13
+ s.platform = Gem::Platform::RUBY
14
+ s.has_rdoc = true
15
+
16
+ s.add_dependency 'colorize', '~> 0.7.0'
17
+ s.add_dependency 'json', '~> 1.8.0'
18
+
19
+ s.executables << 'git-rosette'
20
+
21
+ s.require_path = 'lib'
22
+ s.files = Dir["{lib,spec}/**/*", "Gemfile", "History.txt", "README.md", "Rakefile", "rosette-client.gemspec"]
23
+ end
data/spec/api_spec.rb ADDED
@@ -0,0 +1,96 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ include Rosette::Client
6
+
7
+ describe Api do
8
+ let(:api_class) { Api }
9
+
10
+ it 'uses default options' do
11
+ api_class.new.tap do |api|
12
+ expect(api.host).to eq('localhost')
13
+ expect(api.port).to eq(8080)
14
+ expect(api.version).to eq('v1')
15
+ end
16
+ end
17
+
18
+ it 'uses only some default options' do
19
+ api_class.new(host: 'foo.com').tap do |api|
20
+ expect(api.host).to eq('foo.com')
21
+ expect(api.port).to eq(8080)
22
+ expect(api.version).to eq('v1')
23
+ end
24
+ end
25
+
26
+ shared_examples 'an api endpoint' do
27
+ let(:api) { api_class.new }
28
+ let(:params) { { param: 'value' } }
29
+ let(:endpoint) { method }
30
+
31
+ before(:each) do
32
+ url = "http://localhost:8080/v1/#{path}"
33
+
34
+ args = case verb
35
+ when :get
36
+ ["#{url}/#{endpoint_override rescue endpoint}.json/?param=value"]
37
+ else
38
+ ["#{url}/#{endpoint_override rescue endpoint}.json", params]
39
+ end
40
+
41
+ allow(api).to receive(verb).with(*args).and_return('{"foo":"bar"}')
42
+ end
43
+
44
+ it 'wraps the response in a Response object' do
45
+ expect(api.send(endpoint, params)).to be_a(Response)
46
+ end
47
+ end
48
+
49
+ context 'get requests' do
50
+ let(:verb) { :get }
51
+
52
+ describe '#diff' do
53
+ let(:path) { 'git' }
54
+ let(:method) { :diff }
55
+ it_behaves_like 'an api endpoint'
56
+ end
57
+
58
+ describe '#show' do
59
+ let(:path) { 'git' }
60
+ let(:method) { :show }
61
+ it_behaves_like 'an api endpoint'
62
+ end
63
+
64
+ describe '#status' do
65
+ let(:path) { 'git' }
66
+ let(:method) { :status }
67
+ it_behaves_like 'an api endpoint'
68
+ end
69
+
70
+ describe '#commit' do
71
+ let(:path) { 'git' }
72
+ let(:method) { :commit }
73
+ it_behaves_like 'an api endpoint'
74
+ end
75
+
76
+ describe '#snapshot' do
77
+ let(:path) { 'git' }
78
+ let(:method) { :snapshot }
79
+ it_behaves_like 'an api endpoint'
80
+ end
81
+
82
+ describe '#repo_snapshot' do
83
+ let(:path) { 'git' }
84
+ let(:method) { :repo_snapshot }
85
+ it_behaves_like 'an api endpoint'
86
+ end
87
+
88
+ describe '#add_or_update_translation' do
89
+ let(:path) { 'translations' }
90
+ let(:method) { :add_or_update_translation }
91
+ let(:endpoint_override) { 'add_or_update' }
92
+ let(:verb) { :post }
93
+ it_behaves_like 'an api endpoint'
94
+ end
95
+ end
96
+ end
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,33 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ include Rosette::Client
6
+
7
+ describe Cli do
8
+ let(:api) { double }
9
+ let(:base_repo) { TmpRepo.new }
10
+ let(:repo) { Repo.new(base_repo.working_dir) }
11
+ let(:terminal) { FakeTerminal.new }
12
+ let(:cli) { Cli.new(terminal, api, repo) }
13
+
14
+ before(:each) do
15
+ add_user_to(base_repo)
16
+ end
17
+
18
+ describe '#start' do
19
+ it 'resolves the command into a class and executes it' do
20
+ base_repo.create_file('file.txt') { |f| f.write('hello, world') }
21
+ base_repo.add_all
22
+ base_repo.commit('Initial commit')
23
+
24
+ expect(api).to receive(:commit).and_return(Response.new({ 'foo' => 'bar' }))
25
+ cli.start(['commit', base_repo.git('rev-parse HEAD').strip])
26
+ end
27
+
28
+ it "prints a message if the command isn't recognized" do
29
+ cli.start(['foobar'])
30
+ expect(terminal).to have_said("Command 'foobar' not recognized.")
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ include Rosette::Client
6
+ include Rosette::Client::Commands
7
+
8
+ describe CommitCommand do
9
+ let(:api) { double }
10
+ let(:base_repo) { TmpRepo.new }
11
+ let(:repo) { Repo.new(base_repo.working_dir) }
12
+ let(:terminal) { FakeTerminal.new }
13
+ let(:repo_name) { 'my_awesome_repo' }
14
+ let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
15
+ let(:command) { CommitCommand.new(api, terminal, repo, [commit_id]) }
16
+
17
+ before(:each) do
18
+ add_user_to(base_repo)
19
+ base_repo.git("remote add origin git@github.com/camertron/#{repo_name}")
20
+ base_repo.create_file('file.txt') { |f| f.write('hello, world') }
21
+ base_repo.add_all
22
+ base_repo.commit('Initial commit')
23
+ end
24
+
25
+ around(:each) do |example|
26
+ Dir.chdir(base_repo.working_dir) do
27
+ example.run
28
+ end
29
+ end
30
+
31
+ describe '#execute' do
32
+ it 'makes a commit api call' do
33
+ expect(api).to receive(:commit)
34
+ .with(repo_name: repo_name, ref: commit_id)
35
+ .and_return(Response.new({}))
36
+
37
+ command.execute
38
+ end
39
+
40
+ it 'prints the number of phrases added, removed, and modified' do
41
+ expect(api).to receive(:commit)
42
+ .with(repo_name: repo_name, ref: commit_id)
43
+ .and_return(
44
+ Response.new(
45
+ { 'added' => 1, 'removed' => 2, 'modified' => 3 }
46
+ )
47
+ )
48
+
49
+ command.execute
50
+
51
+ expect(terminal).to have_said('Added: 1')
52
+ expect(terminal).to have_said('Removed: 2')
53
+ expect(terminal).to have_said('Modified: 3')
54
+ end
55
+
56
+ it 'prints the error if the api response contains one' do
57
+ expect(api).to receive(:commit)
58
+ .with(repo_name: repo_name, ref: commit_id)
59
+ .and_return(
60
+ Response.new({ 'error' => 'Jelly beans', 'added' => 1 })
61
+ )
62
+
63
+ command.execute
64
+
65
+ expect(terminal).to have_said('Jelly beans')
66
+ expect(terminal).to_not have_said('Added: 1')
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,151 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ include Rosette::Client
6
+ include Rosette::Client::Commands
7
+
8
+ describe 'diff' do
9
+ let(:args) { DiffCommandArgs }
10
+ let(:base_repo) { TmpRepo.new }
11
+ let(:repo) { Repo.new(base_repo.working_dir) }
12
+ let(:repo_name) { 'my_awesome_repo' }
13
+ let(:new_branch) { 'new_branch' }
14
+ let(:new_branch_commit_id) { base_repo.git("rev-parse #{new_branch}").strip }
15
+ let(:master_commit_id) { base_repo.git("rev-parse master").strip }
16
+
17
+ before(:each) do
18
+ add_user_to(base_repo)
19
+ base_repo.git("remote add origin git@github.com/camertron/#{repo_name}")
20
+ base_repo.create_file('file.txt') { |f| f.write('hello, world') }
21
+ base_repo.add_all
22
+ base_repo.commit('Initial commit')
23
+ base_repo.create_branch(new_branch)
24
+ base_repo.create_file('file2.txt') { |f| f.write('another file') }
25
+ base_repo.add_all
26
+ base_repo.commit('Second file')
27
+ end
28
+
29
+ around(:each) do |example|
30
+ Dir.chdir(base_repo.working_dir) do
31
+ example.run
32
+ end
33
+ end
34
+
35
+ describe DiffCommandArgs do
36
+ it "uses the default head if one isn't provided" do
37
+ args.from_argv(['master'], repo).tap do |args|
38
+ expect(args.diff_point_ref).to eq(master_commit_id)
39
+ expect(args.head_ref).to eq(new_branch_commit_id)
40
+ expect(args.paths).to eq([])
41
+ end
42
+ end
43
+
44
+ it 'accepts both head and diff point as the first two arguments' do
45
+ args.from_argv(['master', new_branch], repo).tap do |args|
46
+ expect(args.diff_point_ref).to eq(master_commit_id)
47
+ expect(args.head_ref).to eq(new_branch)
48
+ expect(args.paths).to eq([])
49
+ end
50
+ end
51
+
52
+ it 'allows the second argument to be a path' do
53
+ args.from_argv(['master', '.'], repo).tap do |args|
54
+ expect(args.diff_point_ref).to eq(master_commit_id)
55
+ expect(args.head_ref).to eq(new_branch_commit_id)
56
+ expect(args.paths).to eq(['.'])
57
+ end
58
+ end
59
+
60
+ it 'allows multiple paths' do
61
+ args.from_argv(['master', 'file.txt', 'file2.txt'], repo).tap do |args|
62
+ expect(args.diff_point_ref).to eq(master_commit_id)
63
+ expect(args.head_ref).to eq(new_branch_commit_id)
64
+ expect(args.paths).to eq(['file.txt', 'file2.txt'])
65
+ end
66
+ end
67
+
68
+ it 'allows paths to be separated by --' do
69
+ args.from_argv(['master', '--', 'file.txt', 'file2.txt'], repo).tap do |args|
70
+ expect(args.diff_point_ref).to eq(master_commit_id)
71
+ expect(args.head_ref).to eq(new_branch_commit_id)
72
+ expect(args.paths).to eq(['file.txt', 'file2.txt'])
73
+ end
74
+ end
75
+
76
+ it 'allows a head and diff point followed by -- and paths' do
77
+ args.from_argv(['master', new_branch, '--', 'file.txt', 'file2.txt'], repo).tap do |args|
78
+ expect(args.diff_point_ref).to eq(master_commit_id)
79
+ expect(args.head_ref).to eq(new_branch)
80
+ expect(args.paths).to eq(['file.txt', 'file2.txt'])
81
+ end
82
+ end
83
+ end
84
+
85
+ describe DiffCommand do
86
+ let(:api) { double }
87
+ let(:command) { DiffCommand.new(api, terminal, repo, [master_commit_id, new_branch_commit_id]) }
88
+ let(:terminal) { FakeTerminal.new }
89
+
90
+ describe '#execute' do
91
+ it 'makes a diff api call' do
92
+ expect(api).to receive(:diff)
93
+ .with(
94
+ repo_name: repo_name,
95
+ head_ref: new_branch_commit_id,
96
+ diff_point_ref: master_commit_id,
97
+ paths: ''
98
+ )
99
+ .and_return(Response.new({}))
100
+
101
+ command.execute
102
+ end
103
+
104
+ it 'prints the number of phrases added, removed, and modified' do
105
+ expect(api).to receive(:diff)
106
+ .with(
107
+ repo_name: repo_name,
108
+ head_ref: new_branch_commit_id,
109
+ diff_point_ref: master_commit_id,
110
+ paths: ''
111
+ )
112
+ .and_return(
113
+ Response.new(
114
+ sample_diff(new_branch_commit_id)
115
+ )
116
+ )
117
+
118
+ command.execute
119
+
120
+ expect(terminal).to have_said("+ I'm a little teapot (about.training.teapot)", :green)
121
+ expect(terminal).to have_said('- The green albatross flitters in the moonlight (animals.birds.albatross.message)', :red)
122
+
123
+ expect(terminal).to have_said('+ Purple eggplants make delicious afternoon snacks (foods.vegetables.eggplant.snack_message)', :green)
124
+ expect(terminal).to have_said('- Blue eggplants make wonderful evening meals (foods.vegetables.eggplant.snack_message)', :red)
125
+
126
+ expect(terminal).to have_said('+ The Seattle Seahawks rock (sports.teams.football.best)', :green)
127
+ expect(terminal).to have_said('- The Seattle Seahawks rule (sports.teams.football.best)', :red)
128
+ end
129
+
130
+ it 'prints the error if the api response contains one' do
131
+ expect(api).to receive(:diff)
132
+ .with(
133
+ repo_name: repo_name,
134
+ head_ref: new_branch_commit_id,
135
+ diff_point_ref: master_commit_id,
136
+ paths: ''
137
+ )
138
+ .and_return(
139
+ Response.new({ 'error' => 'Jelly beans' }.merge(
140
+ sample_diff(new_branch_commit_id))
141
+ )
142
+ )
143
+
144
+ command.execute
145
+
146
+ expect(terminal).to have_said('Jelly beans')
147
+ expect(terminal).to_not have_said("+ I'm a little teapot (about.training.teapot)", :green)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,71 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ include Rosette::Client
6
+ include Rosette::Client::Commands
7
+
8
+ describe RepoSnapshotCommand do
9
+ let(:api) { double }
10
+ let(:base_repo) { TmpRepo.new }
11
+ let(:repo) { Repo.new(base_repo.working_dir) }
12
+ let(:terminal) { FakeTerminal.new }
13
+ let(:repo_name) { 'my_awesome_repo' }
14
+ let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
15
+ let(:command) { RepoSnapshotCommand.new(api, terminal, repo, [commit_id]) }
16
+
17
+ before(:each) do
18
+ add_user_to(base_repo)
19
+ base_repo.git("remote add origin git@github.com/camertron/#{repo_name}")
20
+ base_repo.create_file('file.txt') { |f| f.write('hello, world') }
21
+ base_repo.add_all
22
+ base_repo.commit('Initial commit')
23
+ end
24
+
25
+ around(:each) do |example|
26
+ Dir.chdir(base_repo.working_dir) do
27
+ example.run
28
+ end
29
+ end
30
+
31
+ describe '#execute' do
32
+ it 'makes a repo_snapshot api call' do
33
+ expect(api).to receive(:repo_snapshot)
34
+ .with(repo_name: repo_name, ref: commit_id)
35
+ .and_return(Response.new({}))
36
+
37
+ command.execute
38
+ end
39
+
40
+ it 'prints the hash of files to commit ids' do
41
+ expect(api).to receive(:repo_snapshot)
42
+ .with(repo_name: repo_name, ref: commit_id)
43
+ .and_return(
44
+ Response.new({
45
+ 'file1.txt' => 'abc123',
46
+ 'path/file2.txt' => 'def456',
47
+ 'my/awesome/file3.rb' => 'ghi789'
48
+ })
49
+ )
50
+
51
+ command.execute
52
+
53
+ expect(terminal).to have_said('file1.txt: abc123')
54
+ expect(terminal).to have_said('path/file2.txt: def456')
55
+ expect(terminal).to have_said('my/awesome/file3.rb: ghi789')
56
+ end
57
+
58
+ it 'prints the error if the api response contains one' do
59
+ expect(api).to receive(:repo_snapshot)
60
+ .with(repo_name: repo_name, ref: commit_id)
61
+ .and_return(
62
+ Response.new({ 'error' => 'Jelly beans', 'file1.txt' => 'abc123' })
63
+ )
64
+
65
+ command.execute
66
+
67
+ expect(terminal).to have_said('Jelly beans')
68
+ expect(terminal).to_not have_said('file1.txt: abc123')
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ include Rosette::Client
6
+ include Rosette::Client::Commands
7
+
8
+ describe ShowCommand do
9
+ let(:api) { double }
10
+ let(:base_repo) { TmpRepo.new }
11
+ let(:repo) { Repo.new(base_repo.working_dir) }
12
+ let(:terminal) { FakeTerminal.new }
13
+ let(:repo_name) { 'my_awesome_repo' }
14
+ let(:commit_id) { base_repo.git('rev-parse HEAD').strip }
15
+ let(:command) { ShowCommand.new(api, terminal, repo, [commit_id]) }
16
+
17
+ before(:each) do
18
+ add_user_to(base_repo)
19
+ base_repo.git("remote add origin git@github.com/camertron/#{repo_name}")
20
+ base_repo.create_file('file.txt') { |f| f.write('hello, world') }
21
+ base_repo.add_all
22
+ base_repo.commit('Initial commit')
23
+ end
24
+
25
+ around(:each) do |example|
26
+ Dir.chdir(base_repo.working_dir) do
27
+ example.run
28
+ end
29
+ end
30
+
31
+ describe '#execute' do
32
+ it 'makes a commit api call' do
33
+ expect(api).to receive(:show)
34
+ .with(repo_name: repo_name, ref: commit_id)
35
+ .and_return(Response.new({}))
36
+
37
+ command.execute
38
+ end
39
+
40
+ it 'prints the phrases that were added, removed, and modified' do
41
+ expect(api).to receive(:show)
42
+ .with(repo_name: repo_name, ref: commit_id)
43
+ .and_return(
44
+ Response.new(sample_diff(commit_id))
45
+ )
46
+
47
+ command.execute
48
+
49
+ expect(terminal).to have_said("+ I'm a little teapot (about.training.teapot)", :green)
50
+ expect(terminal).to have_said('- The green albatross flitters in the moonlight (animals.birds.albatross.message)', :red)
51
+
52
+ expect(terminal).to have_said('+ Purple eggplants make delicious afternoon snacks (foods.vegetables.eggplant.snack_message)', :green)
53
+ expect(terminal).to have_said('- Blue eggplants make wonderful evening meals (foods.vegetables.eggplant.snack_message)', :red)
54
+
55
+ expect(terminal).to have_said('+ The Seattle Seahawks rock (sports.teams.football.best)', :green)
56
+ expect(terminal).to have_said('- The Seattle Seahawks rule (sports.teams.football.best)', :red)
57
+ end
58
+
59
+ it 'prints the error if the api response contains one' do
60
+ expect(api).to receive(:show)
61
+ .with(repo_name: repo_name, ref: commit_id)
62
+ .and_return(
63
+ Response.new({ 'error' => 'Jelly beans' }.merge(sample_diff(commit_id)))
64
+ )
65
+
66
+ command.execute
67
+
68
+ expect(terminal).to have_said('Jelly beans')
69
+ expect(terminal).to_not have_said("+ I'm a little teapot (about.training.teapot)", :green)
70
+ end
71
+ end
72
+ end