codefumes 0.1.10 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +19 -0
- data/Gemfile.lock +135 -0
- data/History.txt +12 -0
- data/LICENSE +20 -0
- data/Manifest.txt +40 -19
- data/README.txt +11 -29
- data/Rakefile +15 -10
- data/bin/fumes +214 -0
- data/config/website.yml +2 -0
- data/cucumber.yml +2 -0
- data/features/claiming_a_project.feature +46 -0
- data/features/deleting_a_project.feature +32 -0
- data/features/releasing_a_project.feature +50 -0
- data/features/step_definitions/cli_steps.rb +98 -0
- data/features/step_definitions/common_steps.rb +168 -0
- data/features/step_definitions/filesystem_steps.rb +19 -0
- data/features/storing_user_api_key.feature +41 -0
- data/features/support/common.rb +29 -0
- data/features/support/env.rb +24 -0
- data/features/support/matchers.rb +11 -0
- data/features/synchronizing_repository_with_project.feature +33 -0
- data/lib/codefumes.rb +10 -8
- data/lib/codefumes/api.rb +20 -11
- data/lib/codefumes/api/build.rb +139 -0
- data/lib/codefumes/api/claim.rb +74 -0
- data/lib/codefumes/api/commit.rb +150 -0
- data/lib/codefumes/api/payload.rb +93 -0
- data/lib/codefumes/api/project.rb +158 -0
- data/lib/codefumes/cli_helpers.rb +54 -0
- data/lib/codefumes/config_file.rb +3 -2
- data/lib/codefumes/errors.rb +21 -0
- data/lib/codefumes/exit_codes.rb +10 -0
- data/lib/codefumes/harvester.rb +113 -0
- data/lib/codefumes/quick_build.rb +43 -0
- data/lib/codefumes/quick_metric.rb +20 -0
- data/lib/codefumes/source_control.rb +137 -0
- data/lib/integrity_notifier/codefumes.haml +11 -0
- data/lib/integrity_notifier/codefumes.rb +62 -0
- data/spec/codefumes/{build_spec.rb → api/build_spec.rb} +14 -24
- data/spec/codefumes/{claim_spec.rb → api/claim_spec.rb} +42 -3
- data/spec/codefumes/{commit_spec.rb → api/commit_spec.rb} +34 -24
- data/spec/codefumes/api/payload_spec.rb +148 -0
- data/spec/codefumes/api/project_spec.rb +286 -0
- data/spec/codefumes/api_spec.rb +38 -15
- data/spec/codefumes/config_file_spec.rb +69 -13
- data/spec/codefumes/harvester_spec.rb +118 -0
- data/spec/codefumes/source_control_spec.rb +199 -0
- data/spec/codefumes_service_helpers.rb +23 -19
- data/spec/fixtures/sample_project_dirs/no_scm/description +4 -0
- data/spec/spec_helper.rb +1 -0
- data/tasks/cucumber.rake +11 -0
- metadata +145 -60
- data/bin/cf_claim_project +0 -9
- data/bin/cf_release_project +0 -10
- data/bin/cf_store_credentials +0 -10
- data/lib/cf_claim_project/cli.rb +0 -95
- data/lib/cf_release_project/cli.rb +0 -76
- data/lib/cf_store_credentials/cli.rb +0 -50
- data/lib/codefumes/build.rb +0 -131
- data/lib/codefumes/claim.rb +0 -57
- data/lib/codefumes/commit.rb +0 -144
- data/lib/codefumes/payload.rb +0 -103
- data/lib/codefumes/project.rb +0 -129
- data/spec/cf_claim_project/cli_spec.rb +0 -17
- data/spec/cf_release_project/cli_spec.rb +0 -41
- data/spec/cf_store_credentials/cli_spec.rb +0 -28
- data/spec/codefumes/payload_spec.rb +0 -155
- data/spec/codefumes/project_spec.rb +0 -274
@@ -0,0 +1,29 @@
|
|
1
|
+
module CommonHelpers
|
2
|
+
def in_tmp_folder(&block)
|
3
|
+
FileUtils.chdir(@tmp_root, &block)
|
4
|
+
end
|
5
|
+
|
6
|
+
def in_project_folder(&block)
|
7
|
+
project_folder = @active_project_folder || @tmp_root
|
8
|
+
FileUtils.chdir(project_folder, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def in_home_folder(&block)
|
12
|
+
FileUtils.chdir(@home_path, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def force_local_lib_override(project_name = @project_name)
|
16
|
+
rakefile = File.read(File.join(project_name, 'Rakefile'))
|
17
|
+
File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
|
18
|
+
f << "$:.unshift('#{@lib_path}')\n"
|
19
|
+
f << rakefile
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def setup_active_project_folder project_name
|
24
|
+
@active_project_folder = File.join(@tmp_root, project_name)
|
25
|
+
@project_name = project_name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
World(CommonHelpers)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../lib/codefumes"
|
2
|
+
|
3
|
+
gem 'cucumber'
|
4
|
+
require 'cucumber'
|
5
|
+
gem 'rspec'
|
6
|
+
require 'spec'
|
7
|
+
require 'spec/stubs/cucumber'
|
8
|
+
|
9
|
+
include CodeFumes
|
10
|
+
|
11
|
+
gem 'aruba'
|
12
|
+
require 'aruba'
|
13
|
+
|
14
|
+
Before do
|
15
|
+
@tmp_root = File.dirname(__FILE__) + "/../../tmp"
|
16
|
+
@home_path = File.expand_path(File.join(@tmp_root, "home"))
|
17
|
+
@lib_path = File.expand_path(File.dirname(__FILE__) + "/../../lib")
|
18
|
+
@bin_path = File.expand_path(File.dirname(__FILE__) + "/../../bin")
|
19
|
+
FileUtils.rm_rf @tmp_root
|
20
|
+
FileUtils.mkdir_p @home_path
|
21
|
+
ENV['HOME'] = @home_path
|
22
|
+
ENV['CODEFUMES_CONFIG_FILE'] = File.expand_path(File.join(@tmp_root, "codefumes_config_file"))
|
23
|
+
ENV['FUMES_ENV'] = ENV['FUMES_ENV'] || "test"
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Matchers
|
2
|
+
def contain(expected)
|
3
|
+
simple_matcher("contain #{expected.inspect}") do |given, matcher|
|
4
|
+
matcher.failure_message = "expected #{given.inspect} to contain #{expected.inspect}"
|
5
|
+
matcher.negative_failure_message = "expected #{given.inspect} not to contain #{expected.inspect}"
|
6
|
+
given.index expected
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
World(Matchers)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
Feature: Synchronizing a repository with CodeFumes
|
2
|
+
Keeping a CodeFumes project synchronized with a project's development
|
3
|
+
is a fundamental feature of the site/service. Synchronizing this data must
|
4
|
+
be as simple, quick, and reliable as possible in order to provide value to
|
5
|
+
the users of the site.
|
6
|
+
|
7
|
+
Scenario: Unsupported repository type
|
8
|
+
When I run "#{@bin_path}/fumes sync"
|
9
|
+
Then it should fail with:
|
10
|
+
"""
|
11
|
+
Unsupported
|
12
|
+
"""
|
13
|
+
And the exit status should be 1
|
14
|
+
|
15
|
+
Scenario: Successful synchronization
|
16
|
+
Given I have cloned 1 project
|
17
|
+
When I synchronize the project
|
18
|
+
Then the exit status should be 0
|
19
|
+
And the output should contain "Successfully saved"
|
20
|
+
And the output should contain "Visit http://"
|
21
|
+
|
22
|
+
Scenario: Providing feedback when data is being sent to a non-production server
|
23
|
+
Given I have cloned 1 project
|
24
|
+
When I synchronize the project
|
25
|
+
Then the output should contain "non-production"
|
26
|
+
And the output should contain "test.codefumes.com"
|
27
|
+
And the exit status should be 0
|
28
|
+
|
29
|
+
Scenario: Specifying a custom, but non-existant public/private key combination
|
30
|
+
Given I have cloned 1 project
|
31
|
+
And I cd to "project_1/"
|
32
|
+
When I run "#{@bin_path}/fumes sync -p non-existant-pubkey -a non-existant-privkey"
|
33
|
+
And the exit status should be 2
|
data/lib/codefumes.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
require '
|
2
|
-
require 'chronic'
|
1
|
+
require 'grit'
|
3
2
|
|
4
3
|
require 'codefumes/api'
|
5
|
-
require 'codefumes/build'
|
6
|
-
require 'codefumes/claim'
|
7
|
-
require 'codefumes/commit'
|
8
4
|
require 'codefumes/config_file'
|
9
|
-
require 'codefumes/
|
10
|
-
require 'codefumes/
|
5
|
+
require 'codefumes/errors'
|
6
|
+
require 'codefumes/exit_codes'
|
7
|
+
require 'codefumes/harvester.rb'
|
8
|
+
require 'codefumes/quick_build.rb'
|
9
|
+
require 'codefumes/quick_metric.rb'
|
10
|
+
require 'codefumes/source_control.rb'
|
11
|
+
|
12
|
+
include CodeFumes::API
|
11
13
|
|
12
14
|
module CodeFumes
|
13
|
-
VERSION = '0.
|
15
|
+
VERSION = '0.2.0' unless defined?(CodeFumes::VERSION)
|
14
16
|
end
|
data/lib/codefumes/api.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'chronic'
|
3
|
+
|
4
|
+
require 'codefumes/api/build'
|
5
|
+
require 'codefumes/api/claim'
|
6
|
+
require 'codefumes/api/commit'
|
7
|
+
require 'codefumes/api/payload'
|
8
|
+
require 'codefumes/api/project'
|
9
|
+
|
1
10
|
module CodeFumes
|
2
|
-
|
11
|
+
module API
|
3
12
|
include HTTParty
|
4
|
-
|
13
|
+
base_uri 'http://codefumes.com/api/v1/xml'
|
5
14
|
format :xml
|
6
15
|
|
7
16
|
BASE_URIS = {
|
@@ -10,16 +19,16 @@ module CodeFumes
|
|
10
19
|
:local => 'http://codefumes.com.local/api/v1/xml'
|
11
20
|
} #:nodoc:
|
12
21
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
# +:local+ is also technically supported, but provided for local
|
18
|
-
# testing and likely only useful for CodeFumes.com developers.
|
19
|
-
def self.mode(mode)
|
20
|
-
base_uri(BASE_URIS[mode]) if BASE_URIS[mode]
|
22
|
+
def self.mode=(mode)
|
23
|
+
return if mode.to_s.empty?
|
24
|
+
base_uri(BASE_URIS[mode.to_sym]) if BASE_URIS[mode.to_sym]
|
21
25
|
end
|
22
26
|
|
23
|
-
mode(
|
27
|
+
def self.mode?(mode)
|
28
|
+
return false if mode.nil?
|
29
|
+
base_uri == BASE_URIS[mode.to_sym]
|
30
|
+
end
|
24
31
|
end
|
25
32
|
end
|
33
|
+
|
34
|
+
CodeFumes::API.mode= ENV['FUMES_ENV']
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module CodeFumes
|
2
|
+
module API
|
3
|
+
# Represents a specific instance of tests running on
|
4
|
+
# a continuous integration server. Builds have a name and are
|
5
|
+
# associated with # a specific Commit of a Project and can track
|
6
|
+
# the current status (running, failed, success) and the
|
7
|
+
# start & end times of the Build process.
|
8
|
+
class Build
|
9
|
+
attr_reader :created_at, :api_uri, :identifier, :commit, :project
|
10
|
+
attr_accessor :started_at, :ended_at, :state, :name
|
11
|
+
|
12
|
+
# Initializes new instance of a Build.
|
13
|
+
#
|
14
|
+
# * commit - Instance of CodeFumes::Commit to associate the Build with
|
15
|
+
# * name - A name for the build ('ie7', 'specs', etc.)
|
16
|
+
# * state - Current state of the build (defaults: 'running')
|
17
|
+
# * valid values: 'running', 'failed', 'successful'
|
18
|
+
# * options - Hash of additional options. Accepts the following:
|
19
|
+
# * :started_at - Time the build started
|
20
|
+
# * :ended_at - Time the build completed (defaults to nil)
|
21
|
+
def initialize(commit, name, state = 'running', options = {})
|
22
|
+
@commit = commit
|
23
|
+
@project = commit.project
|
24
|
+
@name = name
|
25
|
+
@state = state.to_s
|
26
|
+
@started_at = options[:started_at] || options['started_at'] || Time.now
|
27
|
+
@ended_at = options[:ended_at] || options['ended_at']
|
28
|
+
end
|
29
|
+
|
30
|
+
# Overrides existing attributes with those supplied in +options+. This
|
31
|
+
# simplifies the process of updating an object's state when given a response
|
32
|
+
# from the CodeFumes API.
|
33
|
+
#
|
34
|
+
# Valid options are:
|
35
|
+
# * identifier
|
36
|
+
# * name
|
37
|
+
# * state
|
38
|
+
# * started_at
|
39
|
+
# * ended_at
|
40
|
+
# * api_uri
|
41
|
+
#
|
42
|
+
# Returns +self+
|
43
|
+
def reinitialize_from_hash!(options = {})
|
44
|
+
@identifier = options[:identifier] || options['identifier']
|
45
|
+
@name = options[:name] || options['name']
|
46
|
+
@state = options[:state] || options['state']
|
47
|
+
@api_uri = options[:api_uri] || options['api_uri']
|
48
|
+
@started_at = options[:started_at] || options['started_at']
|
49
|
+
@ended_at = options[:ended_at] || options['ended_at']
|
50
|
+
@created_at = options[:created_at] || options['created_at']
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
# Saves the Build instance to CodeFumes.com
|
55
|
+
#
|
56
|
+
# Returns +true+ if successful
|
57
|
+
#
|
58
|
+
# Returns +false+ if request fails
|
59
|
+
# ---
|
60
|
+
# TODO: Make this consistent w/ other class' create/update handling
|
61
|
+
def save
|
62
|
+
response = exists? ? update : create
|
63
|
+
|
64
|
+
case response.code
|
65
|
+
when 201,200
|
66
|
+
reinitialize_from_hash!(response['build'])
|
67
|
+
true
|
68
|
+
else
|
69
|
+
false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Searches website for Build with the supplied identifier.
|
74
|
+
#
|
75
|
+
# Returns a Build instance if the Build exists and is available,
|
76
|
+
# to the user making the request.
|
77
|
+
#
|
78
|
+
# Returns +nil+ in all other cases.
|
79
|
+
def self.find(commit, build_name)
|
80
|
+
project = commit.project
|
81
|
+
uri = "/projects/#{project.public_key}/commits/#{commit.identifier}/builds/#{build_name}"
|
82
|
+
|
83
|
+
response = API.get(uri)
|
84
|
+
|
85
|
+
case response.code
|
86
|
+
when 200
|
87
|
+
build_params = response["build"] || {}
|
88
|
+
name = build_params["name"]
|
89
|
+
state = build_params["state"]
|
90
|
+
build = Build.new(commit, name, state)
|
91
|
+
build.reinitialize_from_hash!(build_params)
|
92
|
+
else
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# Returns true if the request was successful
|
99
|
+
#
|
100
|
+
# Returns +false+ in all other cases.
|
101
|
+
def destroy
|
102
|
+
uri = "/projects/#{@project.public_key}/commits/#{@commit.identifier}/builds/#{@name}"
|
103
|
+
auth_args = {:username => @project.public_key, :password => @project.private_key}
|
104
|
+
|
105
|
+
response = API.delete(uri, :basic_auth => auth_args)
|
106
|
+
|
107
|
+
case response.code
|
108
|
+
when 200 : true
|
109
|
+
else false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
# Verifies existence of Build on website.
|
115
|
+
#
|
116
|
+
# Returns +true+ if a build with the specified identifier or name is associated with
|
117
|
+
# the specified project/commit
|
118
|
+
#
|
119
|
+
# Returns +false+ if the public key of the Project is not available.
|
120
|
+
def exists?
|
121
|
+
!self.class.find(commit, name).nil?
|
122
|
+
end
|
123
|
+
|
124
|
+
# Saves a new build (makes POST request)
|
125
|
+
def create
|
126
|
+
API.post("/projects/#{project.public_key}/commits/#{commit.identifier}/builds", :query => {:build => standard_content_hash}, :basic_auth => {:username => project.public_key, :password => project.private_key})
|
127
|
+
end
|
128
|
+
|
129
|
+
# Updates an existing build (makes PUT request)
|
130
|
+
def update
|
131
|
+
API.put("/projects/#{project.public_key}/commits/#{commit.identifier}/builds/#{name}", :query => {:build => standard_content_hash}, :basic_auth => {:username => project.public_key, :password => project.private_key})
|
132
|
+
end
|
133
|
+
|
134
|
+
def standard_content_hash
|
135
|
+
{:name => name,:started_at => started_at, :ended_at => ended_at, :state => state}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module CodeFumes
|
2
|
+
module API
|
3
|
+
class Claim
|
4
|
+
attr_accessor :created_at
|
5
|
+
SUPPORTED_VISIBILITIES = [:public, :private]
|
6
|
+
|
7
|
+
# Attempts to claim the specified Project instance using the
|
8
|
+
# supplied API key.
|
9
|
+
#
|
10
|
+
# +visibility+ defaults to +:public+. Valid options are +public+
|
11
|
+
# and +private+.
|
12
|
+
#
|
13
|
+
# Similar to Project#claim, but more explicit.
|
14
|
+
#
|
15
|
+
# Returns +true+ if the request is successful, or if the project
|
16
|
+
# was already owned by the user associated with the privided API
|
17
|
+
# key.
|
18
|
+
#
|
19
|
+
# Returns +false+ in all other cases.
|
20
|
+
def self.create(project, api_key, visibility = :public)
|
21
|
+
validate_api_key(api_key)
|
22
|
+
validate_visibility(visibility)
|
23
|
+
|
24
|
+
auth_args = {:username => project.public_key, :password => project.private_key}
|
25
|
+
|
26
|
+
uri = "/projects/#{project.public_key}/claim"
|
27
|
+
response = API.put(uri, :query => {:api_key => api_key, :visibility => visibility}, :basic_auth => auth_args)
|
28
|
+
|
29
|
+
case response.code
|
30
|
+
when 200 : true
|
31
|
+
else false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Removes a claim on the specified Project instance using the
|
36
|
+
# supplied API key, releasing ownership. If the project was a
|
37
|
+
# "private" project, this method will convert it to "public".
|
38
|
+
#
|
39
|
+
# Returns true if the request was successful or there was not
|
40
|
+
# an existing owner (the action is idempotent).
|
41
|
+
#
|
42
|
+
# Returns +false+ in all other cases.
|
43
|
+
def self.destroy(project, api_key)
|
44
|
+
validate_api_key(api_key)
|
45
|
+
|
46
|
+
auth_args = {:username => project.public_key, :password => project.private_key}
|
47
|
+
|
48
|
+
uri = "/projects/#{project.public_key}/claim"
|
49
|
+
response = API.delete(uri, :query => {:api_key => api_key}, :basic_auth => auth_args)
|
50
|
+
|
51
|
+
case response.code
|
52
|
+
when 200 : true
|
53
|
+
else false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def self.validate_api_key(api_key)
|
59
|
+
if api_key.nil? || api_key.empty?
|
60
|
+
msg = "Invalid user api key provided. (provided: '#{api_key}')"
|
61
|
+
raise(Errors::NoUserApiKeyError, msg)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.validate_visibility(visibility)
|
66
|
+
unless SUPPORTED_VISIBILITIES.include?(visibility.to_sym)
|
67
|
+
msg = "Unsupported visibility supplied (#{visibility.to_s}). "
|
68
|
+
msg << "Valid options are: #{SUPPORTED_VISIBILITIES.join(', ')}"
|
69
|
+
raise ArgumentError, msg
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module CodeFumes
|
2
|
+
module API
|
3
|
+
# Similar to a revision control system, a Commit encompasses a set of
|
4
|
+
# changes to a codebase, who made them, when said changes were applied
|
5
|
+
# to the previous revision of codebase, et cetera.
|
6
|
+
#
|
7
|
+
# A Commit has a concept of 'standard attributes' which will always be
|
8
|
+
# present in a response from CodeFumes.com[http://codefumes.com], such
|
9
|
+
# as the +identifier+, +author+, and +commit_message+ (see the list of
|
10
|
+
# attributes for a comprehensive listing). In addition to this, users
|
11
|
+
# are able to associate 'custom attributes' to a Commit, allowing
|
12
|
+
# users to link any number of attributes with a commit identifier and
|
13
|
+
# easily retrieve them later.
|
14
|
+
#
|
15
|
+
# One thing to note about Commit objects is that they are read-only.
|
16
|
+
# To associate metrics with a Commit object, a Payload object should
|
17
|
+
# be created and saved. Refer to the Payload documentation for more
|
18
|
+
# information.
|
19
|
+
class Commit
|
20
|
+
attr_reader :identifier, :author_name, :author_email, :committer_name,
|
21
|
+
:committer_email, :short_message, :message,:committed_at,
|
22
|
+
:authored_at, :uploaded_at, :api_uri, :parent_identifiers,
|
23
|
+
:line_additions, :line_deletions, :line_total,
|
24
|
+
:affected_file_count, :custom_attributes, :project
|
25
|
+
alias_method :id, :identifier
|
26
|
+
alias_method :sha, :identifier
|
27
|
+
|
28
|
+
# Instantiates a new Commit object
|
29
|
+
#
|
30
|
+
# * +identifier+ - the revision number of the commit (git commit sha, svn version, etc)
|
31
|
+
#
|
32
|
+
# Accepts a Hash of options, including:
|
33
|
+
# * author_email
|
34
|
+
# * author_name
|
35
|
+
# * committer_email
|
36
|
+
# * committer_name
|
37
|
+
# * short_message
|
38
|
+
# * message
|
39
|
+
# * committed_at
|
40
|
+
# * authored_at
|
41
|
+
# * uploaded_at
|
42
|
+
# * api_uri
|
43
|
+
# * parent_identifiers
|
44
|
+
# * line_additions
|
45
|
+
# * line_deletions
|
46
|
+
# * line_total
|
47
|
+
# * affected_file_count
|
48
|
+
# * custom_attributes
|
49
|
+
#
|
50
|
+
# +custom_attributes+ should be a Hash of attribute_name/value
|
51
|
+
# pairs associated with the commit. All other attributes are
|
52
|
+
# expected to be String values, other than +committed_at+ and
|
53
|
+
# +authored_at+, which are expected to be DateTime objects.
|
54
|
+
# Technically speaking, you could pass anything you wanted into
|
55
|
+
# the fields, but when using with the CodeFumes API, the attribute
|
56
|
+
# values will be of the type String, DateTime, or Hash.
|
57
|
+
def initialize(project, identifier, options = {})
|
58
|
+
@project = project
|
59
|
+
@identifier = identifier
|
60
|
+
@author_email = options[:author_email] || options["author_email"]
|
61
|
+
@author_name = options[:author_name] || options["author_name"]
|
62
|
+
@committer_email = options[:committer_email] || options["committer_email"]
|
63
|
+
@committer_name = options[:committer_name] || options["committer_name"]
|
64
|
+
@short_message = options[:short_message] || options["short_message"]
|
65
|
+
@message = options[:message] || options["message"]
|
66
|
+
@committed_at = options[:committed_at] || options["committed_at"]
|
67
|
+
@authored_at = options[:authored_at] || options["authored_at"]
|
68
|
+
@uploaded_at = options[:uploaded_at] || options["uploaded_at"]
|
69
|
+
@api_uri = options[:api_uri] || options["api_uri"]
|
70
|
+
@parent_identifiers = options[:parent_identifiers] || options["parent_identifiers"]
|
71
|
+
@line_additions = options[:line_additions] || options["line_additions"]
|
72
|
+
@line_deletions = options[:line_deletions] || options["line_deletions"]
|
73
|
+
@line_total = options[:line_total] || options["line_total"]
|
74
|
+
@affected_file_count = options[:affected_file_count] || options["affected_file_count"]
|
75
|
+
@custom_attributes = options[:custom_attributes] || options["custom_attributes"] || {}
|
76
|
+
convert_custom_attributes_keys_to_symbols
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the name of the author and the email associated
|
80
|
+
# with the commit in a string formatted as:
|
81
|
+
# "Name [email_address]"
|
82
|
+
# (ie: "John Doe [jdoe@example.com]")
|
83
|
+
def author
|
84
|
+
"#{author_name} [#{author_email}]"
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns the name of the committer and the email associated
|
88
|
+
# with the commit in a string formatted as:
|
89
|
+
# "Name [email_address]"
|
90
|
+
# (ie: "John Doe [jdoe@example.com]")
|
91
|
+
def committer
|
92
|
+
"#{committer_name} [#{committer_email}]"
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the Commit object associated with the supplied identifier.
|
96
|
+
# Returns nil if the identifier is not found.
|
97
|
+
def self.find(project, identifier)
|
98
|
+
response = API.get("/projects/#{project.public_key}/commits/#{identifier}")
|
99
|
+
case response.code
|
100
|
+
when 200
|
101
|
+
return nil if response["commit"].empty?
|
102
|
+
new(project, response["commit"].delete("identifier"), response["commit"])
|
103
|
+
else
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns a collection of commits associated with the specified
|
109
|
+
# Project public key.
|
110
|
+
def self.all(project)
|
111
|
+
response = API.get("/projects/#{project.public_key}/commits")
|
112
|
+
case response.code
|
113
|
+
when 200
|
114
|
+
return [] if response["commits"].empty? || response["commits"]["commit"].nil?
|
115
|
+
response["commits"]["commit"].map do |commit_data|
|
116
|
+
new(commit_data.delete("identifier"), commit_data)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns the most recent commit associated with the specified
|
124
|
+
# Project public key.
|
125
|
+
def self.latest(project)
|
126
|
+
response = API.get("/projects/#{project.public_key}/commits/latest")
|
127
|
+
case response.code
|
128
|
+
when 200
|
129
|
+
new(project, response["commit"].delete("identifier"), response["commit"])
|
130
|
+
else
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the commit identifier of the most recent commit of with
|
136
|
+
# the specified Project public key.
|
137
|
+
def self.latest_identifier(project)
|
138
|
+
latest_commit = latest(project)
|
139
|
+
latest_commit.nil? ? nil : latest_commit.identifier
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
def convert_custom_attributes_keys_to_symbols
|
144
|
+
@custom_attributes = @custom_attributes.inject({}) do |results, key_and_value|
|
145
|
+
results.merge! key_and_value.first.to_sym => key_and_value.last
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|