adwords4r 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/Authors.txt +2 -0
  2. data/ChangeLog.txt +4 -0
  3. data/Copying.txt +28 -0
  4. data/Licence.txt +10 -0
  5. data/Rakefile +190 -0
  6. data/Readme.txt +61 -0
  7. data/Todo.txt +9 -0
  8. data/examples/campaign.rb +36 -0
  9. data/examples/framework.rb +6 -0
  10. data/lib/adwords4r.rb +136 -0
  11. data/lib/adwords4r/credentials.rb +37 -0
  12. data/lib/adwords4r/services.rb +41 -0
  13. data/lib/adwords4r/v2/AccountService.rb +626 -0
  14. data/lib/adwords4r/v2/AccountServiceDriver.rb +153 -0
  15. data/lib/adwords4r/v2/AdGroupService.rb +315 -0
  16. data/lib/adwords4r/v2/AdGroupServiceDriver.rb +97 -0
  17. data/lib/adwords4r/v2/CampaignService.rb +515 -0
  18. data/lib/adwords4r/v2/CampaignServiceDriver.rb +111 -0
  19. data/lib/adwords4r/v2/CreativeService.rb +399 -0
  20. data/lib/adwords4r/v2/CreativeServiceDriver.rb +104 -0
  21. data/lib/adwords4r/v2/CriterionService.rb +413 -0
  22. data/lib/adwords4r/v2/CriterionServiceDriver.rb +97 -0
  23. data/lib/adwords4r/v2/InfoService.rb +258 -0
  24. data/lib/adwords4r/v2/InfoServiceDriver.rb +90 -0
  25. data/lib/adwords4r/v2/KeywordService.rb +541 -0
  26. data/lib/adwords4r/v2/KeywordServiceDriver.rb +125 -0
  27. data/lib/adwords4r/v2/ReportService.rb +567 -0
  28. data/lib/adwords4r/v2/ReportServiceDriver.rb +83 -0
  29. data/lib/adwords4r/v2/TrafficEstimatorService.rb +249 -0
  30. data/lib/adwords4r/v2/TrafficEstimatorServiceDriver.rb +62 -0
  31. data/lib/adwords4r/v2/default.rb +364 -0
  32. data/lib/adwords4r/v2/defaultDriver.rb +102 -0
  33. data/lib/adwords4r/v3/AccountService.rb +626 -0
  34. data/lib/adwords4r/v3/AccountServiceDriver.rb +153 -0
  35. data/lib/adwords4r/v3/AdGroupService.rb +318 -0
  36. data/lib/adwords4r/v3/AdGroupServiceDriver.rb +97 -0
  37. data/lib/adwords4r/v3/CampaignService.rb +529 -0
  38. data/lib/adwords4r/v3/CampaignServiceDriver.rb +111 -0
  39. data/lib/adwords4r/v3/CreativeService.rb +399 -0
  40. data/lib/adwords4r/v3/CreativeServiceDriver.rb +104 -0
  41. data/lib/adwords4r/v3/CriterionService.rb +413 -0
  42. data/lib/adwords4r/v3/CriterionServiceDriver.rb +97 -0
  43. data/lib/adwords4r/v3/InfoService.rb +258 -0
  44. data/lib/adwords4r/v3/InfoServiceDriver.rb +90 -0
  45. data/lib/adwords4r/v3/KeywordService.rb +541 -0
  46. data/lib/adwords4r/v3/KeywordServiceDriver.rb +125 -0
  47. data/lib/adwords4r/v3/KeywordToolService.rb +225 -0
  48. data/lib/adwords4r/v3/KeywordToolServiceDriver.rb +55 -0
  49. data/lib/adwords4r/v3/ReportService.rb +567 -0
  50. data/lib/adwords4r/v3/ReportServiceDriver.rb +83 -0
  51. data/lib/adwords4r/v3/TrafficEstimatorService.rb +263 -0
  52. data/lib/adwords4r/v3/TrafficEstimatorServiceDriver.rb +62 -0
  53. data/scripts/publish.rb +16 -0
  54. data/setup.rb +1585 -0
  55. metadata +92 -0
