metaforce 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
1
  source "http://rubygems.org"
2
+ gem 'highline'
3
+ gem 'thor'
2
4
 
3
5
  # Specify your gem's dependencies in metaforce.gemspec
4
6
  gemspec
data/README.md CHANGED
@@ -61,6 +61,10 @@ feature on a new branch, then send me a pull request with a detailed
61
61
  description. Please provide applicable rspec specs.
62
62
 
63
63
  ## Version History
64
+ **HEAD**
65
+
66
+ * Add thor integration.
67
+
64
68
  **0.5.0** (March 23, 2012)
65
69
 
66
70
  * Implemented CRUD calls.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require './lib/metaforce/thor/metaforce'
4
+
5
+ MetaForce.new :shell => Thor::Shell::Color.new
6
+ MetaForce.start
@@ -5,3 +5,5 @@ require 'metaforce/error'
5
5
  require 'metaforce/manifest'
6
6
  require 'metaforce/services'
7
7
  require 'metaforce/metadata'
8
+ require 'metaforce/custom_actions'
9
+ require 'metaforce/login_details'
@@ -2,6 +2,13 @@ module Metaforce
2
2
  module CoreExtensions
3
3
  module String
4
4
 
5
+ def camelcase
6
+ str = dup
7
+ str.gsub!(/^[a-z]|_[a-z]/) { |a| a.upcase }
8
+ str.gsub!('_', '')
9
+ str
10
+ end unless method_defined?(:camelcase)
11
+
5
12
  def lower_camelcase
6
13
  str = dup
7
14
  str.gsub!(/_[a-z]/) { |a| a.upcase }
@@ -0,0 +1,29 @@
1
+ class Thor
2
+ module Actions
3
+ module CustomActions
4
+ require 'highline'
5
+
6
+ def masked_ask(question)
7
+ password = HighLine.new.ask(question) { |q| q.echo = '*' }
8
+ end
9
+
10
+ def spinner(&block)
11
+ return unless block_given?
12
+ chars = %w{ | / - \\ }
13
+
14
+ t = Thread.new { yield if block_given? }
15
+ while t.alive?
16
+ print chars[0]
17
+ sleep 0.1
18
+ print "\b"
19
+
20
+ chars.push chars.shift
21
+ end
22
+
23
+ t.join
24
+ t.value
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ class LoginDetails
2
+ require 'yaml'
3
+
4
+ attr_accessor :username, :password, :security_token, :sandbox, :log
5
+ STORAGE_LOCATION = "#{Dir.pwd}/.git/force.com.config"
6
+
7
+ def initialize(user, pass, security_token, sandbox, log = nil)
8
+ @username = user
9
+ @password = pass
10
+ @security_token = security_token
11
+ @sandbox = sandbox
12
+ @log = log
13
+ end
14
+
15
+ def save!
16
+ File.open(STORAGE_LOCATION, "w+") do |file|
17
+ file.print Marshal::dump(self)
18
+ end
19
+ end
20
+
21
+ def self.load
22
+ $/="---_---" #record separator
23
+ File.open(STORAGE_LOCATION, "r") do |object|
24
+ return Marshal::load(object)
25
+ end
26
+ end
27
+
28
+ end
@@ -178,7 +178,7 @@ module Metaforce
178
178
  :retrieve_request => options[:options] || {}
179
179
  }
180
180
  end
181
- Transaction.retrieval self, response[:retrieve_response][:result][:id]
181
+ Transaction.retrieval(self, response[:retrieve_response][:result][:id])
182
182
  end
183
183
 
184
184
  # Retrieves files specified in the manifest file (package.xml). Specificy any extra options in +options[:options]+.
@@ -20,25 +20,66 @@ module Metaforce
20
20
  :security_token => Metaforce.configuration.security_token
21
21
  } if options.nil?
22
22
  @session = self.login(options[:username], options[:password], options[:security_token])
