polisher 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/README.rdoc +84 -14
  2. data/Rakefile +21 -19
  3. data/TODO +5 -4
  4. data/bin/server +1 -0
  5. data/config/polisher.yml +0 -15
  6. data/db/connection.rb +13 -10
  7. data/db/migrations/{002_create_managed_gems.rb → 001_create_projects.rb} +4 -5
  8. data/db/migrations/{001_create_sources.rb → 002_create_sources.rb} +1 -3
  9. data/db/migrations/003_create_project_source_versions.rb +28 -0
  10. data/db/migrations/{003_create_events.rb → 004_create_events.rb} +2 -2
  11. data/db/migrations/005_create_project_dependencies.rb +28 -0
  12. data/db/models/event.rb +47 -21
  13. data/db/models/project.rb +110 -0
  14. data/db/models/project_dependency.rb +27 -0
  15. data/db/models/project_source_version.rb +31 -0
  16. data/db/models/source.rb +82 -16
  17. data/lib/common.rb +37 -5
  18. data/lib/dsl.rb +292 -0
  19. data/lib/event_handlers.rb +139 -73
  20. data/lib/gem_adapter.rb +94 -0
  21. data/polisher.rb +302 -50
  22. data/public/stylesheets/style.css +15 -31
  23. data/spec/common_spec.rb +28 -0
  24. data/spec/dsl_spec.rb +357 -0
  25. data/spec/event_handlers_spec.rb +166 -30
  26. data/spec/gem_adapter_spec.rb +89 -0
  27. data/spec/models_spec.rb +635 -107
  28. data/spec/polisher_spec.rb +496 -56
  29. data/views/layout.haml +3 -5
  30. data/views/projects/index.haml +42 -0
  31. data/views/projects/index.html.haml +38 -0
  32. data/views/result.haml +9 -0
  33. data/views/sources/index.haml +24 -41
  34. data/views/sources/index.html.haml +26 -0
  35. metadata +131 -12
  36. data/db/models/managed_gem.rb +0 -111
  37. data/public/javascripts/jquery-1.2.6.min.js +0 -32
  38. data/public/javascripts/polisher.js +0 -15
  39. data/views/gems/index.haml +0 -106
