travis-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format nested
@@ -0,0 +1,9 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - ree
6
+ - rbx
7
+ - jruby
8
+ - ruby-head
9
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ gemspec
4
+
@@ -0,0 +1,111 @@
1
+ h1. Travis Ruby Client - "!https://secure.travis-ci.org/travis-ci/travis-ruby-client.png?branch=master!":http://travis-ci.org/travis-ci/travis-ruby-client
2
+
3
+ h2. Contact
4
+ * "Github":http://github.com/travis-ci/travis-ruby-client
5
+ * "Twitter":http://twitter.com/travisci
6
+ * "IRC":irc://irc.freenode.net#travis
7
+ * "Mailinglist":http://groups.google.com/group/travis-ci
8
+
9
+ h2. Documentation
10
+
11
+ * "YARD docs":http://rdoc.info/github/travis-ci/travis-ruby-client/master/file/README.textile
12
+
13
+ h2. Usage [As a Command Line Tool]
14
+
15
+ <pre>
16
+ Supported Commands:
17
+ travis repositories|repos|repo|r {options}
18
+ travis status|stat|s {options}
19
+
20
+ Repositories:
21
+ travis status|stat|s
22
+ travis repositories|repos|repo|r [--recent]
23
+ travis repositories --slugs={repository_slug}[,{repository_slug}[,...]]
24
+ travis repositories --name={repository_name} --owner={owner_name}
25
+ travis repositories --slug={repository_slug}
26
+ travis repositories --builds
27
+ travis repositories --name={repository_name} --owner={owner_name} --build_id={build_id}
28
+ travis repositories --slug={repository_slug} --build_id={build_id}
29
+
30
+ Supported Options:
31
+ --recent lists the recent processed repositories.
32
+ -B, --builds lists the recent builds for a repository.
33
+ -o, --owner= sets the target repository owner's name.
34
+ -n, --name= sets the target repository name.
35
+ -s, --slug= sets the target repositorys slug.
36
+ -S, --slugs= sets the target repositories slugs (comma separated).
37
+ -b, --build_id= sets the target repository build id.
38
+ -h, -H, --help display this help message.
39
+ </pre>
40
+
41
+ The `status` command should be run from your project directory. It will try to identify and dispaly the Travis status of the target repository based on the current directory remote git urls.
42
+
43
+ h2. Usage [As a Ruby Library]
44
+
45
+ h3. Fetching Repositories
46
+
47
+ <pre>
48
+ Travis::API::Client::Repositories.all #Collection of Repository instances
49
+
50
+ Travis::API::Client::Repositories.all! #Collection of Repository instances bypassing cache
51
+
52
+ Travis::API::Client::Repositories.owner('owner_name_here').name('name_here').fetch #Repository
53
+
54
+ Travis::API::Client::Repositories.owner('owner_name_here').name('name_here').fetch! #Repository bypassing cache
55
+
56
+ Travis::API::Client::Repositories.slug('owner_name_here/name_here').fetch #Repository
57
+
58
+ Travis::API::Client::Repositories.slug('owner_name_here/name_here').fetch! #Repository bypassing cache
59
+ </pre>
60
+
61
+ h3. Fetching Builds
62
+
63
+ <pre>
64
+ Travis::API::Client::Repositories.owner('owner_name_here').name('name_here').builds #Collection of Build instances
65
+
66
+ Travis::API::Client::Repositories.owner('owner_name_here').name('name_here').builds! #Collection of Build instances bypassing cache
67
+
68
+ Travis::API::Client::Repositories.slug('owner_name_here/name_here').builds #Collection of Build instances
69
+
70
+ Travis::API::Client::Repositories.slug('owner_name_here/name_here').builds! #Collection of Build instances bypassing cache
71
+
72
+ Travis::API::Client::Repositories.owner('owner_name_here').name('name_here').build('id_here') #Build
73
+
74
+ Travis::API::Client::Repositories.owner('owner_name_here').name('name_here').build!('id_here') #Build bypassing cache
75
+
76
+ Travis::API::Client::Repositories.slug('owner_name_here/name_here').build('id_here') #Build
77
+
78
+ Travis::API::Client::Repositories.slug('owner_name_here/name_here').build!('id_here') #Build bypassing cache
79
+ </pre>
80
+
81
+ h3. Repository methods
82
+
83
+ The main attributes from the API will be accessible via the following methods:
84
+
85
+ bc. [:slug, :id, :status, :last_build_id, :last_build_status, :last_build_number, :last_build_finished_at]
86
+
87
+ Some relationships can be retrieved using the following methods:
88
+
89
+ bc. repository.builds #=> Collection of Build instances
90
+ repository.last_build #=> Build
91
+
92
+ You can also update the repository information from the API usign:
93
+
94
+ bc. repository.reload! #=> Fetch the updated repository data from the API and updates its attributes and relationships
95
+
96
+ h3. Build methods
97
+
98
+ The main attributes from the API will be accessible via the following methods:
99
+
100
+ bc. [:number, :commited_at, :commit, :finished_at, :config, :author_name, :log, :branch, :id, :parent_id, :started_at, :author_email, :status, :repository_id, :message, :compare_url]
101
+
102
+ Some relationships can be retrieved using the following methods:
103
+
104
+ bc. build.parent #=> Build or nil
105
+ build.repository #=> Repository
106
+ build.matrix #=> Collection of Build instances or nil
107
+
108
+ You can also update the build information from the API usign:
109
+
110
+ bc. build.reload! #=> Fetch the updated build data from the API and updates its attributes and relationships
111
+
@@ -0,0 +1,8 @@
1
+ require 'cucumber/rake/task'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Cucumber::Rake::Task.new(:cucumber)
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => [:spec, :cucumber]
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << './lib/'
3
+ require 'travis/client'
4
+
5
+ available_commands = ['repositories', 'status']
6
+
7
+ command = ARGV.first.to_s.downcase
8
+
9
+ if !available_commands.include?(command) && available_commands.select {|c| c =~ Regexp.new("^#{command}.*")}.length == 1
10
+ command = available_commands.detect {|c| c =~ Regexp.new("^#{command}.*")}
11
+ end
12
+
13
+ if ['repositories'].include?(command)
14
+ Travis::Client.const_get(command.capitalize).new.run
15
+ elsif ['status'].include?(command)
16
+ Travis::Client.send(command)
17
+ else
18
+ ARGV << '-h'
19
+ Travis::Client.new.run
20
+ end
21
+
@@ -0,0 +1,117 @@
1
+ Feature: Repositories
2
+ In order to get informatio about the Travis CI repoisitories
3
+ As ruby application
4
+ I want to get the the repositories information through the API wrapper
5
+
6
+ Scenario Outline: Get all repositories
7
+ Given I am using the Travis::API::Client::Repositories client
8
+ When I request <method>
9
+ Then I should get a collection of Travis::API::Entity::Repository instances
10
+
11
+ Examples:
12
+ |method |
13
+ |all |
14
+ |all! |
15
+
16
+ Scenario Outline: Get a repository by name and owner
17
+ Given I am using the Travis::API::Client::Repositories client
18
+ When I set the name to "travis-ci"
19
+ And I set the owner to "travis-ci"
20
+ And I request <method>
21
+ Then I should get a Travis::API::Entity::Repository
22
+ And the result should have the following values:
23
+ | name | travis-ci |
24
+ | owner | travis-ci |
25
+ | slug | travis-ci/travis-ci |
26
+ And the result should have the following numeric values:
27
+ | id | 59 |
28
+
29
+ Examples:
30
+ |method |
31
+ |fetch |
32
+ |fetch! |
33
+
34
+ # Right now there's no way to get a repository directly by id.
35
+ #
36
+ # We are iterating over the recent repositories trying to find the target repo by id.
37
+ #
38
+ # It will fail most of the times since the recent repositories is a really small list.
39
+ #
40
+ # Looking forward for an API call to get repos by id or at list one to get all the repos and
41
+ # not just the recent ones.
42
+ #
43
+ # Scenario Outline: Get a repository by name and owner
44
+ # Given I am using the Travis::API::Client::Repositories client
45
+ # When I set the id to "59"
46
+ # And I request <method>
47
+ # Then I should get a Travis::API::Entity::Repository
48
+ # And the result should have the following values:
49
+ # | name | travis-ci |
50
+ # | owner | travis-ci |
51
+ # | slug | travis-ci/travis-ci |
52
+ # And the result should have the following numeric values:
53
+ # | id | 59 |
54
+ #
55
+ # Examples:
56
+ # |method |
57
+ # |fetch |
58
+ # |fetch! |
59
+ #
60
+
61
+ Scenario Outline: Get a repository by name and owner
62
+ Given I am using the Travis::API::Client::Repositories client
63
+ When I set the slug to "travis-ci/travis-ci"
64
+ And I request <method>
65
+ Then I should get a Travis::API::Entity::Repository
66
+ And the result should have the following values:
67
+ | name | travis-ci |
68
+ | owner | travis-ci |
69
+ | slug | travis-ci/travis-ci |
70
+ And the result should have the following numeric values:
71
+ | id | 59 |
72
+
73
+ Examples:
74
+ |method |
75
+ |fetch |
76
+ |fetch! |
77
+
78
+ Scenario Outline: Get the builds from a repository
79
+ Given I am using the Travis::API::Client::Repositories client
80
+ When I set the owner to "travis-ci"
81
+ And I set the name to "travis-ci"
82
+ And I request <method>
83
+ Then I should get a collection of Travis::API::Entity::Build instances
84
+
85
+ Examples:
86
+ |method |
87
+ |builds |
88
+ |builds! |
89
+
90
+ Scenario Outline: Get a build from a repository by id
91
+ Given I am using the Travis::API::Client::Repositories client
92
+ When I set the owner to "travis-ci"
93
+ And I set the name to "travis-ci"
94
+ And I request <method> with the following params:
95
+ |69619 |
96
+ Then I should get a Travis::API::Entity::Build
97
+ And the result should have the following values:
98
+ | author_email | svenfuchs@artweb-design.de |
99
+ | author_name | Sven Fuchs |
100
+ | branch | statemachine |
101
+ | commit | 04beda102abcb37b353e406663535d2bc2c4da5c |
102
+ | committed_at | 2011-08-07T00:03:40Z |
103
+ | compare_url | https://github.com/travis-ci/travis-ci/compare/5878605...04beda1 |
104
+ | finished_at | 2011-08-07T00:18:36Z |
105
+ | message | Merge branch 'statemachine' of github.com:travis-ci/travis-ci into statemachine |
106
+ | number | 772 |
107
+ | started_at | 2011-08-07T00:14:41Z |
108
+ And the result should have the following numeric values:
109
+ | id | 69619 |
110
+ | repository_id | 59 |
111
+ | status | 1 |
112
+ And the result should respond to "matrix" with a collection of Travis::API::Entity::Build instances
113
+ And the result should respond to "repository" with a Travis::API::Entity::Repository instances
114
+ Examples:
115
+ |method |
116
+ |build |
117
+ |build! |
@@ -0,0 +1,51 @@
1
+ Given /^I am using the ((?:\:?{2}\w+)+) client$/ do |class_name|
2
+ @client = class_name.split('::').inject(Object) {|x,y| x = x.const_get(y)}
3
+ end
4
+
5
+ Then /^I should get a collection of ((?:\:?{2}\w+)+) instances$/ do |class_name|
6
+ expected_class = class_name.split('::').inject(Object) {|x,y| x = x.const_get(y)}
7
+ @result.each do |element|
8
+ element.should be_instance_of expected_class
9
+ end
10
+ end
11
+
12
+ When /^I set the (\w+) to "([^"]*)"$/ do |method, value|
13
+ @client = @client.send(method, value)
14
+ end
15
+
16
+ Then /^I should get a ((?:\:?{2}\w+)+)$/ do |class_name|
17
+ @result.should be_instance_of class_name.split('::').inject(Object) {|x,y| x = x.const_get(y)}
18
+ end
19
+
20
+ Then /^the result should have the following values:$/ do |table|
21
+ table.rows_hash.each_pair do |key, value|
22
+ @result.send(key).should == value
23
+ end
24
+ end
25
+
26
+ Then /^the result should have the following numeric values:$/ do |table|
27
+ table.rows_hash.each_pair do |key, value|
28
+ @result.send(key).should == value.to_f
29
+ end
30
+ end
31
+
32
+ When /^I request (\w+\!?)$/ do |method|
33
+ @result = @client.send(method)
34
+ end
35
+
36
+ When /^I request (\w+\!?) with the following params:$/ do |method, table|
37
+ @result = @client.send(method, *table.raw.collect(&:first))
38
+ end
39
+
40
+ Then /^the result should respond to "([^"]*)" with a collection of ((?:\:?{2}\w+)+) instances$/ do |method, class_name|
41
+ expected_class = class_name.split('::').inject(Object) {|x,y| x = x.const_get(y)}
42
+ @result.send(method).each do |element|
43
+ element.should be_instance_of expected_class
44
+ end
45
+ end
46
+
47
+ Then /^the result should respond to "([^"]*)" with a ((?:\:?{2}\w+)+) instances$/ do |method, class_name|
48
+ expected_class = class_name.split('::').inject(Object) {|x,y| x = x.const_get(y)}
49
+ @result.send(method).should be_instance_of expected_class
50
+ end
51
+
@@ -0,0 +1,2 @@
1
+ require File.dirname(__FILE__) + '/../../lib/travis.rb'
2
+ require File.dirname(__FILE__) + '/../../lib/travis/client.rb'
@@ -0,0 +1,2 @@
1
+ require 'travis/api'
2
+
@@ -0,0 +1,3 @@
1
+ require 'travis/api/client'
2
+ require 'travis/api/entity'
3
+
@@ -0,0 +1,95 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'faraday'
4
+ require 'travis/api/client/repositories'
5
+
6
+ module Travis
7
+
8
+ module API
9
+
10
+ # Travis API Client
11
+
12
+ class Client
13
+
14
+ def initialize(options = {})
15
+ @options = {:format => DEFAULT_FORMAT}.merge(options)
16
+ end
17
+
18
+ # Sets the response format and returns the host client instance
19
+ #
20
+ # @param [Symbol, String] format The desired response format, <tt>:json</tt>.
21
+ # @return [Client, Client::Repositories] The host client instance.
22
+ def format(format)
23
+ @options[:format] = format.to_s
24
+ self
25
+ end
26
+
27
+ def self.method_missing(method, *args, &block)
28
+ return self.new.send(method, *args, &block) if self.client.respond_to?(method)
29
+ super
30
+ end
31
+
32
+ def self.respond_to?(method, include_private = false)
33
+ self.client.respond_to?(method, include_private) || super(method, include_private)
34
+ end
35
+
36
+ private
37
+
38
+ # Client default host
39
+ API_HOST = 'http://travis-ci.org'
40
+
41
+ # Response default format
42
+ DEFAULT_FORMAT = 'json'
43
+
44
+ # Returns the HTTP connection handler
45
+ #
46
+ # @return [Faraday]
47
+ def connection
48
+ @@connection ||= Faraday.new(:url => API_HOST) do |builder|
49
+ builder.adapter :net_http
50
+ end
51
+ end
52
+
53
+ # Returns the encoded response for the given path and previouly set options
54
+ #
55
+ # @param [String] path API target path
56
+ # @return Encoded response
57
+ def results_for(path_template)
58
+ path = path_template.dup
59
+
60
+ @options.each_pair do |key, value|
61
+ path.gsub!(":#{key}", value)
62
+ end
63
+
64
+ response = connection().get(path)
65
+
66
+ return response.status == 200 ? parse(response.body) : nil
67
+ end
68
+
69
+ # Encodes and return the given content based on the previously set format
70
+ #
71
+ # @param [String] content Content to be encoded
72
+ # @return Encoded conter
73
+ def parse(content)
74
+ case @options[:format]
75
+ when 'json' then
76
+ return JSON.parse(content)
77
+ when 'xml'
78
+ return REXML::Document.new(content)
79
+ end
80
+ end
81
+
82
+
83
+ # Initializes and return an instance of the current class
84
+ #
85
+ # @return A new instance of the current class
86
+ def self.client
87
+ self.new
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
@@ -0,0 +1,234 @@
1
+ module Travis
2
+
3
+ module API
4
+
5
+ class Client
6
+
7
+ # Travis API Client for Repositories
8
+
9
+ class Repositories < Client
10
+
11
+ # Returns the cached list of repositories or fetches the
12
+ # recent list if there's nothing already cached.
13
+ #
14
+ # @return [Array<Entity::Repository>]
15
+ def all
16
+ #Having some issues with the cached repositories
17
+ #@@repositories || self.all!
18
+ self.all!
19
+ end
20
+
21
+ # Fetches and return the list of recent repositories.
22
+ # It also updates the repositorires cache.
23
+ #
24
+ # @return [Array<Entity::Repository>]
25
+ def all!
26
+ @@repositories = results_for(REPOSITORIES_PATH).map {|repository_data| Entity::Repository.new(repository_data)}
27
+ end
28
+
29
+ # Returns a repository from the cache or fetches it
30
+ # based on the previously set options (name and owner or slug or id).
31
+ #
32
+ # @return [Entity::Repository]
33
+ def fetch
34
+ #Having some issues with the cached repositories
35
+ #fetch_from_cache() || self.fetch!
36
+ self.fetch!
37
+ end
38
+
39
+ # Fetches and return a repository based on the previously
40
+ # set options (name and owner or slug or id).
41
+ # It also adds this repository to the repositories cache.
42
+ #
43
+ # @return [Entity::Repository]
44
+ def fetch!
45
+ repository = @options[:id] ? fetch_by_id!() : fetch_by_owner_and_name!()
46
+ repositories_cache() << repository
47
+ repository
48
+ end
49
+
50
+ # Returns the cached list of builds or fetches the
51
+ # recent list if there's nothing already cached.
52
+ #
53
+ # @return [Array<Entity::Build>]
54
+ def builds
55
+ #Having some issues with the cahced builds
56
+ #@@builds || self.builds!
57
+ self.builds!
58
+ end
59
+
60
+ # Fetches and return the list of recent builds.
61
+ # It also updates the builds cache.
62
+ #
63
+ # @return [Array<Entity::Build>]
64
+ def builds!
65
+ @@builds = results_for(REPOSITORY_BUILDS_PATH).map {|build_data| Entity::Build.new(build_data, @options[:owner], @options[:name])}
66
+ end
67
+
68
+ # Returns a build from the cache or fetches it
69
+ # based on the given build id and the previously set
70
+ # options (name and owner or slug or id).
71
+ #
72
+ # @return [Entity::Build]
73
+ def build(build_id)
74
+ #Having some issues with the cached builds
75
+ #build_from_cache(build_id) || self.build!(build_id)
76
+ self.build!(build_id)
77
+ end
78
+
79
+ # Fetches and return a build based on the given
80
+ # build id and the previously set options (name and owner or
81
+ # slug or id).
82
+ # It also adds this build to the builds cache.
83
+ #
84
+ # @return [Entity::Build]
85
+ def build!(build_id)
86
+ @options[:build_id] = build_id.to_s
87
+ builds_cache().delete_if {|b| b.id == @options[:build_id]}
88
+ build = Entity::Build.new(results_for(REPOSITORY_BUILD_PATH), @options[:owner], @options[:name])
89
+ builds_cache() << build
90
+ build
91
+ end
92
+
93
+ # Sets the target repository id and return the host client instance.
94
+ # @param [Fixnum, String] id The target repository id.
95
+ # @return [Client::Repositories] The host client instance.
96
+ def id(id)
97
+ @options[:id] = id
98
+ self
99
+ end
100
+
101
+ # Sets the target repository owner and return the host client instance.
102
+ # @param [String] owner The target repository owner.
103
+ # @return [Client::Repositories] The host client instance.
104
+ def owner(owner)
105
+ @options[:owner] = owner
106
+ self
107
+ end
108
+
109
+ # Sets the target repository name and return the host client instance.
110
+ # @param [String] name The target repository name.
111
+ # @return [Client::Repositories] The host client instance.
112
+ def name(name)
113
+ @options[:name] = name
114
+ self
115
+ end
116
+
117
+ # Sets the target repository slug and return the host client instance.
118
+ # @param [String] slug The target repository slug.
119
+ # @return [Client::Repositories] The host client instance.
120
+ def slug(slug)
121
+ @options[:owner], @options[:name] = slug.split('/')
122
+ self
123
+ end
124
+
125
+ # Twick to accept `self.name(param)` as class method. It won't
126
+ # raise the NoMethodError but an ArgumentError so we can not
127
+ # apply reflection.
128
+ # Sice we are polite programmers the class still respond to the
129
+ # `self.name()` class method
130
+ #
131
+ # Creates an instance of the client, sets the target repository
132
+ # name and returns the host client instance.
133
+ #
134
+ # @param [String] name The target repository name.
135
+
136
+ # @return [Client::Repositories] The host client instance.
137
+ def self.name(name = nil)
138
+ return super unless name
139
+ self.new.name(name)
140
+ end
141
+
142
+ private
143
+
144
+ # API Path template for a repository
145
+ REPOSITORY_PATH = '/:owner/:name.:format'
146
+
147
+ # API Path template for the repository listing
148
+ REPOSITORIES_PATH = '/repositories.:format'
149
+
150
+ # API Path template for a repository build
151
+ REPOSITORY_BUILD_PATH = '/:owner/:name/builds/:build_id.:format'
152
+
153
+ # API Path template for the repository build listing
154
+ REPOSITORY_BUILDS_PATH = '/:owner/:name/builds.:format'
155
+
156
+ # Cached repositories
157
+ # @return [Array<Entity::Repository>, NilClass]
158
+ @@repositories = nil
159
+
160
+ # Cached builds
161
+ # @return [Array<Entity::Build>, NilClass]
162
+ @@builds = nil
163
+
164
+ # Returns the cached collection of repositories
165
+ # or an empty array if it was not initialized yet.
166
+ #
167
+ # @return [Array<Entity::Repository>, NilClass]
168
+ def repositories_cache
169
+ @@repositories || []
170
+ end
171
+
172
+ # Returns the cached collection of builds
173
+ # or an empty array if it was not initialized yet.
174
+ #
175
+ # @return [Array<Entity::Build>, NilClass]
176
+ def builds_cache
177
+ @@builds || []
178
+ end
179
+
180
+ # Fetches and returns a repository based on the
181
+ # owner and name option values.
182
+ #
183
+ # @return [Entity::Repository]
184
+ def fetch_by_owner_and_name!
185
+ repositories_cache().delete_if {|r| r.owner == @options[:owner] && r.name == @options[:name]}
186
+ repository_data = results_for(REPOSITORY_PATH)
187
+ return repository_data && Entity::Repository.new(repository_data, @options[:owner], @options[:name])
188
+ end
189
+
190
+ # Right now there's no way to get a repository directly by id.
191
+ #
192
+ # Iterates over the recent repositories trying to find the target repo by id.
193
+ #
194
+ # It will fail most of the times since the recent repositories is a really small list.
195
+ # Looking forward for an API call to get repos by id or at list one to get all the repos and
196
+ # not just the recent ones.
197
+ #
198
+ # Fetches and returns a repository based the id option value.
199
+ # If the Repository can not be found an exception will be rised.
200
+ #
201
+ # @return [Entity::Repository]
202
+ def fetch_by_id!
203
+ repositories_cache().delete_if {|r| r.id == @options[:id]}
204
+ return self.all!.find {|r| r.id == @options[:id]} || raise("Repository not found: id => #{@options[:id]}")
205
+ end
206
+
207
+ # Looks into the cache for a repository, based on the previously set
208
+ # id or name and owner, returning the repository or nil if it's not present.
209
+ #
210
+ # @return [Entity::Repository, NilClass]
211
+ def fetch_from_cache
212
+ if @options[:id]
213
+ repositories_cache().find {|repository| repository.id == @options[:id].to_i}
214
+ else
215
+ repositories_cache().find {|repository| repository.owner == @options[:owner] && repository.name == @options[:name]}
216
+ end
217
+ end
218
+
219
+ # Looks into the cache for a build, based on the given build id,
220
+ # returning the build or nil if it's not present.
221
+ #
222
+ # @return [Entity::Build, NilClass]
223
+ def build_from_cache(build_id)
224
+ builds_cache().find {|build| build.id == build_id.to_s}
225
+ end
226
+
227
+ end
228
+
229
+ end
230
+
231
+ end
232
+
233
+ end
234
+