relish 0.0.5 → 0.0.6

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.
@@ -1,7 +1,4 @@
1
1
  $:.unshift(File.dirname(__FILE__) + '/../../lib')
2
2
  require 'relish/command'
3
3
 
4
- Relish::Command::Base.class_eval do
5
- remove_const(:GLOBAL_OPTIONS_FILE)
6
- const_set(:GLOBAL_OPTIONS_FILE, '~/.relish')
7
- end
4
+ Relish.global_options_file = '~/.relish'
@@ -1,3 +1,4 @@
1
+ require 'relish'
1
2
  require 'relish/commands/base'
2
3
  require 'relish/commands/push'
3
4
  require 'relish/commands/config'
@@ -1,54 +1,72 @@
1
1
  require 'yaml'
2
+ require 'relish/ui'
3
+ require 'relish/options_file'
4
+ require 'relish/commands/dsl'
2
5
 
3
6
  module Relish
4
7
  module Command
5
8
  class Base
6
- DEFAULT_HOST = 'relishapp.com'
7
- GLOBAL_OPTIONS_FILE = File.join(File.expand_path('~'), '.relish')
8
- LOCAL_OPTIONS_FILE = '.relish'
9
-
10
- attr_accessor :args
9
+ extend Dsl
11
10
 
11
+ attr_writer :args
12
+ attr_reader :cli_options
13
+
12
14
  def initialize(args = [])
13
- @args = args
15
+ @args = clean_args(args)
14
16
  @param = get_param
15
- @options = get_options
17
+ @cli_options = Hash[*@args]
16
18
  end
17
19
 
18
- def organization
19
- @options['--organization'] || @options['-o'] || parsed_options_file['organization']
20
+ def url
21
+ "http://#{host}/api"
20
22
  end
21
23
 
22
- def project
23
- @options['--project'] || @options['-p'] || parsed_options_file['project']
24
+ def get_param
25
+ @args.shift if @args.size.odd?
24
26
  end
27
+
28
+ private
25
29
 
26
- def url
27
- "http://#{@options['--host'] || DEFAULT_HOST}/api"
28
- end
30
+ option :organization
31
+ option :project
32
+ option :api_token, :default => lambda { get_and_store_api_token }
33
+ option :host, :default => lambda { Relish.default_host }
29
34
 
30
- def resource
31
- RestClient::Resource.new(url)
35
+ def get_and_store_api_token
36
+ api_token = get_api_token
37
+ global_options_file.store('api_token' => api_token)
38
+ api_token
32
39
  end
33
40
 
34
- def api_token
35
- parsed_options_file['api_token']
41
+ def get_api_token
42
+ email, password = ui.get_credentials
43
+
44
+ raw_response = resource(:user => email, :password => password)['token'].get
45
+ String.new(raw_response.to_s)
36
46
  end
37
47
 
38
- def get_param
39
- args.shift if args.size.odd?
48
+ def resource(options = {})
49
+ RestClient::Resource.new(url, options)
50
+ end
51
+
52
+ def clean_args(args)
53
+ cleaned = []
54
+ args.each do |arg|
55
+ cleaned << arg.sub('--', '')
56
+ end
57
+ cleaned
40
58
  end
41
59
 
42
- def get_options
43
- parsed_options_file.merge(Hash[*args])
60
+ def global_options_file
61
+ @global_options ||= OptionsFile.new(Relish.global_options_file)
62
+ end
63
+
64
+ def local_options_file
65
+ @local_options ||= OptionsFile.new(Relish.local_options_file)
44
66
  end
45
67
 
46
- def parsed_options_file
47
- @parsed_options_file ||= {}.tap do |parsed_options|
48
- [GLOBAL_OPTIONS_FILE, LOCAL_OPTIONS_FILE].each do |options_file|
49
- parsed_options.merge!(YAML.load_file(options_file)) if File.exist?(options_file)
50
- end
51
- end
68
+ def ui
69
+ @ui ||= Ui.new
52
70
  end
53
71
 
54
72
  end
@@ -7,13 +7,19 @@ module Relish
7
7
  end
8
8
 
9
9
  def show