23
+
24
+ @client = Savon::Client.new File.expand_path("../../../../wsdl/#{Metaforce.configuration.api_version}/partner.xml", __FILE__) do |wsdl|
25
+ wsdl.endpoint = @session[:services_url]
26
+ end
27
+ @client.http.auth.ssl.verify_mode = :none
28
+ @header = {
29
+ "ins0:SessionHeader" => {
30
+ "ins0:sessionId" => @session[:session_id]
31
+ }
32
+ }
23
33
  end
24
34
 
25
35
  # Performs a login and sets @session
26
36
  def login(username, password, security_token=nil)
27
37
  password = "#{password}#{security_token}" unless security_token.nil?
28
- client = Savon::Client.new File.expand_path("../../../../wsdl/#{Metaforce.configuration.api_version}/partner.xml", __FILE__) do |wsdl|
38
+ @client = Savon::Client.new File.expand_path("../../../../wsdl/#{Metaforce.configuration.api_version}/partner.xml", __FILE__) do |wsdl|
29
39
  wsdl.endpoint = wsdl.endpoint.to_s.sub(/login/, 'test') if Metaforce.configuration.test
30
40
  Metaforce.log("Logging in via #{wsdl.endpoint.to_s}")
31
41
  end
32
- client.http.auth.ssl.verify_mode = :none
42
+ @client.http.auth.ssl.verify_mode = :none
33
43
 
34
- response = client.request(:login) do
44
+ response = @client.request(:login) do
35
45
  soap.body = {
36
46
  :username => username,
37
47
  :password => password
38
48
  }
39
49
  end
40
50
  { :session_id => response.body[:login_response][:result][:session_id],
41
- :metadata_server_url => response.body[:login_response][:result][:metadata_server_url] }
51
+ :metadata_server_url => response.body[:login_response][:result][:metadata_server_url],
52
+ :services_url => response.body[:login_response][:result][:server_url] }
53
+ end
54
+
55
+ # Returns the layout metadata for the sobject.
56
+ # If a +record_type_id+ is passed in, it will only return the layout for
57
+ # that record type.
58
+ #
59
+ # This method is really useful finding out picklist values that are
60
+ # available for a certain record type
61
+ #
62
+ # == Examples
63
+ #
64
+ # @picklists_for_record_type = client.describe_layout('Account', '0123000000100Rn')[:record_type_mappings][:picklists_for_record_type]
65
+ #
66
+ # def picklist_values_for(field)
67
+ # picklist_values = @picklists_for_record_type.select { |f| f[:picklist_name] == field }.first[:picklist_values]
68
+ # picklist_values.select { |p| p[:active] }.collect { |p| [ p[:label], p[:value] ] }
69
+ # end
70
+ #
71
+ # picklist_values_for('some_field__c')
72
+ # # => [ ['label1', 'value1'], ['label2', 'value2'] ]
73
+ def describe_layout(sobject, record_type_id=nil)
74
+ body = {
75
+ 'sObjectType' => sobject
76
+ }
77
+ body['recordTypeID'] = record_type_id if record_type_id
78
+ response = @client.request(:describe_layout) do |soap|
79
+ soap.header = @header
80
+ soap.body = body
81
+ end
82
+ response.body[:describe_layout_response][:result]
42
83
  end
43
84
  end
44
85
  end