@@ -0,0 +1,110 @@
1
+ # Copyright (C) 2010 Red Hat, Inc.
2
+ # Written by Mohammed Morsi <mmorsi@redhat.com>
3
+ #
4
+ # This program is free software, you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License
6
+ # as published by the Free Software Foundation, either version 3
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # You should have received a copy of the the GNU Affero
10
+ # General Public License, along with Polisher. If not, see
11
+ # <http://www.gnu.org/licenses/>
12
+
13
+ class Project < ActiveRecord::Base
14
+ # TODO on delete, destroy these
15
+ has_many :project_source_versions
16
+ has_many :sources, :through => :project_source_versions
17
+ has_many :events
18
+
19
+ has_many :project_dependencies
20
+ has_many :project_dependents, :class_name => "ProjectDependency", :foreign_key => "depends_on_project_id"
21
+
22
+ alias :dependencies :project_dependencies
23
+ alias :dependents :project_dependents
24
+
25
+ validates_presence_of :name
26
+ validates_uniqueness_of :name
27
+
28
+ # Download all project sources to specified :dir
29
+ def download_to(args = {})
30
+ # If a version isn't specified we can't lookup project_source_versions entry for uri substitutions
31
+ # FIXME the latter case in this ternary operator should be something along the lines of sources_for_all_versions returning those only associated w/ project_version = nil (also being sure to do uri_params substition) (?)
32
+ srcs = args.has_key?(:version) ? sources_for_version(args[:version]) : sources
33
+ srcs.each { |source| source.download_to args }
34
+ end
35
+
36
+ # Return all events associated w/ particular version of the project
37
+ def events_for_version(version)
38
+ evnts = events
39
+ evnts.find_all { |event| event.applies_to_version?(version) }
40
+ end
41
+
42
+ # Return all dependencies associated w/ particular version of the project
43
+ def dependencies_for_version(version)
44
+ deps = project_dependencies
45
+ deps.find_all { |dep| dep.project_version == version || dep.project_version.nil? }
46
+ end
47
+
48
+ # Return all project_source_versions associated w/ particular version of the project
49
+ def project_source_versions_for_version(version)
50
+ psa = project_source_versions
51
+ psa.find_all { |ps| ps.project_version == version || ps.project_version.nil? }
52
+ end
53
+
54
+ # Return all sources associated w/ particular version of the project, each w/ uri formatted
55
+ # using project_source_versions source_uri_params
56
+ def sources_for_version(version)
57
+ project_source_versions_for_version(version).collect { |ps|
58
+ ps.source.format_uri!(ps.source_uri_params)
59
+ ps.source
60
+ }
61
+ end
62
+
63
+ # Get the project primary source
64
+ def primary_source
65
+ ps = project_source_versions.all.find { |ps| ps.primary_source }
66
+ # TODO special case if no sources are marked as primary, grab the first ? (also in primary_source_for_version below)
67
+ return ps.nil? ? nil : ps.source
68
+ end
69
+
70
+ # Set the project primary source
71
+ def primary_source=(source)
72
+ project_source_versions << ProjectSourceVersion.new(:project => self, :source => source, :primary_source => true)
73
+ #source.save! ; save!
74
+ end
75
+
76
+ # Return the primary source for the specified version
77
+ def primary_source_for_version(version)
78
+ ps = project_source_versions_for_version(version).find { |ps| ps.primary_source }
79
+ ps.source.format_uri!(ps.source_uri_params) unless ps.nil?
80
+ return ps.nil? ? nil : ps.source
81
+ end
82
+
83
+ # Return all versions which we have configured this project for
84
+ def versions
85
+ # TODO should we return configured project_depents.depends_on_project_version as well ?
86
+ (project_source_versions.collect { |ps| ps.project_version } +
87
+ events.collect { |e| e.version } +
88
+ project_dependencies.collect { |d| d.project_version }).uniq - [nil]
89
+ end
90
+
91
+ # Release specified project version
92
+ def released_version(version, args = {})
93
+ # process dependencies
94
+ dependencies_for_version(version).each { |dep|
95
+ dargs = {}
96
+ dargs = dep.depends_on_project_params.to_h.merge!(args) unless dep.depends_on_project_params.nil?
97
+
98
+ # if dep_version.nil? grab all configured depends_on_project versions
99
+ dep_versions = dep.depends_on_project_version
100
+ dep_versions = dep_versions.nil? ? dep.depends_on_project.versions : [dep_versions]
101
+
102
+ dep_versions.each { |dv| dep.depends_on_project.released_version(dv, dargs) }
103
+ }
104
+
105
+ # process events
106
+ args[:version] = version
107
+ events_for_version(version).each { |event| event.run(args) }
108
+ end
109
+
110
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright (C) 2010 Red Hat, Inc.
2
+ # Written by Mohammed Morsi <mmorsi@redhat.com>
3
+ #
4
+ # This program is free software, you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License
6
+ # as published by the Free Software Foundation, either version 3
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # You should have received a copy of the the GNU Affero
10
+ # General Public License, along with Polisher. If not, see
11
+ # <http://www.gnu.org/licenses/>
12
+
13
+ class ProjectDependency < ActiveRecord::Base
14
+ belongs_to :project
15
+ belongs_to :depends_on_project, :class_name => "Project", :foreign_key => "depends_on_project_id"
16
+
17
+ validates_presence_of :project_id
18
+ validates_presence_of :depends_on_project_id
19
+
20
+ validates_uniqueness_of :depends_on_project_version, :scope => [:project_id, :depends_on_project_id, :depends_on_project_version]
21
+
22
+ before_save :normalize_versions
23
+ def normalize_versions
24
+ self.project_version = nil if project_version == ""
25
+ self.depends_on_project_version = nil if depends_on_project_version == ""
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright (C) 2010 Red Hat, Inc.
2
+ # Written by Mohammed Morsi <mmorsi@redhat.com>
3
+ #
4
+ # This program is free software, you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License
6
+ # as published by the Free Software Foundation, either version 3
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # You should have received a copy of the the GNU Affero
10
+ # General Public License, along with Polisher. If not, see
11
+ # <http://www.gnu.org/licenses/>
12
+
13
+ class ProjectSourceVersion < ActiveRecord::Base
14
+ belongs_to :project
15
+ belongs_to :source
16
+
17
+ # FIXME destroy source on deletion only if no other project_source_versions sharing the source exist
18
+
19
+ validates_uniqueness_of :source_id, :scope => [:project_id, :project_version]
20
+
21
+ # validate only one primary_source set to 'true' in scope of (project_id, project_version)
22
+ validates_uniqueness_of :primary_source,
23
+ :scope => [:project_id, :project_version],
24
+ :if => Proc.new { |ps| ps.primary_source }
25
+
26
+ before_save :normalize_versions
27
+ def normalize_versions
28
+ self.project_version = nil if project_version == ""
29
+ self.source_version = nil if source_version == ""
30
+ end
31
+ end
@@ -10,26 +10,92 @@
10
10
  # General Public License, along with Polisher. If not, see
