metaforce 0.2.0.alpha → 0.3.0.alpha

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.
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ script: bundle exec rspec spec
data/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # Metaforce
2
+ ![travis-ci](https://secure.travis-ci.org/ejholmes/metaforce.png)
3
+
2
4
  Metaforce is a Ruby gem for interacting with the [Salesforce Metadata API](http://www.salesforce.com/us/developer/docs/api_meta/index.htm).
3
5
  The goal of this project is to make the [Migration Tool](http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_deploying_ant.htm) obsolete, favoring Rake over Ant.
4
6
 
5
- Metaforce is in active development and is currently in alpha status. Don't use
6
- it to deploy code to production instances. You've been warned!
7
+ **Metaforce is in active development and is currently in alpha status. Don't use
8
+ it to deploy code to production instances. You've been warned!**
7
9
 
8
10
  ## Installation
9
11
  ```bash
10
- gem install metaforce -v '0.2.0.alpha'
12
+ gem install metaforce --pre
11
13
  ```
12
14
 
13
15
  ## Usage
@@ -22,7 +24,7 @@ client.describe
22
24
  client.list(:type => "CustomObject")
23
25
  # => [{ :created_by_id => "005U0000000EGpcIAG", :created_by_name => "Eric Holmes", ... }]
24
26
 
25
- deployment = client.deploy(File.expand_path("../src"))
27
+ deployment = client.deploy(File.dirname(__FILE__))
26
28
  # => #<Metaforce::Transaction:0x00000102779bf8 @id="04sU0000000WNWoIAO" @type=:deploy>
27
29
 
28
30
  deployment.done?
@@ -32,15 +34,61 @@ deployment.result(:wait_until_done => true)
32
34
  # => { :id => "04sU0000000WNWoIAO", :messages => [{ :changed => true ... :success => true }
33
35
  ```
34
36
 
37
+ ## DSL
38
+ Metaforce includes a lightweight DSL to make deployments and retrieve's easier.
39
+
40
+ ```ruby
41
+ require "metaforce/dsl"
42
+ include Metaforce::DSL::Metadata
43
+
44
+ login :username => 'username', :password => 'password', :security_token => 'security token' do
45
+
46
+ deploy File.dirname(__FILE__) do |result|
47
+ puts "Successful deployment!"
48
+ puts result
49
+ end
50
+
51
+ retrieve File.expand_path("../src/package.xml", __FILE__) |result, zip|
52
+ puts "Successful retrieve!"
53
+ puts result
54
+ File.open("retrieve.zip", "wb") do |file|
55
+ file.write(zip)
56
+ end
57
+ end
58
+
59
+ retrieve File.expand_path("../src/package.xml", __FILE__), :to => "directory"
60
+ end
61
+ ```
62
+
35
63
  ## Roadmap
36
64
  This gem is far from being feature complete. Here's a list of things that still
37
65
  need to be done.
38
66
 
39
- * Implement .retrieve for retrieving metadata.
67
+ * <del>Implement .retrieve for retrieving metadata.</del>
40
68
  * Implement CRUD based calls <http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_crud_based_calls_intro.htm>.
41
69
  * Implement some helper methods for diffing metadata.
70
+ * <del>Implement a DSL.</del>
42
71
  * And some other stuff that I haven't quite thought of yet...
43
72
 
73
+ ## Contributing
74
+ If you'd like to contribute code, please fork the repository and implement your
75
+ feature on a new branch, then send me a pull request with a detailed
76
+ description. Please provide applicable rspec specs.
77
+
78
+ ## Version History
79
+ **0.3.0.alpha** (January 29, 2012)
80
+
81
+ * Ability to retrieve metadata from an organization.
82
+ * Added a DSL.
83
+
84
+ **0.2.0.alpha** (January 28, 2012)
85
+
86
+ * Gem now supports interacting with the metadata api.
87
+
88
+ **0.1.0.alpha** (January 10, 2012)
89
+
90
+ * Ability to parse and modify package.xml files easily.
91
+
44
92
  ## License
45
93
  Copyright (C) 2012 Eric Holmes
46
94
 
@@ -7,14 +7,22 @@ require 'ostruct'
7
7
  module Metaforce
8
8
  module Metadata
9
9
  class Client
10
- DEPLOY_ZIP = 'deploy.zip'
11
- RETRIEVE_ZIP = 'retrieve.zip'
10
+ DEPLOY_ZIP = 'deploy.zip' # :nodoc:
11
+ RETRIEVE_ZIP = 'retrieve.zip' # :nodoc:
12
12
 
13
+ # Performs a login and sets the session_id and metadata_server_url.
14
+ # _options_ should be hash containing the :username, :password and
15
+ # :security_token keys.
16
+ #
17
+ # Metaforce::Metadata::Client.new :username => "username",
18
+ # :password => "password",
19
+ # :security_token => "security token"
13
20
  def initialize(options=nil)
14
21
  @session = Services::Client.new(options).session
15
22
  @client = Savon::Client.new File.expand_path("../../../../wsdl/#{Metaforce.configuration.api_version}/metadata.xml", __FILE__) do |wsdl|
16
23
  wsdl.endpoint = @session[:metadata_server_url]
17
24
  end
25
+ @client.http.auth.ssl.verify_mode = :none
18
26
  @header = {
19
27
  "ins0:SessionHeader" => {
20
28
  "ins0:sessionId" => @session[:session_id]
@@ -24,11 +32,10 @@ module Metaforce
24
32
 
25
33
  # Specify an array of component types to list
26
34
  #
27
- # example:
28
- # [
29
- # { :type => "ApexClass" },
30
- # { :type => "ApexComponent" }
31
- # ]
35
+ # [
36
+ # { :type => "ApexClass" },
37
+ # { :type => "ApexComponent" }
38
+ # ]
32
39
  def list(queries=[])
33
40
  unless queries.is_a?(Array)
34
41
  queries = [ queries ]
@@ -73,10 +80,12 @@ module Metaforce
73
80
  self.status(id)[:done]
74
81
  end
75
82
 
76
- # Deploys dir to the organisation
77
- def deploy(dir, options={})
78
- options = OpenStruct.new options
79
-
83
+ # Deploys _dir_ to the organisation
84
+ #
85
+ # See http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_deploy.htm#deploy_options
86
+ # for a list of _deploy_options_. Options should be convereted from
87
+ # camelCase to an :underscored_symbol.
88
+ def deploy(dir, deploy_options={})
80
89
  if dir.is_a?(String)
81
90
  filename = File.join(File.dirname(dir), DEPLOY_ZIP)
82
91
  zip_contents = create_deploy_file(filename, dir)
@@ -84,18 +93,51 @@ module Metaforce
84
93
  zip_contents = Base64.encode64(dir.read)
85
94
  end
86
95
 
87
- yield options if block_given?
88
-
89
96
  response = @client.request(:deploy) do |soap|
90
97
  soap.header = @header
91
98
  soap.body = {
92
99
  :zip_file => zip_contents,
93
- :deploy_options => options.marshal_dump
100
+ :deploy_options => deploy_options
94
101
  }
95
102
  end
96
103
  Transaction.deployment self, response[:deploy_response][:result][:id]
97
104
  end
98
105
 
106
+ # Performs a retrieve
107
+ #
108
+ # See http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_retrieve_request.htm
109
+ # for a list of _retrieve_request_ options. Options should be convereted from
110
+ # camelCase to an :underscored_symbol.
111
+ def retrieve(retrieve_request={})
112
+ response = @client.request(:retrieve) do |soap|
113
+ soap.header = @header
114
+ soap.body = {
115
+ :retrieve_request => retrieve_request
116
+ }
117
+ end
118
+ Transaction.retrieval self, response[:retrieve_response][:result][:id]
119
+ end
120
+
121
+ # Retrieves files specified in the manifest file (package.xml)
122
+ #
123
+ # Specificy any extra _retrieve_request_ options in _extra_.
124
+ def retrieve_unpackaged(manifest, extra=nil)
125
+ if manifest.is_a?(Metaforce::Manifest)
126
+ package = manifest.to_package
127
+ elsif manifest.is_a?(String)
128
+ package = Metaforce::Manifest.new(File.open(manifest).read).to_package
129
+ end
130
+ retrieve_request = {
131
+ :api_version => Metaforce.configuration.api_version,
132
+ :single_package => true,
133
+ :unpackaged => {
134
+ :types => package
135
+ }
136
+ }
137
+ retrieve_request.merge!(extra) if extra.is_a?(Hash)
138
+ retrieve(retrieve_request)
139
+ end
140
+
99
141
  private
100
142
 
101
143
  # Creates the deploy file, reads in the contents and returns the base64
@@ -3,8 +3,13 @@ require 'savon'
3
3
  module Metaforce
4
4
  module Services
5
5
  class Client
6
+ # Contains the session_id and metadata_server_url
6
7
  attr_reader :session
7
8
 
9
+ # Initializes a new instance of Client and logs in. _options_ should be a
10
+ # hash containing the username, password and security token. If options
11
+ # is nil, it will get the username, password and security token from the
12
+ # configuration.
8
13
  def initialize(options=nil)
9
14
  options = {
10
15
  :username => Metaforce.configuration.username,
@@ -14,11 +19,13 @@ module Metaforce
14
19
  @session = self.login(options[:username], options[:password], options[:security_token])
15
20
  end
16
21
 
22
+ # Performs a login and sets @session
17
23
  def login(username, password, security_token=nil)
18
24
  password = "#{password}#{security_token}" unless security_token.nil?
19
25
  client = Savon::Client.new File.expand_path("../../../../wsdl/#{Metaforce.configuration.api_version}/partner.xml", __FILE__) do |wsdl|
20
26
  wsdl.endpoint = wsdl.endpoint.to_s.sub(/login/, 'test') if Metaforce.configuration.test
21
27
  end
28
+ client.http.auth.ssl.verify_mode = :none
22
29
  response = client.request(:login) do
23
30
  soap.body = {
24
31
  :username => username,
@@ -1,3 +1,5 @@
1
+ require 'base64'
2
+
1
3
  module Metaforce
2
4
 
3
5
  # Convenience class for deployment/retrieval results
@@ -26,11 +28,34 @@ module Metaforce
26
28
  end
27
29
  alias :complete? :done?
28
30
  alias :completed? :done?
31
+
32
+ # Returns the zip file from a retrieval
33
+ def zip_file
34
+ raise "Request was not a retrieve." if @type != :retrieve
35
+ Base64.decode64(@result[:zip_file])
36
+ end
37
+
38
+ # Unzips the returned zip file to _destination_
39
+ def unzip(destination)
40
+ zip = zip_file
41
+ file = Tempfile.new("retrieve")
42
+ file.write(zip)
43
+ path = file.path
44
+ file.close
45
+
46
+ Zip::ZipFile.open(path) do |zip|
47
+ zip.each do |f|
48
+ path = File.join(destination, f.name)
49
+ FileUtils.mkdir_p(File.dirname(path))
50
+ zip.extract(f, path)
51
+ end
52
+ end
53
+ end
29
54
 
30
55
  # Returns the deploy or retrieve result
31
56
  def result(options={})
32
57
  self.wait_until_done if options[:wait_until_done]
33
- raise "Request is not complete! Be sure to call .done? first!" unless @done
58
+ raise "Request has not completed." unless @done
34
59
  @result = @client.status(@id, @type) if @result.nil?
35
60
  @result
36
61
  end
@@ -1,19 +1,35 @@
1
1
  module Metaforce
2
2
  class << self
3
+ # Returns the current Configuration
4
+ #
5
+ # Metaforce.configuration.username = "username"
6
+ # Metaforce.configuration.password = "password"
3
7
  def configuration
4
8
  @configuration ||= Configuration.new
5
9
  end
6
10
 
11
+ # Yields the Configuration
12
+ #
13
+ # Metaforce.configure do |config|
14
+ # config.username = "username"
15
+ # config.password = "password"
16
+ # end
7
17
  def configure
8
18
  yield configuration
9
19
  end
10
20
  end
11
21
 
12
22
  class Configuration
23
+ # The Salesforce API version to use. Defaults to 23.0
13
24
  attr_accessor :api_version
25
+ # The username to use during login.
14
26
  attr_accessor :username
27
+ # The password to use during login.
15
28
  attr_accessor :password
29
+ # The security token to use during login.
16
30
  attr_accessor :security_token
31
+ # Set this to true if you're authentication with a Sandbox instance.
32
+ # Defaults to false.
17
33
  attr_accessor :test
18
34
 
19
35
  def initialize
@@ -0,0 +1,42 @@
1
+ require 'tempfile'
2
+ require 'zip/zip'
3
+
4
+ module Metaforce
5
+ module DSL
6
+ module Metadata
7
+
8
+ # Logs in and creates a new instance of Metaforce::Metadata::Client
9
+ def login(options)
10
+ @client = Metaforce::Metadata::Client.new options
11
+ yield if block_given?
12
+ end
13
+
14
+ # Deploy the contents of _dir_ to the target organization
15
+ def deploy(dir, options={})
16
+ deployment = @client.deploy(dir, options)
17
+ result = deployment.result(:wait_until_done => true)
18
+ raise "Deploy failed." if !result[:success]
19
+ yield result if block_given?
20
+ end
21
+
22
+ # Retrieve the metadata specified in the manifest file. If manifest is a
23
+ # Hash, the underlying .retrieve method is used instead of
24
+ # .retrieve_unpackaged.
25
+ #
26
+ # If _options_ # contains a key _:to_, the resulting zip
27
+ # file that is retrieved is unzipped to the directory specified.
28
+ def retrieve(manifest, options={})
29
+ if manifest.is_a?(Hash)
30
+ retrieval = @client.retrieve(manifest)
31
+ else
32
+ retrieval = @client.retrieve_unpackaged(manifest)
33
+ end
34
+ result = retrieval.result(:wait_until_done => true)
35
+ zip_contents = retrieval.zip_file
36
+ retrieval.unzip(options[:to]) if options.has_key?(:to)
37
+ yield result, zip_contents if block_given?
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -4,16 +4,38 @@ module Metaforce
4
4
  class Manifest
5
5
  SFDC_API_VERSION = "23.0"
6
6
 
7
- # example format
8
- # {
9
- # :apex_class => [
10
- # "TestController",
11
- # "TestClass"
12
- # ],
13
- # :apex_component => [
14
- # "SiteLogin"
15
- # ]
16
- # }
7
+ # Initializes a new instance of a manifest (package.xml) file.
8
+ #
9
+ # It can either take a hash:
10
+ # {
11
+ # :apex_class => [
12
+ # "TestController",
13
+ # "TestClass"
14
+ # ],
15
+ # :apex_component => [
16
+ # "SiteLogin"
17
+ # ]
18
+ # }
19
+ #
20
+ # Or an xml string containing the contents of a packge.xml file:
21
+ # <?xml version="1.0"?>
22
+ # <Package xmlns="http://soap.sforce.com/2006/04/metadata">
23
+ # <types>
24
+ # <members>TestClass</members>
25
+ # <members>AnotherClass</members>
26
+ # <name>ApexClass</name>
27
+ # </types>
28
+ # <types>
29
+ # <members>Component</members>
30
+ # <name>ApexComponent</name>
31
+ # </types>
32
+ # <types>
33
+ # <members>Assets</members>
34
+ # <name>StaticResource</name>
35
+ # </types>
36
+ # <version>23.0</version>
37
+ # </Package>
38
+ #
17
39
  def initialize(components={})
18
40
  # Map component type => folder
19
41
  if components.is_a?(Hash)
@@ -25,6 +47,8 @@ module Metaforce
25
47
  end
26
48
 
27
49
  # Adds components to the package
50
+ #
51
+ # manifest.add :apex_class, 'SomeClass'
28
52
  def add(type, members=nil)
29
53
  unless members.nil?
30
54
  @components[type] = [] if @components[type].nil?
@@ -38,6 +62,8 @@ module Metaforce
38
62
  end
39
63
 
40
64
  # Removes components from the package
65
+ #
66
+ # manifest.remove :apex_class, 'SomeClass'
41
67
  def remove(type, members=nil)
42
68
  unless members.nil?
43
69
  members = [members] if members.is_a?(String)
@@ -53,6 +79,8 @@ module Metaforce
53
79
  end
54
80
 
55
81
  # Filters the components based on a list of files
82
+ #
83
+ # manifest.only(['classes/SomeClass'])
56
84
  def only(files)
57
85
  components = @components
58
86
  @components = {}
@@ -72,23 +100,41 @@ module Metaforce
72
100
  end
73
101
 
74
102
  # Returns the components name
75
- def component_name(key)
103
+ def component_name(key) # :nodoc:
76
104
  COMPONENT_TYPE_MAP[key][:name]
77
105
  end
78
106
 
79
107
  # Returns the components folder
80
- def component_folder(key)
108
+ def component_folder(key) # :nodoc:
81
109
  COMPONENT_TYPE_MAP[key][:folder]
82
110
  end
83
111
 
84
112
  # Returns a key for the component name
85
- def component_key(name)
113
+ def component_key(name) # :nodoc:
86
114
  COMPONENT_TYPE_MAP.each do |key, component|
87
115
  return key if component[:name] == name
88
116
  end
89
117
  end
90
118
 
91
119
  # Returns a string containing a package.xml file
120
+ #
121
+ # <?xml version="1.0"?>
122
+ # <Package xmlns="http://soap.sforce.com/2006/04/metadata">
123
+ # <types>
124
+ # <members>TestClass</members>
125
+ # <members>AnotherClass</members>
126
+ # <name>ApexClass</name>
127
+ # </types>
128
+ # <types>
129
+ # <members>Component</members>
130
+ # <name>ApexComponent</name>
131
+ # </types>
132
+ # <types>
133
+ # <members>Assets</members>
134
+ # <name>StaticResource</name>
135
+ # </types>
136
+ # <version>23.0</version>
137
+ # </Package>
92
138
  def to_xml
93
139
  xml_builder = Nokogiri::XML::Builder.new do |xml|
94
140
  xml.Package("xmlns" => "http://soap.sforce.com/2006/04/metadata") {
@@ -106,10 +152,22 @@ module Metaforce
106
152
  xml_builder.to_xml
107
153
  end
108
154
 
155
+ # Returns the underlying hash structure
156
+ #
157
+ # {
158
+ # :apex_class => [
159
+ # "TestController",
160
+ # "TestClass"
161
+ # ],
162
+ # :apex_component => [
163
+ # "SiteLogin"
164
+ # ]
165
+ # }
109
166
  def to_hash
110
167
  @components
111
168
  end
112
169
 
170
+ # Used internall my Metaforce::Metadata::Client
113
171
  def to_package
114
172
  components = []
115
173
  @components.each do |type, members|
@@ -1,3 +1,3 @@
1
1
  module Metaforce
2
- VERSION = "0.2.0.alpha"
2
+ VERSION = "0.3.0.alpha"
3
3
  end
data/metaforce.gemspec CHANGED
@@ -18,11 +18,12 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.add_dependency "nokogiri"
22
- s.add_dependency "savon"
23
- s.add_dependency "rubyzip"
21
+ s.add_dependency "nokogiri", "~> 1.5.0"
22
+ s.add_dependency "savon", "~> 0.9.7"
23
+ s.add_dependency "rubyzip", "~> 0.9.5"
24
24
 
25
+ s.add_development_dependency "rake"
25
26
  s.add_development_dependency "rspec"
26
27
  s.add_development_dependency "mocha"
27
- s.add_development_dependency "savon_spec"
28
+ s.add_development_dependency "savon_spec", "~> 0.1.6"
28
29
  end
@@ -0,0 +1,37 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <soapenv:Body>
4
+ <checkRetrieveStatusResponse>
5
+ <result>
6
+ <fileProperties>
7
+ <createdById>005U0000000EGpcIAG</createdById>
8
+ <createdByName>Eric Holmes</createdByName>
9
+ <createdDate>2012-01-14T22:11:17.000Z</createdDate>
10
+ <fileName>classes/TestClass.cls</fileName>
11
+ <fullName>TestClass</fullName>
12
+ <id>01pU0000000pDrtIAE</id>
13
+ <lastModifiedById>005U0000000EGpcIAG</lastModifiedById>
14
+ <lastModifiedByName>Eric Holmes</lastModifiedByName>
15
+ <lastModifiedDate>2012-01-15T03:17:28.000Z</lastModifiedDate>
16
+ <manageableState>unmanaged</manageableState>
17
+ <type>ApexClass</type>
18
+ </fileProperties>
19
+ <fileProperties>
20
+ <createdById>005U0000000EGpcIAG</createdById>
21
+ <createdByName>Eric Holmes</createdByName>
22
+ <createdDate>2012-01-28T03:35:13.400Z</createdDate>
23
+ <fileName>package.xml</fileName>
24
+ <fullName>package.xml</fullName>
25
+ <id/>
26
+ <lastModifiedById>005U0000000EGpcIAG</lastModifiedById>
27
+ <lastModifiedByName>Eric Holmes</lastModifiedByName>
28
+ <lastModifiedDate>2012-01-28T03:35:13.400Z</lastModifiedDate>
29
+ <manageableState>unmanaged</manageableState>
30
+ <type>Package</type>
31
+ </fileProperties>
32
+ <id>04sU0000000WkdIIAS</id>
33
+ <zipFile>UEsDBBQACAAIAGYcPEAAAAAAAAAAAAAAAAAVAAAAY2xhc3Nlcy9UZXN0Q2xhc3MuY2xzKyhNyslMVkjOSSwuVghJLS5xBrOquWoBUEsHCKYgavMYAAAAGgAAAFBLAwQUAAgACABmHDxAAAAAAAAAAAAAAAAAHgAAAGNsYXNzZXMvVGVzdENsYXNzLmNscy1tZXRhLnhtbK2RTWsCMRCG7/srltzdibZUkWxECj321PY+xtGmbj7IRPHnN3SluwdBD87tffMM80DU6uy6+kSJbfCtmDZS1ORN2Fq/b8Xnx9tkIVa6UutI59cOmevCe27Fd85xCcABY8O7kAw1JjiYSfkC8hkcZdxiRqGruozCaL/6I3r21EgFo6InIpoD7ulSct/+vTj8Cen96DaU9FTBOI4g6wdoXqhRHiiPjrhcIo2dgiH1CnDV4ZFm8rYYcQ6J7pbjjPnIem2yPZWtS6wU/P+Yrn4BUEsHCKXszAvOAAAA5QEAAFBLAwQUAAgACABmHDxAAAAAAAAAAAAAAAAACwAAAHBhY2thZ2UueG1sTY7LCsIwEEX3/YqQvZlYRUTSFBFcu6gfENNRi82DTpD695Y+0FnNGQ53rip717I3dtQEX/C1kJyht6Fu/KPg1+q82vNSZ+pi7Ms8kA22p4I/U4oHAAomCrqHzqKwwUEu5Q7kFhwmU5tkuM7YMCp9ItK0j+zQ3YaXukJKp9YQKVhOP8kbh/oYsZ+Nkac8+AtUc3mdb4RUsFCmYO6ssy9QSwcIe4wRVJ4AAADlAAAAUEsBAhQAFAAIAAgAZhw8QKYgavMYAAAAGgAAABUAAAAAAAAAAAAAAAAAAAAAAGNsYXNzZXMvVGVzdENsYXNzLmNsc1BLAQIUABQACAAIAGYcPECl7MwLzgAAAOUBAAAeAAAAAAAAAAAAAAAAAFsAAABjbGFzc2VzL1Rlc3RDbGFzcy5jbHMtbWV0YS54bWxQSwECFAAUAAgACABmHDxAe4wRVJ4AAADlAAAACwAAAAAAAAAAAAAAAAB1AQAAcGFja2FnZS54bWxQSwUGAAAAAAMAAwDIAAAATAIAAAAA</zipFile>
34
+ </result>
35
+ </checkRetrieveStatusResponse>
36
+ </soapenv:Body>
37
+ </soapenv:Envelope>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <soapenv:Body>
4
+ <retrieveResponse>
5
+ <result>
6
+ <done>false</done>
7
+ <id>04sU0000000WkdIIAS</id>
8
+ <state>InProgress</state>
9
+ </result>
10
+ </retrieveResponse>
11
+ </soapenv:Body>
12
+ </soapenv:Envelope>
@@ -108,17 +108,6 @@ describe Metaforce::Metadata::Client do
108
108
 
109
109
  end
110
110
 
111
- # context "when given a file" do
112
-
113
- # it "deploys the file and returns the id of the result" do
114
- # path = Tempfile.new('zipfile').tap { |f| f.write('h'); f.close }.path
115
- # savon.expects(:deploy).with(:zip_file => "aA==\n", :deploy_options => {}).returns(:in_progress)
116
- # id = client.deploy(File.open(path))
117
- # id.should eq("04sU0000000WNWoIAO")
118
- # end
119
-
120
- # end
121
-
122
111
  it "allows deploy options to be configured via a hash" do
123
112
  savon.expects(:deploy).with(:zip_file => '', :deploy_options => { :run_all_tests => true }).returns(:in_progress)
124
113
  expect {
@@ -126,14 +115,61 @@ describe Metaforce::Metadata::Client do
126
115
  }.to_not raise_error
127
116
  end
128
117
 
129
- it "allows deploy options to be configured via a block" do
130
- savon.expects(:deploy).with(:zip_file => '', :deploy_options => { :run_all_tests => true }).returns(:in_progress)
131
- expect {
132
- client.deploy('') do |options|
133
- options.run_all_tests = true
118
+ end
119
+
120
+ describe ".retrieve" do
121
+
122
+ let(:manifest) do
123
+ Metaforce::Manifest.new(File.open(File.expand_path('../../../fixtures/sample/src/package.xml', __FILE__)).read)
124
+ end
125
+
126
+ describe ".retrieve_unpackaged" do
127
+
128
+ context "when given a manifest file" do
129
+ before(:each) do
130
+ savon.expects(:retrieve).with(:retrieve_request => { :api_version => Metaforce.configuration.api_version, :single_package => true, :unpackaged => { :types => manifest.to_package } }).returns(:in_progress)
131
+ savon.expects(:check_status).with(:ids => ['04sU0000000WkdIIAS']).returns(:done)
132
+ savon.expects(:check_retrieve_status).with(:ids => ['04sU0000000WkdIIAS']).returns(:success)
134
133
  end
135
- }.to_not raise_error
134
+
135
+ it "returns a valid retrieve result" do
136
+ retrieval = client.retrieve_unpackaged(manifest)
137
+ retrieval.done?
138
+ result = retrieval.result
139
+ result.should be_a(Hash)
140
+ end
141
+
142
+ end
143
+
144
+ context "when given the path to a manifest file" do
145
+ before(:each) do
146
+ savon.expects(:retrieve).with(:retrieve_request => { :api_version => Metaforce.configuration.api_version, :single_package => true, :unpackaged => { :types => manifest.to_package } }).returns(:in_progress)
147
+ savon.expects(:check_status).with(:ids => ['04sU0000000WkdIIAS']).returns(:done)
148
+ savon.expects(:check_retrieve_status).with(:ids => ['04sU0000000WkdIIAS']).returns(:success)
149
+ end
150
+
151
+ it "returns a valid retrieve result" do
152
+ retrieval = client.retrieve_unpackaged(File.expand_path('../../../fixtures/sample/src/package.xml', __FILE__))
153
+ retrieval.done?
154
+ result = retrieval.result
155
+ result.should be_a(Hash)
156
+ end
157
+
158
+ end
159
+
160
+ context "when given extra retrieve options" do
161
+ before(:each) do
162
+ savon.expects(:retrieve).with(:retrieve_request => { :api_version => Metaforce.configuration.api_version, :single_package => true, :unpackaged => { :types => manifest.to_package }, :extra => true }).returns(:in_progress)
163
+ end
164
+
165
+ it "merges the options" do
166
+ expect {
167
+ retrieval = client.retrieve_unpackaged(File.expand_path('../../../fixtures/sample/src/package.xml', __FILE__), { :extra => true })
168
+ }.to_not raise_error
169
+ end
170
+
171
+ end
136
172
  end
137
-
173
+
138
174
  end
139
175
  end
@@ -0,0 +1,93 @@
1
+ require "spec_helper"
2
+ require "metaforce/dsl"
3
+ include Metaforce::DSL::Metadata
4
+
5
+ describe Metaforce::DSL::Metadata do
6
+
7
+ let(:manifest) do
8
+ Metaforce::Manifest.new(File.open(File.expand_path('../../fixtures/sample/src/package.xml', __FILE__)).read)
9
+ end
10
+
11
+ let(:valid_credentials) do
12
+ { :username => 'valid', :password => 'password' }
13
+ end
14
+
15
+ describe ".login" do
16
+
17
+ before(:each) do
18
+ savon.expects(:login).with(valid_credentials).returns(:success)
19
+ end
20
+
21
+ it "logs the user in" do
22
+ expect { login valid_credentials }.to_not raise_error
23
+ end
24
+
25
+ end
26
+
27
+ describe ".deploy" do
28
+
29
+ before(:each) do
30
+ Metaforce::Metadata::Client.any_instance.stubs(:create_deploy_file).returns('')
31
+ savon.expects(:login).with(valid_credentials).returns(:success)
32
+ savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
33
+ savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
34
+ savon.expects(:check_deploy_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
35
+ end
36
+
37
+ context "when given a directory" do
38
+
39
+ it "deploys the directory" do
40
+ login valid_credentials
41
+ expect {
42
+ deploy File.expand_path('../../../fixtures/sample', __FILE__) do |result|
43
+ result.should be_a(Hash)
44
+ end
45
+ }.to_not raise_error
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ describe ".retrieve" do
53
+ before(:each) do
54
+ savon.expects(:login).with(valid_credentials).returns(:success)
55
+ savon.expects(:retrieve).with(:retrieve_request => { :api_version => Metaforce.configuration.api_version, :single_package => true, :unpackaged => { :types => manifest.to_package } }).returns(:in_progress)
56
+ savon.expects(:check_status).with(:ids => ['04sU0000000WkdIIAS']).returns(:done)
57
+ savon.expects(:check_retrieve_status).with(:ids => ['04sU0000000WkdIIAS']).returns(:success)
58
+ end
59
+
60
+ context "when given a manifest file" do
61
+
62
+ it "retrieves the components" do
63
+ login valid_credentials
64
+ expect {
65
+ retrieve manifest do |result, zip|
66
+ result.should be_a(Hash)
67
+ zip.should be_a(String)
68
+ end
69
+ }.to_not raise_error
70
+ end
71
+
72
+ context "and :to is specified" do
73
+
74
+ before(:each) do
75
+ Metaforce::Transaction.any_instance.stubs(:unzip).returns('')
76
+ end
77
+
78
+ it "retrieves the components and unzips them to the directory" do
79
+ login valid_credentials
80
+ expect {
81
+ retrieve manifest, :to => "test_retrieve" do |result, zip|
82
+ result.should be_a(Hash)
83
+ zip.should be_a(String)
84
+ end
85
+ }.to_not raise_error
86
+ end
87
+
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metaforce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0.alpha
4
+ version: 0.3.0.alpha
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,44 +9,55 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-27 00:00:00.000000000 Z
12
+ date: 2012-01-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
16
- requirement: &2168826860 !ruby/object:Gem::Requirement
16
+ requirement: &3632840 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
21
+ version: 1.5.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2168826860
24
+ version_requirements: *3632840
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: savon
27
- requirement: &2168825720 !ruby/object:Gem::Requirement
27
+ requirement: &3631640 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
- - - ! '>='
30
+ - - ~>
31
31
  - !ruby/object:Gem::Version
32
- version: '0'
32
+ version: 0.9.7
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2168825720
35
+ version_requirements: *3631640
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rubyzip
38
- requirement: &2168824120 !ruby/object:Gem::Requirement
38
+ requirement: &3630690 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.5
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *3630690
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &3629920 !ruby/object:Gem::Requirement
39
50
  none: false
40
51
  requirements:
41
52
  - - ! '>='
42
53
  - !ruby/object:Gem::Version
43
54
  version: '0'
44
- type: :runtime
55
+ type: :development
45
56
  prerelease: false
46
- version_requirements: *2168824120
57
+ version_requirements: *3629920
47
58
  - !ruby/object:Gem::Dependency
48
59
  name: rspec
49
- requirement: &2168822980 !ruby/object:Gem::Requirement
60
+ requirement: &3629280 !ruby/object:Gem::Requirement
50
61
  none: false
51
62
  requirements:
52
63
  - - ! '>='
@@ -54,10 +65,10 @@ dependencies:
54
65
  version: '0'
55
66
  type: :development
56
67
  prerelease: false
57
- version_requirements: *2168822980
68
+ version_requirements: *3629280
58
69
  - !ruby/object:Gem::Dependency
59
70
  name: mocha
60
- requirement: &2168821960 !ruby/object:Gem::Requirement
71
+ requirement: &3627890 !ruby/object:Gem::Requirement
61
72
  none: false
62
73
  requirements:
63
74
  - - ! '>='
@@ -65,18 +76,18 @@ dependencies:
65
76
  version: '0'
66
77
  type: :development
67
78
  prerelease: false
68
- version_requirements: *2168821960
79
+ version_requirements: *3627890
69
80
  - !ruby/object:Gem::Dependency
70
81
  name: savon_spec
71
- requirement: &2168820680 !ruby/object:Gem::Requirement
82
+ requirement: &3625600 !ruby/object:Gem::Requirement
72
83
  none: false
73
84
  requirements:
74
- - - ! '>='
85
+ - - ~>
75
86
  - !ruby/object:Gem::Version
76
- version: '0'
87
+ version: 0.1.6
77
88
  type: :development
78
89
  prerelease: false
79
- version_requirements: *2168820680
90
+ version_requirements: *3625600
80
91
  description: A Ruby gem for interacting with the Salesforce Metadata API
81
92
  email:
82
93
  - eric@ejholmes.net
@@ -85,7 +96,7 @@ extensions: []
85
96
  extra_rdoc_files: []
86
97
  files:
87
98
  - .gitignore
88
- - .rvmrc
99
+ - .travis.yml
89
100
  - Gemfile
90
101
  - Guardfile
91
102
  - README.md
@@ -96,6 +107,7 @@ files:
96
107
  - lib/metaforce/api/services.rb
97
108
  - lib/metaforce/api/transaction.rb
98
109
  - lib/metaforce/config.rb
110
+ - lib/metaforce/dsl.rb
99
111
  - lib/metaforce/manifest.rb
100
112
  - lib/metaforce/version.rb
101
113
  - metaforce.gemspec
@@ -103,6 +115,7 @@ files:
103
115
  - spec/fixtures/package.xml
104
116
  - spec/fixtures/requests/check_deploy_status/done.xml
105
117
  - spec/fixtures/requests/check_deploy_status/error.xml
118
+ - spec/fixtures/requests/check_retrieve_status/success.xml
106
119
  - spec/fixtures/requests/check_status/done.xml
107
120
  - spec/fixtures/requests/check_status/not_done.xml
108
121
  - spec/fixtures/requests/deploy/in_progress.xml
@@ -110,6 +123,7 @@ files:
110
123
  - spec/fixtures/requests/list_metadata/objects.xml
111
124
  - spec/fixtures/requests/login/failure.xml
112
125
  - spec/fixtures/requests/login/success.xml
126
+ - spec/fixtures/requests/retrieve/in_progress.xml
113
127
  - spec/fixtures/sample/src/classes/TestClass.cls
114
128
  - spec/fixtures/sample/src/classes/TestClass.cls-meta.xml
115
129
  - spec/fixtures/sample/src/package.xml
@@ -117,6 +131,7 @@ files:
117
131
  - spec/lib/api/services_spec.rb
118
132
  - spec/lib/api/transaction_spec.rb
119
133
  - spec/lib/config_spec.rb
134
+ - spec/lib/dsl_spec.rb
120
135
  - spec/lib/manifest_spec.rb
121
136
  - spec/spec_helper.rb
122
137
  - wsdl/23.0/metadata.xml
@@ -149,6 +164,7 @@ test_files:
149
164
  - spec/fixtures/package.xml
150
165
  - spec/fixtures/requests/check_deploy_status/done.xml
151
166
  - spec/fixtures/requests/check_deploy_status/error.xml
167
+ - spec/fixtures/requests/check_retrieve_status/success.xml
152
168
  - spec/fixtures/requests/check_status/done.xml
153
169
  - spec/fixtures/requests/check_status/not_done.xml
154
170
  - spec/fixtures/requests/deploy/in_progress.xml
@@ -156,6 +172,7 @@ test_files:
156
172
  - spec/fixtures/requests/list_metadata/objects.xml
157
173
  - spec/fixtures/requests/login/failure.xml
158
174
  - spec/fixtures/requests/login/success.xml
175
+ - spec/fixtures/requests/retrieve/in_progress.xml
159
176
  - spec/fixtures/sample/src/classes/TestClass.cls
160
177
  - spec/fixtures/sample/src/classes/TestClass.cls-meta.xml
161
178
  - spec/fixtures/sample/src/package.xml
@@ -163,5 +180,6 @@ test_files:
163
180
  - spec/lib/api/services_spec.rb
164
181
  - spec/lib/api/transaction_spec.rb
165
182
  - spec/lib/config_spec.rb
183
+ - spec/lib/dsl_spec.rb
166
184
  - spec/lib/manifest_spec.rb
167
185
  - spec/spec_helper.rb
data/.rvmrc DELETED
@@ -1,55 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
- # development environment upon cd'ing into the directory
5
-
6
- # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
- environment_id="ruby-1.9.2-p290@metaforce"
8
-
9
- #
10
- # Uncomment following line if you want options to be set only for given project.
11
- #
12
- # PROJECT_JRUBY_OPTS=( --1.9 )
13
-
14
- #
15
- # First we attempt to load the desired environment directly from the environment
16
- # file. This is very fast and efficient compared to running through the entire
17
- # CLI and selector. If you want feedback on which environment was used then
18
- # insert the word 'use' after --create as this triggers verbose mode.
19
- #
20
- if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
21
- && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
22
- then
23
- \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
24
-
25
- if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
26
- then
27
- . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
28
- fi
29
- else
30
- # If the environment file has not yet been created, use the RVM CLI to select.
31
- if ! rvm --create "$environment_id"
32
- then
33
- echo "Failed to create RVM environment '${environment_id}'."
34
- return 1
35
- fi
36
- fi
37
-
38
- #
39
- # If you use an RVM gemset file to install a list of gems (*.gems), you can have
40
- # it be automatically loaded. Uncomment the following and adjust the filename if
41
- # necessary.
42
- #
43
- # filename=".gems"
44
- # if [[ -s "$filename" ]]
45
- # then
46
- # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
47
- # fi
48
-
49
- # If you use bundler, this might be useful to you:
50
- # if command -v bundle && [[ -s Gemfile ]]
51
- # then
52
- # bundle install
53
- # fi
54
-
55
-