@@ -0,0 +1,117 @@
1
+ require 'thor' #should probably be in say, lib/metaforce.rb, but I don't want to make that decision alone.
2
+
3
+ class MetaForce < Thor
4
+ include Thor::Actions
5
+ require './lib/metaforce/custom_actions'
6
+ require './lib/metaforce/login_details'
7
+ require './lib/metaforce'
8
+ include Thor::Actions::CustomActions
9
+
10
+ ######## Tasks ############################################################
11
+ desc "login", "accepts login parameters and completes a soap call to salesforce via the partner api."
12
+ method_option :reset, :type => :boolean, :aliases => "-r", :required => false, :banner => " Reset stored login information"
13
+ def login
14
+ if ((File.exists? LoginDetails::STORAGE_LOCATION) && (options[:reset].nil?))
15
+ say "Using stored login information"
16
+ @login_details = LoginDetails.load
17
+ else
18
+ login_email = ask "Login Email address: "
19
+ sandbox = yes? "Is this a sandbox org login: "
20
+ login_pass = masked_ask "Login Password: "
21
+ login_security_token = masked_ask "Security Token: "
22
+ say "If you'd like I can save this login information to this directories .git/config/force.com.config"
23
+ say "----- PLEASE NOTE, HOWEVER, THAT SAVING THIS INFORMATION IS INSECURE AND IS IN NO WAY ENCRYPTED"
24
+ save = yes? "Should I save this Login information? (y/n) "
25
+
26
+ if save
27
+ @login_details = LoginDetails.new(login_email, login_pass, login_security_token, sandbox)
28
+ @login_details.save!
29
+ else
30
+ @login_details = LoginDetails.new(login_email, login_pass, login_security_token, sandbox)
31
+ end
32
+ end
33
+
34
+ raise "Failed to find viable login information!" if @login_details.nil?
35
+ Metaforce.log = @login_details.log
36
+ Metaforce.configuration.test = @login_details.sandbox
37
+ @client = Metaforce::Metadata::Client.new :username => @login_details.username,
38
+ :password => @login_details.password,
39
+ :security_token => @login_details.security_token
40
+ end
41
+
42
+ ######## Deploy ###########################################################
43
+ desc "deploy", "deploys the current working directory's src folder to salesforce"
44
+ method_option :dir, :type => :string, :aliases => "-d", :required => true, :default => "src", :banner => "Specify the directory to deploy"
45
+ method_option :reset, :type => :boolean, :aliases => "-r", :required => false, :banner => " Reset stored login information"
46
+ def deploy
47
+ login unless @client
48
+ login unless options[:reset].nil?
49
+ raise "failed to create connection!" unless @client
50
+ deploy_results = @client.deploy(options[:dir]).result
51
+ say "Deploy Successful!", color = Thor::Shell::Color::GREEN if deploy_results[:success]
52
+ end
53
+
54
+ ######## RunTests #########################################################
55
+ desc "test", "runs _all_ salesforce unit tests!"
56
+ method_option :dir, :type => :string, :aliases => "-d", :required => true, :default => "src", :banner => "Specify the directory to execute tests"
57
+ method_option :reset, :type => :boolean, :aliases => "-r", :required => false, :banner => " Reset stored login information"
58
+ def test
59
+ login unless @client
60
+ login unless options[:reset].nil?
61
+ raise "failed to create connection!" unless @client
62
+ result = spinner {
63
+ result = @client.deploy(options[:dir], :options => { :run_all_tests => true }).result
64
+ }
65
+ failures = result[:run_test_result][:failures]
66
+ if failures
67
+ failures = [failures] unless failures.responds_to? :each
68
+ say "--- FAILURES: ", color = Thor::Shell::Color::RED
69
+ failures.each_with_index do |f,i|
70
+ say "#{"-" * 80}", color = Thor::Shell::Color::YELLOW
71
+ say ""
72
+ say "\t(#{index +1}) #{failure[:method_name]}"
73
+ say "\t\t#{failure[:message]}", color = Thor::Shell::Color::RED
74
+ say ""
75
+ say "\t\t#{failure[:stack_trace]}", color => Thor::Shell::Color::MAGENTA
76
+ say ""
77
+ end
78
+ end
79
+
80
+ color = (failures) ? Thor::Shell::Color::RED : Thor::Shell::Color::GREEN
81
+ say "Finished in #{Float(result[:run_test_result][:total_time]) / 100} seconds"
82
+ say "#{result[:run_test_result][:num_tests_run]} tests, #{result[:run_test_result][:num_failures]} failures", color = color
83
+ end
84
+
85
+ ######## Pull #############################################################
86
+ desc "pull", "Pull all MetaData objects specified in the package.xml file"
87
+ method_option :reset, :type => :boolean, :aliases => "-r", :required => false, :banner => " Reset stored login information"
88
+ method_option :manifest, :type => :string, :aliases => "-m", :required => true, :banner => " Path to Manifest.xml", :default => "src/package.xml"
89
+ method_option :dir, :type => :string, :aliases => "-d", :required => false, :banner => " Download to directory", :default => "retrieved"
90
+ def pull
91
+ login unless @client
92
+ login unless options[:reset].nil?
93
+ raise "failed to create connection!" unless @client
94
+ result = spinner {
95
+ @client.retrieve_unpackaged(options[:manifest]).to(options[:dir])
96
+ }
97
+ say "Files retrieved sucessfully to #{@directory}", color = Thor::Shell::Color::GREEN
98
+ end
99
+
100
+ ######## Clone ############################################################
101
+ desc "clone", "Pull all MetaData objects from the current org"
102
+ method_option :reset, :type => :boolean, :aliases => "-r", :required => false, :banner => " Reset stored login information"
103
+ method_option :dir, :type => :string, :aliases => "-d", :required => true, :banner => " Download to directory", :default => "retrieved"
104
+ def clone
105
+ login unless @client
106
+ login unless options[:reset].nil?
107
+ raise "failed to create connection!" unless @client
108
+ result = spinner {
109
+ metadata_objects = Hash.new
110
+ @client.metadata_objects.collect { |t| metadata_objects[t[:xml_name].underscore.to_sym] = ["*"] }
111
+ md = Metaforce::Manifest.new(metadata_objects)
112
+ @client.retrieve_unpackaged(md).to(options[:dir])
113
+ }
114
+ say "Files retrieved sucessfully to #{options[:dir]}", color = Thor::Shell::Color::GREEN
115
+ end
116
+
117
+ end
@@ -32,10 +32,10 @@ module Metaforce
32
32
  :folder => "objects",