11
11
  # <http://www.gnu.org/licenses/>
12
12
 
13
- # Source represents a remote endpoint which we will use
14
- # the gem API to get gems / subscribe to updates
13
+ require 'curl' # requires 'curb' package
14
+
15
15
  class Source < ActiveRecord::Base
16
- has_many :managed_gems
16
+ # TODO on delete, destroy these
17
+ has_many :project_source_versions
18
+ has_many :projects, :through => :project_source_versions
19
+
20
+ validates_presence_of :name
21
+ validates_uniqueness_of :name
22
+ validates_presence_of :source_type
23
+ validates_presence_of :uri
24
+ validates_uniqueness_of :uri
25
+
26
+ # TODO additional source types
27
+
28
+ SOURCE_TYPES = ['archive', 'patch', 'spec', 'gem', 'file', 'git_repo']
29
+
30
+ validates_inclusion_of :source_type, :in => SOURCE_TYPES
31
+
32
+ # Extract filename of this source from path
33
+ def filename
34
+ URI::parse(uri).path.split('/').last
35
+ end
36
+
37
+ # Return all project_source_versions associated w/ particular version of the source
38
+ def project_source_versions_for_version(version)
39
+ psa = project_source_versions
40
+ psa.find_all { |ps| ps.source_version == version || ps.source_version.nil? }
41
+ end
42
+
43
+ # Return all projects associated w/ particular version of the source
44
+ def projects_for_version(version)
45
+ project_source_versions_for_version(version).collect { |ps| ps.project }
46
+ end
47
+
48
+ # Return all versions which we have configured this project for
49
+ def versions
50
+ (project_source_versions.collect { |ps| ps.source_version }).uniq - [nil]
51
+ end
52
+
53
+ # Swap any occurence of the specified hash
54
+ # keys w/ their cooresponding values in the local source uri
55
+ def format_uri!(variables)
56
+ params = {}
57
+ if variables.class == String
58
+ params = variables.to_h
59
+ elsif variables.class == Hash
60
+ params = variables
61
+ else
62
+ return
63
+ end
64
+
65
+ turi = uri
66
+ params.each { |k,v| turi.gsub!("%{#{k}}", v.to_s) }
67
+ uri = turi
68
+ end
69
+
70
+ # Download source, args may contain any of the following
71
+ # * :path path to download source to
72
+ # * :dir directory to download source to, filename will be generated from the last part of the uri
73
+ def download_to(args = {})
74
+ # TODO handle source_type == git_repo
75
+
76
+ path = args.has_key?(:path) ? args[:path] : nil
77
+ dir = args.has_key?(:dir) ? args[:dir] : nil
78
+
79
+ # format the uri w/ any additional params
80
+ format_uri! args
17
81
 