10
- puts(if File.exists?(LOCAL_OPTIONS_FILE)
11
- IO.read(LOCAL_OPTIONS_FILE)
10
+ puts(if File.exists?(Relish.local_options_file)
11
+ IO.read(Relish.local_options_file)
12
12
  else
13
- "No #{LOCAL_OPTIONS_FILE} file exists"
13
+ "No #{Relish.local_options_file} file exists"
14
14
  end)
15
15
  end
16
16
 
17
+ def add
18
+ File.open(Relish.local_options_file, 'a') do |f|
19
+ f.write(YAML::dump(Hash[*@args]))
20
+ end
21
+ end
22
+
17
23
  end
18
24
  end
19
25
  end
@@ -0,0 +1,17 @@
1
+ module Relish
2
+ module Command
3
+ module Dsl
4
+
5
+ def option(name, options = {})
6
+ default_proc = options[:default] || lambda {}
7
+ define_method(name) do
8
+ cli_options[name.to_s] ||
9
+ local_options_file[name.to_s] ||
10
+ global_options_file[name.to_s] ||
11
+ instance_exec(&default_proc)
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -5,20 +5,33 @@ module Relish
5
5
  module Command
6
6
  class Projects < Base
7
7
 
8
- def default
9
- list
10
- end
8
+ def default; list end
11
9
 
12
10
  def list
13
11
  response = resource['projects'].get(
14
12
  :params => {:api_token => api_token}, :accept => :json
15
13
  )
16
14
  puts format(response)
15
+ rescue RestClient::Exception => exception
16
+ warn exception.response
17
+ exit exception.http_code
18
+ end
19
+
20
+ def add
21
+ puts resource['projects'].post(:api_token => api_token, :handle => @param)
22
+ rescue RestClient::Exception => exception
23
+ warn exception.response
24
+ exit 1
25
+ end
26
+
27
+ def remove
28
+ puts resource["projects/#{@param}?api_token=#{api_token}"].delete
17
29
  rescue RestClient::Exception => exception
18
30
  warn exception.response
19
31
  exit 1
20
32
  end
21
33
 
34
+ private
22
35
  def format(response)
23
36
  json = JSON.parse(response)
24
37
  json.map do |hash|
@@ -11,16 +11,17 @@ module Relish
11
11
 
12
12
  class Push < Base
13
13
 
14
- def default
15
- run
16
- end
14
+ option :version
15
+
16
+ def default; run end
17
17
 
18
18
  def run
19
19
  post files_as_tar_gz
20
20
  end
21
21
 
22
22
  def post(tar_gz_data)
23
- resource[parameters].post(tar_gz_data, :content_type => 'application/x-gzip')
23
+ resource["pushes?#{parameters}"].post(tar_gz_data,
24
+ :content_type => 'application/x-gzip')
24
25
  puts "sent:\n#{files.join("\n")}"
25
26
  rescue RestClient::Exception => exception
26
27
  warn exception.response
@@ -29,7 +30,6 @@ module Relish
29
30
 
30
31
  def parameters
31
32
  "".tap do |str|
32
- str << "pushes?"
33
33
  str << "creator_id=#{organization}&" if organization
34
34
  str << "project_id=#{project}&"
35
35
  str << "version_id=#{version}&" if version
@@ -37,10 +37,6 @@ module Relish
37
37
  end
38
38
  end
39
39
 
40
- def version
41
- @options['--version'] || @options['-v']
42
- end
43
-
44
40
  def files_as_tar_gz
45
41
  stream = StringIO.new
46
42
  begin
@@ -0,0 +1,45 @@
1
+ require 'relish/commands/base'
2
+ require 'yaml'
3
+
4
+ module Relish
5
+
6
+ # Represents a .relish file
7
+ class OptionsFile
8
+
9
+ def initialize(path)
10
+ @path = path
11
+ end
12
+
13
+ # Store the given options into the file. Existing options with the same keys
14
+ # will be overwritten.
15
+ def store(options)
16
+ new_options = self.options.merge(options)
17
+ FileUtils.touch(@path)
18
+ File.open(@path, 'w') do |file|
19
+ YAML.dump(new_options, file)
20
+ end
21
+ end
22
+
23
+ def [](key)
24
+ options[key]
25
+ end
26
+
27
+ def == (other)
28
+ options == other
29
+ end
30
+
31
+ # Stored options as a hash
32
+ def options
33
+ @options ||= current_options
34
+ end
35
+
36
+ private
37
+
38
+ def current_options
39
+ return {} unless File.exist?(@path)
40
+ YAML.load_file(@path)
41
+ end
42
+
43
+ end
44
+
45
+ end
data/lib/relish/ui.rb ADDED
@@ -0,0 +1,60 @@
1
+ module Relish
2
+ class Ui
3
+ def get_credentials
4
+ puts "Please enter your Relish credentials."
5
+
6
+ print "Email: "
7
+ user = ask
8
+
9
+ print "Password: "
10
+ password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
11
+
12
+ [ user, password ]
13
+ end
14
+
15
+ private
16
+
17
+ def running_on_windows?
18
+ RUBY_PLATFORM =~ /mswin32|mingw32/
19
+ end
20
+
21
+ def ask_for_password_on_windows
22
+ require "Win32API"
23
+ char = nil
24
+ password = ''
25
+
26
+ while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
27
+ break if char == 10 || char == 13 # received carriage return or newline
28
+ if char == 127 || char == 8 # backspace and delete
29
+ password.slice!(-1, 1)
30
+ else
31
+ # windows might throw a -1 at us so make sure to handle RangeError
32
+ (password << char.chr) rescue RangeError
33
+ end
34
+ end
35
+ puts
36
+ return password
37
+ end
38
+
39
+ def ask_for_password
40
+ echo_off
41
+ password = ask
42
+ puts
43
+ echo_on
44
+ return password
45
+ end
46
+
47
+ def echo_off
48
+ system "stty -echo"
49
+ end
50
+
51
+ def echo_on
52
+ system "stty echo"
53
+ end
54
+
55
+ def ask
56
+ gets.strip
57
+ end
58
+
59
+ end
60
+ end
data/lib/relish.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Relish
2
+ class << self
3
+
4
+ def self.setting(name, value)
5
+ attr_writer name
6
+ class_eval %{
7
+ def #{name} # def global_options_file
8
+ @#{name.to_s} ||= # @global_options_file ||=
9
+ ENV['RELISH_#{name.to_s.upcase}'] || # ENV['RELISH_GLOBAL_OPTIONS_FILE'] ||
10
+ '#{value}' # '~/.relish'
11
+ end # end
12
+ }
13
+ end
14
+
15
+ setting :global_options_file, File.join(File.expand_path('~'), '.relish')
16
+ setting :local_options_file, '.relish'
17
+ setting :default_host, 'relishapp.com'
18
+ end
19
+ end
data/relish.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "relish"
3
- s.version = "0.0.5"
3
+ s.version = "0.0.6"
4
4
 
5
5
  s.required_rubygems_version = '>= 1.3.5'
6
6
  s.authors = ["Matt Wynne", "Justin Ko"]
@@ -6,7 +6,7 @@ module Relish
6
6
 
7
7
  {:organization => 'rspec', :project => 'rspec-core'}.each do |meth, name|
8
8
  describe "##{meth}" do
9
- context 'passed in command line as full arg' do
9
+ context 'passed in command line' do
10
10
  let(:base) { described_class.new(["--#{meth}", name]) }
11
11
 
12
12
  it 'returns the value' do
@@ -14,20 +14,11 @@ module Relish
14
14
  end
15
15
  end
16
16
 
17
- context 'passed in command line as short arg' do
18
- let(:short_arg) { meth.to_s[0,1] }
19
- let(:base) { described_class.new(["-#{short_arg}", name]) }
20
-
21
- it 'returns the value' do
22
- base.send(meth).should eq(name)
23
- end
24
- end
25
-
26
- context 'contained in the options file' do
17
+ context 'contained in the local options file' do
27
18
  let(:base) { described_class.new }
28
19
 
29
20
  before do
30
- base.stub(:parsed_options_file).and_return({meth.to_s => name})
21
+ OptionsFile.stub(:new).with(Relish.local_options_file).and_return({meth.to_s => name})
31
22
  end
32
23
 
33
24
  it 'returns the value' do
@@ -57,33 +48,60 @@ module Relish
57
48
  let(:base) { described_class.new }
58
49
 
59
50
  it 'returns the default host' do
60
- base.url.should eq("http://#{Base::DEFAULT_HOST}/api")
51
+ base.url.should eq("http://#{Relish.default_host}/api")
61
52
  end
62
53
  end
63
54
  end
64
55
 
65
- describe '#resource' do
66
- let(:base) { described_class.new }
67
-
68
- before do
69
- base.should_receive(:url).and_return('url')
70
- RestClient::Resource.should_receive(:new).with('url')
71
- end
72
-
73
- specify { base.resource }
74
- end
75
-
76
56
  describe '#api_token' do
77
57
  let(:base) { described_class.new }
78
- let(:options) { {'api_token' => '12345'} }
79
-
58
+ let(:ui) { double(Ui) }
59
+
80
60
  before do
81
- base.should_receive(:parsed_options_file).and_return(options)
61
+ Ui.stub(:new).and_return(ui)
62
+ OptionsFile.stub(:new => options)
82
63
  end
83
64
 
84
- it 'parses the api token' do
85
- base.api_token.should eq('12345')
65
+ context "when the token is stored locally" do
66
+ let(:options) { {'api_token' => '12345'} }
67
+
68
+ it 'parses the api token' do
69
+ base.api_token.should eq('12345')
70
+ end
71
+ end
72
+
73
+ context "when the token is not stored locally" do
74
+ let(:options) { {} }
75
+ let(:api_token) { 'abasfawer23123' }
76
+ let(:credentials) { ['testuser', 'testpassword'] }
77
+
78
+ let(:global_options) do
79
+ double = double(OptionsFile, :[] => nil)
80
+ OptionsFile.stub(:new => double)
81
+ double
82
+ end
83
+
84
+ def api_endpoint(name, credentials)
85
+ user, password = *credentials
86
+ endpoint = double
87
+
88
+ RestClient::Resource.stub(:new).
89
+ with(anything, :user => user, :password => password).
90
+ and_return name => endpoint
91
+
92
+ endpoint
93
+ end
94
+
95
+ it "asks the user for their credentials and send them to the server" do
96
+ ui.should_receive(:get_credentials).and_return(credentials)
97
+ api_endpoint('token', credentials).should_receive(:get).and_return(api_token)
98
+ global_options.should_receive(:store).with 'api_token' => api_token
99
+
100
+ base.api_token
101
+ end
102
+
86
103
  end
104
+
87
105
  end
88
106
 
89
107
  describe '#get_param' do
@@ -113,54 +131,6 @@ module Relish
113
131
  end
114
132
  end
115
133
 
116
- describe '#get_options' do
117
- let(:options) { {'project' => 'rspec-core'} }
118
- let(:base) { described_class.new(['--project', 'rspec-core']) }
119
-
120
- before do
121
- base.should_receive(:parsed_options_file).and_return(options)
122
- end
123
-
124
- it 'combines the args and options file' do
125
- base.get_options.should eq(
126
- {'project' => 'rspec-core', '--project' => 'rspec-core'}
127
- )
128
- end
129
- end
130
-
131
- describe '#parsed_options_file' do
132
- let(:base) { described_class.new }
133
-
134
- context 'with options file that exists' do
135
- let(:options) do
136
- {'organization' => 'rspec', 'project' => 'rspec-core'}
137
- end
138
-
139
- before do
140
- File.should_receive(:exist?).twice.and_return(true)
141
- YAML.should_receive(:load_file).twice.and_return(options)
142
- end
143
-
144
- it 'parses the organization' do
145
- base.parsed_options_file['organization'].should eq('rspec')
146
- end
147
-
148
- it 'parses the project' do
149
- base.parsed_options_file['project'].should eq('rspec-core')
150
- end
151
- end
152
-
153
- context 'with options file that does not exist' do
154
- before do
155
- File.stub(:exist?).and_return(false)
156
- end
157
-
158
- it 'returns an empty hash' do
159
- base.parsed_options_file.should eq({})
160
- end
161
- end
162
- end
163
-
164
134
  end
165
135
  end
166
136
  end
@@ -11,13 +11,7 @@ module Relish
11
11
  projects.should_receive(:list)
12
12
  projects.default
13
13
  end
14
- end
15
-
16
- # describe '#url' do
17
- # context 'given a name' do
18
- # let(:projects) { described_class.new(['rspec-core']) }
19
-
20
-
14
+ end
21
15
 
22
16
  end
23
17
  end
@@ -24,7 +24,7 @@ module Relish
24
24
 
25
25
  specify do
26
26
  push.parameters.should eq(
27
- "pushes?project_id=rspec&api_token=abc"
27
+ "project_id=rspec&api_token=abc"
28
28
  )
29
29
  end
30
30
  end
@@ -34,7 +34,7 @@ module Relish
34
34
 
35
35
  specify do
36
36
  push.parameters.should eq(
37
- "pushes?project_id=rspec&version_id=one&api_token=abc"
37
+ "project_id=rspec&version_id=one&api_token=abc"
38
38
  )
39
39
  end
40
40
  end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ module Relish
4
+ describe OptionsFile do
5
+ let(:path) { Dir.tmpdir + '/.relish' }
6
+ let(:global_options) { described_class.new(path) }
7
+
8
+ after { FileUtils.rm_rf(path) }
9
+
10
+ describe '#[]' do
11
+
12
+ context 'with options file that exists' do
13
+ let(:options) do
14
+ {'organization' => 'rspec', 'project' => 'rspec-core'}
15
+ end
16
+
17
+ before do
18
+ File.open(path, 'w') { |f| YAML.dump(options, f) }
19
+ end
20
+
21
+ it 'parses the organization' do
22
+ global_options['organization'].should eq('rspec')
23
+ end
24
+
25
+ it 'parses the project' do
26
+ global_options['project'].should eq('rspec-core')
27
+ end
28
+ end
29
+
30
+ context 'with options file that does not exist' do
31
+ before do
32
+ FileUtils.rm_rf(path)
33
+ end
34
+
35
+ it 'returns an empty hash' do
36
+ global_options.should eq({})
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#store' do
42
+ context 'with options file that exists' do
43
+
44
+ let(:options) do
45
+ {'organization' => 'rspec', 'project' => 'rspec-core'}
46
+ end
47
+
48
+ before do
49
+ File.open(path, 'w') { |f| YAML.dump(options, f) }
50
+ global_options.store('organization' => 'relish')
51
+ end
52
+
53
+ it "over-writes existing values" do
54
+ OptionsFile.new(path).options['organization'].should == 'relish'
55
+ end
56
+
57
+ it 'leaves existing options alone' do
58
+ OptionsFile.new(path).options['project'].should == 'rspec-core'
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Relish do
4
+ it "has a .global_options_file setting" do
5
+ Relish.global_options_file.should_not be_nil
6
+ end
7
+
8
+ it "allows global_options_file to be overwritten" do
9
+ Relish.global_options_file = 'foo'
10
+ Relish.global_options_file.should == 'foo'
11
+ end
12
+ end
data/spec/spec_helper.rb CHANGED
@@ -6,13 +6,5 @@ require 'relish/command'
6
6
 
7
7
  RSpec.configure do |config|
8
8
  config.color_enabled = true
9
-
10
- config.before(:suite) do
11
-
12
- Relish::Command::Base.class_eval do
13
- remove_const(:GLOBAL_OPTIONS_FILE)
14
- const_set(:GLOBAL_OPTIONS_FILE, '~/.relish')
15
- end
16
-
17
- end
9
+
18
10
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relish
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 5
10
- version: 0.0.5
9
+ - 6
10
+ version: 0.0.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt Wynne
@@ -185,18 +185,24 @@ files:
185
185
  - features/step_definitions/fake_web_steps.rb
186
186
  - features/step_definitions/relish_steps.rb
187
187
  - features/support/env.rb
188
+ - lib/relish.rb
188
189
  - lib/relish/command.rb
189
190
  - lib/relish/commands/base.rb
190
191
  - lib/relish/commands/config.rb
192
+ - lib/relish/commands/dsl.rb
191
193
  - lib/relish/commands/help.rb
192
194
  - lib/relish/commands/projects.rb
193
195
  - lib/relish/commands/push.rb
196
+ - lib/relish/options_file.rb
197
+ - lib/relish/ui.rb
194
198
  - relish.gemspec
195
199
  - spec/relish/command_spec.rb
196
200
  - spec/relish/commands/base_spec.rb
197
201
  - spec/relish/commands/config_spec.rb
198
202
  - spec/relish/commands/projects_spec.rb
199
203
  - spec/relish/commands/push_spec.rb
204
+ - spec/relish/options_file_spec.rb
205
+ - spec/relish_spec.rb
200
206
  - spec/spec_helper.rb
201
207
  has_rdoc: true
202
208
  homepage: http://relishapp.com
@@ -246,4 +252,6 @@ test_files:
246
252
  - spec/relish/commands/config_spec.rb
247
253
  - spec/relish/commands/projects_spec.rb
248
254
  - spec/relish/commands/push_spec.rb
255
+ - spec/relish/options_file_spec.rb
256
+ - spec/relish_spec.rb
249
257
  - spec/spec_helper.rb