33
33
  :plural => :action_overrides
34
34
  },
35
- :analytics_snapshot => {
35
+ :analytic_snapshot => {
36
36
  :name => "AnalyticsSnapshot",
37
37
  :folder => "analyticsnapshots",
38
- :plural => :analytics_snapshots
38
+ :plural => :analytic_snapshots
39
39
  },
40
40
  :apex_class => {
41
41
  :name => "ApexClass",
@@ -142,6 +142,11 @@ module Metaforce
142
142
  :folder => "homePageComponents",
143
143
  :plural => :home_page_components
144
144
  },
145
+ :home_page_layout => {
146
+ :name => "HomePageLayout",
147
+ :folder => "HomePageLayouts",
148
+ :plural => :home_page_layouts
149
+ },
145
150
  :layout => {
146
151
  :name => "Layout",
147
152
  :folder => "layouts",
@@ -197,10 +202,10 @@ module Metaforce
197
202
  :folder => "reportTypes",
198
203
  :plural => :report_types
199
204
  },
200
- :scontroler => {
201
- :name => "Scontroler",
205
+ :scontrol => {
206
+ :name => "scontrol",
202
207
  :folder => "scontrols",
203
- :plural => :scontrolers
208
+ :plural => :scontrols
204
209
  },
205
210
  :sharing_reason => {
206
211
  :name => "SharingReason",
@@ -1,3 +1,3 @@
1
1
  module Metaforce
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.1"
3
3
  end
@@ -5,8 +5,8 @@ require "metaforce/version"
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "metaforce"
7
7
  s.version = Metaforce::VERSION
8
- s.authors = ["Eric J. Holmes"]
9
- s.email = ["eric@ejholmes.net"]
8
+ s.authors = ["Eric J. Holmes", "Kevin J. Poorman"]
9
+ s.email = ["eric@ejholmes.net", "Kevinp@madronasg.com"]
10
10
  s.homepage = "https://github.com/ejholmes/metaforce"
11
11
  s.summary = %q{A Ruby gem for interacting with the Salesforce Metadata API}
12
12
  s.description = %q{A Ruby gem for interacting with the Salesforce Metadata API}
@@ -34,7 +34,8 @@ describe Metaforce do
34
34
  savon.expects(:login).with(:username => 'valid', :password => 'password').returns(:success)
35
35
  session = Metaforce::Services::Client.new.session
36
36
  session.should eq({ :session_id => "00DU0000000Ilbh!AQoAQHVcube9Z6CRlbR9Eg8ZxpJlrJ6X8QDbnokfyVZItFKzJsLHIRGiqhzJkYsNYRkd3UVA9.s82sbjEbZGUqP3mG6TP_P8",
37
- :metadata_server_url => "https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh" })
37
+ :metadata_server_url => "https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh",
38
+ :services_url => "https://na12-api.salesforce.com/services/Soap/u/23.0/00DU0000000Ilbh" })
38
39
  end
39
40
 
40
41
  it "allows you to set the credentials via the configure block" do
@@ -16,7 +16,8 @@ describe Metaforce::Services::Client do
16
16
  savon.expects(:login).with(:username => 'valid', :password => 'password').returns(:success)
17
17
  session = Metaforce::Services::Client.new(:username => 'valid', :password => 'password').session
18
18
  session.should eq({ :session_id => "00DU0000000Ilbh!AQoAQHVcube9Z6CRlbR9Eg8ZxpJlrJ6X8QDbnokfyVZItFKzJsLHIRGiqhzJkYsNYRkd3UVA9.s82sbjEbZGUqP3mG6TP_P8",
19
- :metadata_server_url => "https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh" })
19
+ :metadata_server_url => "https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh",
20
+ :services_url=>"https://na12-api.salesforce.com/services/Soap/u/23.0/00DU0000000Ilbh" })
20
21
  end