18
- alias :gems :managed_gems
82
+ begin
83
+ # generate path which to d/l the file to
84
+ fn = filename
85
+ path = "#{dir}/#{fn}" if path.nil?
86
+ dir = File.dirname(path)
87
+ raise ArgumentError unless File.writable?(dir)
19
88
 
20
- validates_presence_of :name
21
- validates_presence_of :uri
22
- validates_uniqueness_of :name
23
- validates_uniqueness_of :uri
89
+ # d/l the file
90
+ curl = Curl::Easy.new(uri)
91
+ curl.follow_location = true # follow redirects
92
+ curl.perform
93
+ File.write path, curl.body_str
24
94
 
25
- # TODO validate format of uri
95
+ rescue Exception => e
96
+ raise RuntimeError, "could not download project source from #{uri} to #{path}"
97
+ end
26
98
 
27
- # TODO should have additional validation method that contacts gem source uri and
28
- # makes sure it satisfiest gem API requests
29
-
30
- # remove trailing slash in uri if present
31
- before_save :clean_uri!
32
- def clean_uri!
33
- self.uri = self.uri[0...self.uri.size-1] if self.uri[-1].chr == '/'
99
+ return path
34
100
  end
35
101
  end
@@ -12,24 +12,23 @@
12
12
  # General Public License, along with Polisher. If not, see
13
13
  # <http://www.gnu.org/licenses/>
14
14
 
15
- # read entire file into string
15
+ # Read entire file into string
16
16
  def File.read_all(path)
17
17
  File.open(path, 'rb') {|file| return file.read }
18
18
  end
19
19
 
20
- # write contents of file from string
20
+ # Write contents of file from string
21
21
  def File.write(path, str)
22
22
  File.open(path, 'wb') {|file| file.write str }
23
23
  end
24
24
 
25
- # create any missing directories
25
+ # Create any missing directories
26
26
  def create_missing_polisher_dirs(args = {})
27
27
  artifacts_dir = args[:artifacts_dir]
28
28
  db_data_dir = args[:db_data_dir]
29
29
  log_dir = args[:log_dir]
30
30
 