@@ -0,0 +1,2 @@
1
+ opensource@google.com
2
+ chanezon@google.com
@@ -0,0 +1,4 @@
1
+ 0.2 added packaging and made it a gem, for easy installation.
2
+ 0.1 -first version, not fully tested
3
+ works for Campaigns
4
+
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2006, Google Inc.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are
6
+ met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+ * Redistributions in binary form must reproduce the above
11
+ copyright notice, this list of conditions and the following disclaimer
12
+ in the documentation and/or other materials provided with the
13
+ distribution.
14
+ * Neither the name of Google Inc. nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,10 @@
1
+ Copyright (c) 2006, Google Inc
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
+
6
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
+ * Neither the name of Google, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9
+
10
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,190 @@
1
+ require 'wsdl/soap/wsdl2ruby'
2
+ require 'net/https'
3
+ require 'fileutils'
4
+ require 'rake/clean'
5
+ require 'logger'
6
+ begin
7
+ require 'rubygems'
8
+ require 'rake/gempackagetask'
9
+ rescue Exception
10
+ nil
11
+ end
12
+ begin
13
+ require 'lib/adwords4r/services'
14
+ rescue Exception
15
+ puts 'require services not found'
16
+ nil
17
+ end
18
+
19
+ # Determine the current version of the software
20
+
21
+ CLOBBER.include('pkg')
22
+
23
+ CURRENT_VERSION = '0.2'
24
+ PKG_VERSION = ENV['REL'] ? ENV['REL'] : CURRENT_VERSION
25
+
26
+ SRC_RB = FileList['lib/**/*.rb']
27
+
28
+ # The default task is run if rake is given no explicit arguments.
29
+
30
+ #desc "Default Task"
31
+ #task :default => :test_all
32
+
33
+ WSDLDIR = 'wsdl'
34
+ LIBDIR = 'lib'
35
+ logger = Logger.new(STDERR)
36
+
37
+ #CLEAN.include(WSDLDIR)
38
+
39
+ desc "gets the wsdl and generates the classes"
40
+ task :default => [:getwsdl, :generate]
41
+
42
+ desc "gets the wsdl files for AdWords services"
43
+ task :getwsdl do
44
+ AdWords::Service.getVersions.each do |v|
45
+ vname = "v#{v}"
46
+ mkdir_p File.join(WSDLDIR, vname)
47
+ AdWords::Service.getServices(v).each {|s| save(getfile("adwords.google.com", "/api/adwords/#{vname}/#{s}Service?wsdl"), getWsdlFileName(vname,s))}
48
+ end
49
+ end
50
+
51
+ desc "generates AdWords classes from the wsdl files"
52
+ task :generate do
53
+ AdWords::Service.getVersions.each do |v|
54
+ vname = "v#{v}"
55
+ gendir = "#{LIBDIR}/adwords4r/#{vname}"
56
+ mkdir_p gendir
57
+ AdWords::Service.getServices(v).each do |name|
58
+ worker = WSDL::SOAP::WSDL2Ruby.new
59
+ worker.logger = logger
60
+ worker.location = getWsdlFileName(vname,name)
61
+ worker.basedir = gendir
62
+ worker.opt.update(getWsdlOpt(name))
63
+ worker.run
64
+ fixImport(v, File.join(gendir, "#{name}Driver.rb"))
65
+ end
66
+ end
67
+ end
68
+
69
+ def fixImport(version, file)
70
+ vname = "v#{version}"
71
+ tempfile = file + '.tmp'
72
+ outfile = File.new(tempfile,"w")
73
+ File.open(file, "r") do |infile|
74
+ req = 0
75
+ infile.each do |l|
76
+ if (l =~/require/) then
77
+ # outfile.puts l.gsub(/require \'(.*)Service.rb\'/, 'require #\'adwords4r/' + vname + '/\1Service\'')
78
+ outfile.puts l.gsub(/require \'(.*)Service.rb\'/, 'require \'adwords4r/' + vname + '/\1Service\'')
79
+ req = req + 1
80
+ else
81
+ if (req == 2) then
82
+ outfile.puts "module AdWordsV#{version}"
83
+ req = req + 1
84
+ end
85
+ outfile.puts l
86
+ end
87
+ end
88
+ end
89
+ outfile.puts "end"
90
+ outfile.close
91
+ File.rename(tempfile, file)
92
+ end
93
+
94
+
95
+ def getWsdlOpt(s)
96
+ optcmd= {}
97
+ s << "Service"
98
+ optcmd['classdef'] = s
99
+ #should work but doesn't, driver name is derived from classname
100
+ #if you specify both it breaks, same thing for client_skelton
101
+ #optcmd['driver'] = s
102
+ optcmd['driver'] = nil
103
+ #optcmd['client_skelton'] = nil
104
+ optcmd['force'] = true
105
+ return optcmd
106
+ end
107
+
108
+ def getWsdlFileName(v,s)
109
+ "#{WSDLDIR}/#{v}/#{s}.wsdl"
110
+ end
111
+
112
+ def getfile(host, path)
113
+ puts "getting https//#{host}#{path}"
114
+ https = Net::HTTP.new(host, 443)
115
+ https.use_ssl = true
116
+ https.verify_mode = OpenSSL::SSL::VERIFY_NONE
117
+ https.start { |w| w.get2(path).body }
118
+ end
119
+
120
+ # Saves this document to the specified @var path.
121
+ #doesn't create the file if contains markup for google 404 page
122
+ def save(content, path)
123
+ if content !~ /<H2>Error 404<\/H2>/
124
+ File::open(path, 'w') {|f| f.write(content)}
125
+ end
126
+ end
127
+
128
+ # ====================================================================
129
+ # Create a task that will package the Rake software into distributable
130
+ # gem files.
131
+
132
+ PKG_FILES = FileList[
133
+ '*.*',
134
+ 'Rakefile',
135
+ 'lib/**/*.rb',
136
+ 'examples/**/*.rb',
137
+ # 'test/**/*.rb',
138
+ 'scripts/**/*.rb'
139
+ ]
140
+
141
+ puts PKG_FILES
142
+
143
+ PKG_FILES.exclude(/\._/)
144
+
145
+ if ! defined?(Gem)
146
+ puts "Package Target requires RubyGEMs"
147
+ else
148
+ spec = Gem::Specification.new do |s|
149
+
150
+ #### Basic information.
151
+
152
+ s.name = 'adwords4r'
153
+ s.version = PKG_VERSION
154
+ s.summary = "Client library for the AdWords API."
155
+ s.description = %{\
156
+ Adwords4r provides an easy to use way to access the AdWords API in ruby.
157
+ Currently the following AdWords API versions are supported:
158
+
159
+ * V2
160
+ * V3
161
+ }
162
+
163
+ s.files = PKG_FILES.to_a
164
+ s.require_path = 'lib'
165
+ s.autorequire = 'adwords4r'
166
+
167
+ #s.test_files = PKG_FILES.select { |fn| fn =~ /^test\/test/ }
168
+
169
+ #s.has_rdoc = true
170
+ s.has_rdoc = false
171
+ #s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
172
+ #s.rdoc_options <<
173
+ '--title' << 'Builder -- Easy XML Building' <<
174
+ '--main' << 'README' <<
175
+ '--line-numbers'
176
+
177
+ s.author = "Patrick Chanezon"
178
+ s.email = "patrick@chanezon.com"
179
+ s.homepage = "http://rubyforge.org/projects/adwords4r/"
180
+ s.requirements << 'soap4r v 1.5.4 or greater'
181
+ s.rubyforge_project = 'adwords4r'
182
+ end
183
+
184
+ Rake::GemPackageTask.new(spec) do |t|
185
+ t.need_tar = true
186
+ end
187
+ end
188
+
189
+ require 'scripts/publish'
190
+
@@ -0,0 +1,61 @@
1
+ Google adwwords4r Library
2
+ ----------------------
3
+
4
+ Welcome to adwords4r, bringing the delights of ruby programming to the AdWords world: AdWords::API.new.getAllAdWordsCampaigns(123).each {|c| puts c.name}
5
+
6
+ Not much doc or comments yet
7
+
8
+ docs for users
9
+ --------------
10
+
11
+ adwords4r is a ruby gem. See http://docs.rubygems.org/read/book/1
12
+ Install it using the gem install command.
13
+ > gem install --remote adwords4r
14
+
15
+ It's pretty easy to use.
16
+ See http://docs.rubygems.org/read/chapter/3#page70 for how to set the rubygem environment.
17
+ export RUBYOPT=rubygems
18
+ or ruby -rubygems my_program_that_uses_gems
19
+
20
+ If you do not use the rubygems option, you need to add
21
+ require 'rubygems'
22
+ at the beginning of your programs.
23
+
24
+ Then
25
+ require 'adwords4r'
26
+
27
+ adwords = AdWords::API.new
28
+ creates a driver for the latest version of AdWords API using credentials provided in ~/.adwords.yaml
29
+ If you want something more specific, use the optional paramters of the constructor
30
+ adwords = AdWords::API.new(credentials, version)
31
+
32
+ Then just use methods of the API against your driver.
33
+ adwords.getAllAdWordsCampaigns(123).each {|c| puts c.name}
34
+
35
+ See sample code in examples.
36
+
37
+ docs for developers
38
+ -------------------
39
+ rake getwsdl
40
+ to get the wsdl files
41
+ rake generate
42
+ to regenerate the bindings if needed
43
+ rake package
44
+ to package the gem and create a release
45
+ rake publish
46
+ to publish the gem to rubyforge
47
+
48
+ adwords4r is licensed under the BSD License
49
+
50
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
51
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
54
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
56
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
59
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60
+
61
+ It is fun to use adwords4r anyway...
@@ -0,0 +1,9 @@
1
+ adwords4r todo list
2
+ ---
3
+
4
+ - gem file
5
+ - handling quota management in the driver
6
+ - pass in a hash for complex objects
7
+ - comments everywhere, rdoc
8
+ - documentation
9
+ - more samples
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'adwords4r'
4
+ require 'pp'
5
+
6
+ SEP = "---"
7
+
8
+ def dumpObj(o)
9
+ str = ""
10
+ o.instance_variables.each { |v| str << dumpAttr(o, v)}
11
+ return str << SEP
12
+ end
13
+
14
+ def dumpAttr(o, v)
15
+ name = v.sub(/@/,'')
16
+ value = eval("o.#{name}.to_s")
17
+ return "#{name}: #{value}\n"
18
+ end
19
+
20
+ begin
21
+ adwords = AdWords::API.new
22
+ #pp adwords.thismethodDoesNotExist()
23
+ adwords.getAllAdWordsCampaigns(123).each {|c| puts dumpObj(c)}
24
+ c = Campaign.new
25
+ c.dailyBudget = 10000
26
+ c.status = 'Paused'
27
+ c.name = 'Test P@ #{rand(10000}'
28
+ pp adwords.addCampaign(c)
29
+
30
+ rescue AdWords::Error::UnknownAPICall => e
31
+ puts e
32
+ rescue AdWords::Error::ApiError => e
33
+ puts e.code
34
+ puts e.message
35
+ end
36
+
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'adwords4r'
4
+ require 'pp'
5
+
6
+ adwords = AdWords::API.new
@@ -0,0 +1,136 @@
1
+ require 'adwords4r/credentials'
2
+ require 'adwords4r/services'
3
+ require 'soap/soap'
4
+
5
+ module AdWords
6
+
7
+ class API
8
+
9
+ attr_reader :credentials, :drivers, :version
10
+ @methodMap = Hash.new
11
+
12
+ def initialize(*parm)
13
+ if parm[0]
14
+ @credentials = parm[0]
15
+ else
16
+ @credentials = AdWordsCredentials.new
17
+ end
18
+ if parm[1]
19
+ @version = parm[1]
20
+ else
21
+ @version = Service.getVersions.sort.last
22
+ end
23
+ @drivers = Hash.new
24
+ prepareDrivers
25
+ end
26
+
27
+ def method_missing(m, *args)
28
+ methodName = m.id2name
29
+ requestName = AdWords::fix_case_up(m.id2name) # upper first character
30
+ param = args[0]
31
+
32
+ if valid_call?(methodName)
33
+ req = eval("#{requestName}.new(param)")
34
+ resp = eval("getDriver(methodName).#{methodName}(req)")
35
+ return resp
36
+ else
37
+ raise(Error::UnknownAPICall, "Unknown API Call: #{requestName}", caller)
38
+ end
39
+ # Handle AdWords Application-level error
40
+ rescue SOAP::FaultError => fault
41
+ raise(Error::ApiError.new(fault), "#{methodName} Call Failed: #{fault.faultstring.to_s}", caller)
42
+ end
43
+
44
+ private
45
+
46
+ def prepareDrivers()
47
+ Service.doRequire(@version)
48
+ Service.getServices(@version).each {|s| @drivers[s] = prepareDriver(s)}
49
+ @methodMap = Service.getMethodMap(@drivers)
50
+ end
51
+
52
+ #pass in call name, get the driver back
53
+ def getDriver(call)
54
+ return @methodMap[call]
55
+ end
56
+
57
+ def prepareDriver(s)
58
+ driver = eval("AdWordsV#{@version}::#{getServiceName(s)}.new")
59
+ @credentials.handlers.each {|h| driver.headerhandler << h}
60
+ #driver.wiredump_dev = STDOUT if @debug
61
+ driver.wiredump_file_base = "log"
62
+ driver.options['protocol.http.ssl_config.verify_mode'] = nil
63
+ #set driver.proxy if you are behing a proxy
64
+ return driver
65
+ end
66
+
67
+ #workaround the bug: some services do not follow the naming convention
68
+ def getServiceName(s)
69
+ if s == "TrafficEstimator" then "TrafficEstimatorInterface"
70
+ elsif s == "Report" then "ReportServiceInterface"
71
+ elsif s == "Info" then "InfoServiceInterface"
72
+ elsif s == "KeywordTool" then "KeywordToolInterface"
73
+ else (s + "Service")
74
+ end
75
+ end
76
+
77
+ def valid_call?(call)
78
+ return @methodMap.has_key?(call)
79
+ end
80
+
81
+ end
82
+
83
+ #copied and adapted from ebay4r
84
+ #maybe these type of handlers should be generalized and put in soap4r
85
+ #for other api projects to be able to get started more quickly
86
+ class Error
87
+ class Error < StandardError; end
88
+
89
+ # Raised if a call is made to a method that does not exist in the AdWords SOAP API
90
+ class UnknownAPICall < Error; end
91
+
92
+ # Raised if an attempt is made to instantiate a type that does not exist in the AdWords SOAP API
93
+ class UnknownType < Error; end
94
+
95
+ # Raised if a call returns with a SOAP error, gives you easy access to adwords error fields
96
+ class ApiError < Error
97
+
98
+ attr_accessor :soap_faultcode
99
+ attr_accessor :soap_faultstring
100
+ attr_accessor :code
101
+ attr_accessor :internal
102
+ attr_accessor :message
103
+ attr_accessor :trigger
104
+ attr_accessor :violations
105
+
106
+ def initialize(fault)
107
+ @soap_faultcode = getOrNil(fault, 'faultcode')
108
+ @soap_faultstring = getOrNil(fault, 'faultstring')
109
+ @code = getOrNil(fault.detail, 'code')
110
+ @internal = getOrNil(fault.detail,'internal')
111
+ @message = getOrNil(fault.detail,'message')
112
+ @trigger = getOrNil(fault.detail,'trigger')
113
+ @violations = getOrNil(fault.detail,'violations')
114
+ end
115
+
116
+ private
117
+ def getOrNil(obj, meth)
118
+ obj.respond_to?(meth) ? eval("obj.#{meth}") : nil
119
+ end
120
+ end
121
+ end
122
+
123
+ # These class module methods are helper functions
124
+ class <<self
125
+
126
+ def fix_case_up(name)
127
+ name[0] = name[0,1].upcase # upper first character
128
+ name
129
+ end
130
+
131
+ def fix_case_down(name)
132
+ name[0] = name[0,1].downcase
133
+ name
134
+ end
135
+ end
136
+ end