21
22
 
22
23
  end
@@ -25,7 +26,8 @@ describe Metaforce::Services::Client do
25
26
  savon.expects(:login).with(:username => 'valid', :password => 'password').returns(:success)
26
27
  session = Metaforce::Services::Client.new("username" => 'valid', "password" => 'password').session
27
28
  session.should eq({ :session_id => "00DU0000000Ilbh!AQoAQHVcube9Z6CRlbR9Eg8ZxpJlrJ6X8QDbnokfyVZItFKzJsLHIRGiqhzJkYsNYRkd3UVA9.s82sbjEbZGUqP3mG6TP_P8",
28
- :metadata_server_url => "https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh" })
29
+ :metadata_server_url => "https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh",
30
+ :services_url=>"https://na12-api.salesforce.com/services/Soap/u/23.0/00DU0000000Ilbh" })
29
31
  end
30
32
  end
31
33
  end
metadata CHANGED
@@ -1,19 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metaforce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Eric J. Holmes
9
+ - Kevin J. Poorman
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2012-03-23 00:00:00.000000000 Z
13
+ date: 2012-06-02 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: nokogiri
16
- requirement: &70320535767740 !ruby/object:Gem::Requirement
17
+ requirement: &70207844341860 !ruby/object:Gem::Requirement
17
18
  none: false
18
19
  requirements:
19
20
  - - ~>
@@ -21,10 +22,10 @@ dependencies:
21
22
  version: 1.5.0
22
23
  type: :runtime
23
24
  prerelease: false
24
- version_requirements: *70320535767740
25
+ version_requirements: *70207844341860
25
26
  - !ruby/object:Gem::Dependency
26
27
  name: savon
27
- requirement: &70320535767240 !ruby/object:Gem::Requirement
28
+ requirement: &70207844341360 !ruby/object:Gem::Requirement
28
29
  none: false
29
30
  requirements:
30
31
  - - ~>
@@ -32,10 +33,10 @@ dependencies:
32
33
  version: 0.9.7
33
34
  type: :runtime
34
35
  prerelease: false
35
- version_requirements: *70320535767240
36
+ version_requirements: *70207844341360
36
37
  - !ruby/object:Gem::Dependency
37
38
  name: rubyzip
38
- requirement: &70320535744500 !ruby/object:Gem::Requirement
39
+ requirement: &70207844340900 !ruby/object:Gem::Requirement
39
40
  none: false
40
41
  requirements:
41
42
  - - ~>
@@ -43,10 +44,10 @@ dependencies:
43
44
  version: 0.9.5
44
45
  type: :runtime
45
46
  prerelease: false
46
- version_requirements: *70320535744500
47
+ version_requirements: *70207844340900
47
48
  - !ruby/object:Gem::Dependency
48
49
  name: term-ansicolor
