polisher 0.3 → 0.4

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.
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