31
- [artifacts_dir + '/gems',
32
- artifacts_dir + '/repos',
31
+ [artifacts_dir + '/repos',
33
32
  artifacts_dir + '/SOURCES',
34
33
  artifacts_dir + '/SPECS',
35
34
  artifacts_dir + '/templates',
@@ -37,3 +36,36 @@ def create_missing_polisher_dirs(args = {})
37
36
  FileUtils.mkdir_p(dir) unless File.directory? dir
38
37
  }
39
38
  end
39
+
40
+ # Set up and return polisher config from application
41
+ def load_polisher_config(app)
42
+ config = {}
43
+ loaded_config = YAML::load(File.open(app.polisher_config))[app.environment.to_s]
44
+ config.merge!(loaded_config) unless loaded_config.nil?
45
+
46
+ # Attempt to parse gem api key from ~/.gem/credentials if missing
47
+ if config["gem_api_key"].nil?
48
+ gcfile = File.expand_path("~/.gem/credentials")
49
+ if File.exists?(gcfile)
50
+ config["gem_api_key"] = File.read_all(gcfile).scan(/:rubygems_api_key:\s(.*)/).to_s
51
+ end
52
+ end
53
+ return config
54
+ end
55
+
56
+ class String
57
+
58
+ # Parse/split string around element delimiters (;) and
59
+ # key/value delimiters (=) and convert to hash.
60
+ def to_h
61
+ ret = {}
62
+ split(';').each { |p| u = p.split('='); ret[u[0]] = u[1] }
63
+ ret
64
+ end
65
+
66
+ # Convert hash into string
67
+ def self.from_h(hash)
68
+ hash.keys.collect { |k| k.to_s + "=" + hash[k].to_s }.join(";")
69
+ end
70
+
71
+ end
@@ -0,0 +1,292 @@
1
+ # Polisher dsl
2
+ #
3
+ # Copyright (C) 2010 Red Hat, Inc.
4
+ # Written by Mohammed Morsi <mmorsi@redhat.com>
5
+ #
6
+ # This program is free software, you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation, either version 3
9
+ # of the License, or (at your option) any later version.
10
+ #
11
+ # You should have received a copy of the the GNU Affero
12
+ # General Public License, along with Polisher. If not, see
13
+ # <http://www.gnu.org/licenses/>
14
+
15
+ require 'libxml'
16
+ require 'rest_client'
17
+
18
+ module Polisher
19
+
20
+ # Helper method to handle (print) xml status response
21
+ def handle_response(operation, response, exception_on_fail = false)
22
+ rr = LibXML::XML::Document.string(response.body).root
23
+ success = rr.children.find { |c| c.name == "success" }.content.strip == "true"
24
+ msg = rr.children.find { |c| c.name == "message" }.content.strip
25
+ puts "#{operation} returned w/ success = #{success} and the message: #{msg}"
26
+ raise RuntimeError, "#{operation} returned w/ failed status: #{msg}" if exception_on_fail && !success
27
+ end
28
+ module_function :handle_response
29
+
30
+ # DSL representations of model classes (so as to not require db connection on client side)
31
+
32
+ # DSL project
33
+ class Project
34
+ # Project attributes
35
+ attr_accessor :id, :name
36
+
37
+ # Means to store project version and dependency params to be used when setting up project_source_versions and deps
38
+ attr_accessor :project_version, :dependency_params
39
+
40
+ def initialize(args = {})
41
+ args = {:id => nil, :name => nil}.merge(args)
42
+ @id = args[:id] ; @name = args[:name]
43
+ @dependency_params = {}
44
+ end
45
+
46
+ # Add a method to project for each source type, eg add_archive, add_path, add_repo, etc.
47
+ # TODO maintain as source_types are added (or put this this in a seperate module)
48
+ # XXX ran into this when doing it metaprogramatically http://coderrr.wordpress.com/2008/10/29/using-define_method-with-blocks-in-ruby-18/
49
+ def add_archive args = {}, &block; add_source('archive', args, &block); end
50
+ def add_patch args = {}, &block; add_source('patch', args, &block); end
51
+ def add_gem args = {}, &block; add_source('gem', args, &block); end
52
+ def add_file args = {}, &block; add_source('file', args, &block); end
53
+
54
+ def add_source type, args = {}, &block
55
+ args[:source_type] = type # automatically set the source type
56
+ src = source(args)
57
+
58
+ # Dispatch source to caller blocker to register specific project/source versions.
59
+ if !block.nil?
60
+ block.call src
61
+
62
+ # If no block is given register default */* project/source version
63
+ # TODO should this execute regardless of whether there is a block or not, eg only if no project/source versions were created/exist (need to perform a new query to get that info)
64
+ else
65
+ version nil, :corresponds_to => src
66
+ end
67
+ end
68
+
69
+ # Create Project from xml and return
70
+ def self.from_xml(xml_str)
71
+ project = Polisher::Project.new
72
+
73
+ xml = LibXML::XML::Document.string(xml_str).root
74
+ project.id = xml.children.find { |c| c.name == "id" }.content.to_i
75
+ project.name = xml.children.find { |c| c.name == "name" }.content.strip
76
+ # TODO associated versions, sources, events
77
+ #xml.children.find { |c| c.name == "version" }.children.each { |c|
78
+ #uri = c.children.find { |c| c.name == "uri"}
79
+ #project.sources << uri.content.strip unless uri.nil?
80
+ #}
81
+
82
+ return project
83
+ end
84
+
85
+ # Retrieve and return all projects
86
+ def self.all
87
+ projects = []
88
+ RestClient.get("#{$polisher_uri}/projects") { |response|
89
+ xml = LibXML::XML::Document.string(response.body).root
90
+ xml.children.find_all { |c| c.name == "project" }.each { |s|
91
+ projects << Polisher::Project.from_xml(s.to_s)
92
+ }
93
+ }
94
+ return projects
95
+ end
96
+
97
+ # Create project
98
+ def create
99
+ RestClient.post("#{$polisher_uri}/projects/create", :name => name) { |response| Polisher.handle_response('create project', response, true) }
100
+ end
101
+
102
+ # Delete project
103
+ def delete
104
+ RestClient.delete("#{$polisher_uri}/projects/destroy/#{id}") { |response| Polisher.handle_response('delete project', response, true) }
105
+ end
106
+
107
+ # Create new Event w/ the specified version qualifier, version, process, and process options
108
+ def on_version(*args)
109
+ args.unshift nil unless ['', '=', '>', '<', '>=', '<='].include?(args[0]) # XXX don't like having to replicate entire event version qualifiers, but don't want the activerecord dependency
110
+ version_qualifier = args[0]
111
+ version = args[1]
112
+ process = args[2]
113
+ process_options = args[3]
114
+ process_options = "" if process_options.nil?
115
+ process_options += ";" + String.from_h(args[4]) if args.size == 5
116
+
117
+ process.gsub!(/\s/, '_')
118
+
119
+ RestClient.post("#{$polisher_uri}/events/create",
120
+ :project_id => id, :process => process, :version => version,
121
+ :version_qualifier => version_qualifier, :process_options => process_options) { |response| Polisher.handle_response('create event', response) }
122
+ end
123
+
124
+ # Associate specified project version w/ corresponding source version if specified,
125
+ # else if not just return self
126
+ def version(version, args = {})
127
+ version = nil if version == "*"
128
+ @project_version = version
129
+ @dependency_params = args unless args.empty?
130
+
131
+ if args.has_key?(:corresponds_to)
132
+ # dispatch to source.version so we don't have to implement twice
133
+ source = args[:corresponds_to]
134
+ source.version source.source_version, :corresponds_to => self
135
+
136
+ elsif args.has_key?(:depends_on)
137
+ project = args.delete(:depends_on)
138
+ args = project.dependency_params.merge(args) unless project.dependency_params.empty?
139
+ depends_on_args = String.from_h(args) unless args.empty?
140
+ args = {:project_id => id, :project_version => version,
141
+ :depends_on_project_id => project.id, :depends_on_project_version => project.project_version,
142
+ :depends_on_project_params => depends_on_args}
143
+ RestClient.post("#{$polisher_uri}/project_dependencies/create", args) { |response| Polisher.handle_response('created project dependency', response) }
144
+
145
+ else
146
+ return self
147
+ end
148
+ end
149
+
150
+ # Test fire project released event for specified version
151
+ def released(version, params = {})
152
+ resource = RestClient::Resource.new("#{$polisher_uri}/projects/released", :timeout => 1000) # give event handlers plenty of time to run
153
+
154
+ sparams = "name=#{name}&version=#{version}"
155
+ params.each { |k,v| sparams += "&#{k}=#{v}" }
156
+ resource.post sparams do |response|
157
+ Polisher.handle_response('released project', response)
158
+ end
159
+ end
160
+ end
161
+
162
+ # DSL Source
163
+ class Source
164
+ # Source attributes
165
+ attr_accessor :id, :name, :uri, :source_type
166
+
167
+ # Means to store source version and optional uri params to be used when setting up project sources
168
+ attr_accessor :source_version, :uri_args
169
+
170
+ # Means to store primary_source value when creating projects sources
171
+ attr_accessor :primary_source
172
+
173
+ def initialize(args = {})
174
+ args = { :id => nil, :name => nil, :uri => nil, :source_type => nil, :primary_source => false}.merge(args)
175
+ @id = args[:id] ; @name = args[:name] ; @uri = args[:uri] ; @source_type = args[:source_type] ; @primary_sources = args[:primary_source]
176
+ @uri_args = '' ; @primary_source = false
177
+ end
178
+
179
+ # Create Source from xml and return
180
+ def self.from_xml(xml_str)
181
+ source = Polisher::Source.new
182
+
183
+ xml = LibXML::XML::Document.string(xml_str).root
184
+ source.id = xml.children.find { |c| c.name == "id" }.content.to_i
185
+ source.name = xml.children.find { |c| c.name == "name" }.content.strip
186
+ source.uri = xml.children.find { |c| c.name == "uri" }.content.strip
187
+ source.source_type = xml.children.find { |c| c.name == "source_type" }.content.strip
188
+ # TODO associated versions, projects, events
189
+
190
+ return source
191
+ end
192
+
193
+ # Retrieve and return all sources
194
+ def self.all
195
+ sources = []
196
+ RestClient.get("#{$polisher_uri}/sources") { |response|
197
+ xml = LibXML::XML::Document.string(response.body).root
198
+ xml.children.find_all { |c| c.name == "source" }.each { |s|
199
+ sources << Polisher::Source.from_xml(s.to_s)
200
+ }
201
+ }
202
+ return sources
203
+ end
204
+
205
+ # Create source
206
+ def create
207
+ RestClient.post("#{$polisher_uri}/sources/create", :name => name, :uri => uri, :source_type => source_type) { |response| Polisher.handle_response('create project', response, true) }
208
+ end
209
+
210
+ # Associate specified project source w/ corresponding source version if specified,
211
+ # else if not just return self
212
+ def version(version, args = {})
213
+ version = nil if version == "*"
214
+ project = args.delete(:corresponds_to)
215
+
216
+ @uri_args = String.from_h(args) unless args.empty?
217
+
218
+ if project.nil?
219
+ @source_version = version
220
+ return self
221
+ end
222
+
223
+ args = {:project_id => project.id, :project_version => project.project_version,
224
+ :source_id => id, :source_version => version, :source_uri_params => @uri_args,
225
+ :primary_source => @primary_source }
226
+ RestClient.post("#{$polisher_uri}/project_source_versions/create", args) { |response| Polisher.handle_response('created project source', response) }
227
+ end
228
+
229
+ # Set source as primary in project/source associations
230
+ def is_the_primary_source
231
+ @primary_source = true
232
+ end
233
+ end
234
+
235
+ end # module Polisher
236
+
237
+ # Set polisher uri for all connections
238
+ def polisher(uri)
239
+ # XXX do this better
240
+ $polisher_uri = uri
241
+ end
242
+
243
+ # Retrieve list of all projects, invoking yield w/ each, before returning the list
244
+ def projects
245
+ projects = Polisher::Project.all
246
+ projects.each { |project| yield project if block_given? }
247
+ return projects
248
+ end
249
+
250
+ # Find or create new project w/ specified args
251
+ def project(args = {})
252
+ projects { |project|
253
+ project = nil if (args.has_key?(:name) && args[:name] != project.name) ||
254
+ (args.has_key?(:id) && args[:id] != project.id)
255
+ unless project.nil?
256
+ yield project if block_given?
257
+ return project
258
+ end
259
+ }
260
+ proj = Polisher::Project.new args
261
+ proj.create
262
+ proj = project(args)
263
+ yield proj if block_given?
264
+ return proj
265
+ end
266
+
267
+ # Retrieve list of all sources, invoking yield w/ each, before returning the list
268
+ def sources
269
+ sources = Polisher::Source.all
270
+ sources.each { |source| yield source if block_given? }
271
+ return sources
272
+ end
273
+
274
+ # Find or create new source w/ specified args
275
+ def source(args = {})
276
+ sources { |src|
277
+ src = nil if (args.has_key?(:name) && args[:name] != src.name) ||
278
+ (args.has_key?(:id) && args[:id] != src.id) ||
279
+ (args.has_key?(:source_type) && args[:source_type] != src.source_type) ||
280
+ (args.has_key?(:uri) && args[:uri] != src.uri)
281
+ unless src.nil?
282
+ yield src if block_given?
283
+ return src
284
+ end
285
+ }
286
+ src = Polisher::Source.new args
287
+ src.create
288
+ src = source(args)
289
+ src.primary_source = args[:primary_source] if args.has_key?(:primary_source) # XXX ugly hack
290
+ yield src if block_given?
291
+ return src
292
+ end