49
- requirement: &70320535744120 !ruby/object:Gem::Requirement
50
+ requirement: &70207844340520 !ruby/object:Gem::Requirement
50
51
  none: false
51
52
  requirements:
52
53
  - - ! '>='
@@ -54,10 +55,10 @@ dependencies:
54
55
  version: '0'
55
56
  type: :runtime
56
57
  prerelease: false
57
- version_requirements: *70320535744120
58
+ version_requirements: *70207844340520
58
59
  - !ruby/object:Gem::Dependency
59
60
  name: rake
60
- requirement: &70320535743580 !ruby/object:Gem::Requirement
61
+ requirement: &70207844340060 !ruby/object:Gem::Requirement
61
62
  none: false
62
63
  requirements:
63
64
  - - ! '>='
@@ -65,10 +66,10 @@ dependencies:
65
66
  version: '0'
66
67
  type: :development
67
68
  prerelease: false
68
- version_requirements: *70320535743580
69
+ version_requirements: *70207844340060
69
70
  - !ruby/object:Gem::Dependency
70
71
  name: rspec
71
- requirement: &70320535743020 !ruby/object:Gem::Requirement
72
+ requirement: &70207708128940 !ruby/object:Gem::Requirement
72
73
  none: false
73
74
  requirements:
74
75
  - - ! '>='
@@ -76,10 +77,10 @@ dependencies:
76
77
  version: '0'
77
78
  type: :development
78
79
  prerelease: false
79
- version_requirements: *70320535743020
80
+ version_requirements: *70207708128940
80
81
  - !ruby/object:Gem::Dependency
81
82
  name: mocha
82
- requirement: &70320535742560 !ruby/object:Gem::Requirement
83
+ requirement: &70207708128520 !ruby/object:Gem::Requirement
83
84
  none: false
84
85
  requirements:
85
86
  - - ! '>='
@@ -87,10 +88,10 @@ dependencies:
87
88
  version: '0'
88
89
  type: :development
89
90
  prerelease: false
90
- version_requirements: *70320535742560
91
+ version_requirements: *70207708128520
91
92
  - !ruby/object:Gem::Dependency
92
93
  name: savon_spec
93
- requirement: &70320535742000 !ruby/object:Gem::Requirement
94
+ requirement: &70207708128020 !ruby/object:Gem::Requirement
94
95
  none: false
95
96
  requirements:
96
97
  - - ~>
@@ -98,11 +99,13 @@ dependencies:
98
99
  version: 0.1.6
99
100
  type: :development
100
101
  prerelease: false
101
- version_requirements: *70320535742000
102
+ version_requirements: *70207708128020
102
103
  description: A Ruby gem for interacting with the Salesforce Metadata API
103
104
  email:
104
105
  - eric@ejholmes.net
105
- executables: []
106
+ - Kevinp@madronasg.com
107
+ executables:
108
+ - metaforce
106
109
  extensions: []
107
110
  extra_rdoc_files: []
108
111
  files:
@@ -112,11 +115,14 @@ files:
112
115
  - Guardfile
113
116
  - README.md
114
117
  - Rakefile
118
+ - bin/metaforce
115
119
  - lib/metaforce.rb
116
120
  - lib/metaforce/config.rb
117
121
  - lib/metaforce/core_extensions.rb
118
122
  - lib/metaforce/core_extensions/string.rb
123
+ - lib/metaforce/custom_actions.rb
119
124
  - lib/metaforce/error.rb
125
+ - lib/metaforce/login_details.rb
120
126
  - lib/metaforce/manifest.rb
121
127
  - lib/metaforce/metadata.rb
122
128
  - lib/metaforce/metadata/client.rb
@@ -131,6 +137,7 @@ files:
131
137
  - lib/metaforce/services/client.rb
132
138
  - lib/metaforce/tasks/README.md
133
139
  - lib/metaforce/tasks/metaforce.rake
140
+ - lib/metaforce/thor/metaforce.rb
134
141
  - lib/metaforce/types.rb
135
142
  - lib/metaforce/version.rb
136
143
  - metaforce.gemspec