lib/sem4r/adwords.rb in sem4r-0.1.2 vs lib/sem4r/adwords.rb in sem4r-0.1.3

- old
+ new

@@ -25,133 +25,278 @@ module Sem4r class Adwords attr_reader :profile - + class << self - def sandbox( config = nil ) - new( "sandbox", config ) + + # + # Initialize Adwords with sandbox profile + # @see Adwords#initialize + # + def sandbox(options = nil) + new("sandbox", options) end - def production( config = nil ) - new( "production", config ) + # + # Initialize Adwords with production profile + # @see Adwords#initialize + # + def production(options = nil) + new("production", options) end - def search_config_file - config_filename = "sem4r.yml" + # + # @private + # Search configuration file in standard locations + # @return [OpenStruct] with + # :config_dir + # :config_file + # :password_file + # + def search_config + config_filename = "sem4r.yml" + password_filename = "sem4r_passwords.yml" # # try current directory # - return File.expand_path(config_filename) if File.exists?( config_filename) + # return File.expand_path(config_filename) if File.exists?( config_filename) # # try ~/.sem4r/sem4r.yml # # require 'etc' # homedir = Etc.getpwuid.dir - homedir = ENV['HOME'] - config_filepath = File.join( homedir, ".sem4r", config_filename) - return config_filepath if File.exists?( config_filepath ) + homedir = ENV['HOME'] + config_filepath = File.join(homedir, ".sem4r", config_filename) + if File.exists?(config_filepath) + return OpenStruct.new( + :config_dir => File.join(homedir, ".sem4r"), + :config_file => config_filepath, + :password_file => File.join(homedir, ".sem4r", password_filename)) + end # # try <home_sem4r>/config/sem4r # - config_filepath = File.expand_path( File.join( File.dirname( __FILE__ ), "..", "..", "config", config_filename ) ) - return config_filepath if File.exists?( config_filepath ) + config_filepath = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", config_filename)) + if File.exists?(config_filepath) + return OpenStruct.new( + :config_dir => nil, + :config_file => config_filepath, + :password_file => nil) + end - config_filepath = File.expand_path( File.join( File.dirname( __FILE__ ), "..", "..", "config", "sem4r.example.yml" ) ) - return config_filepath if File.exists?( config_filepath ) + config_filepath = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", "sem4r.example.yml")) + if File.exists?(config_filepath) + return OpenStruct.new( + :config_dir => nil, + :config_file => config_filepath, + :password_file => nil) + end - nil + OpenStruct.new( + :config_dir => nil, + :config_file => nil, + :password_file => nil) end - def profiles( profile_file = nil ) - profile_file = search_config_file unless profile_file + # + # Return list of profile contained into config file + # + def profiles(profile_file = nil) unless profile_file - raise Sem4rError, "config file 'sem4r' not found" + config = search_config + unless config + raise Sem4rError, "config file 'sem4r' not found" + end + profile_file = config.config_file end # puts "Loaded profiles from #{profile_file}" - yaml = YAML::load( File.open( profile_file ) ) + yaml = YAML::load(File.open(profile_file)) profiles = yaml['google_adwords'] profiles end end - # new( "sandbox", {:email=>"..."} ) - # new( {:environment=>"...", email => "..." } ) - # new( "sandbox" ) - # new() sandbox default - def initialize( profile = "sandbox", options = nil ) - @logger = nil + # + # initialize Adwords lib + # profiles "sandbox" and "production" have blocked environment + # + # @param [String] profile name + # @param [Hash] options the options for profile. + # + # @option opts [String] :config_file default to "~/.sem4r/sem4r.yml" + # @option opts [String] :password_file default to "~/.sem4r/sem4r_password.yml" + # + # @option opts [String] :mutable default to true, + # if true doesn't call mutable api call + # + # @option opts [String] :environment "sandbox", "production" + # @option opts [String] :email + # @option opts [String] :password + # @option opts [String] :developer_token + # + # @example + # Adwords.new( "sandbox", {:email=>"..."} ) + # Adwords.new( {:environment=>"...", email => "..." } ) + # Adwords.new( "sandbox" ) + # Adwords.new() # default to sandbox + # + def initialize(profile = "sandbox", options = nil) + all_keys = %w{environment email password developer_token mutable config_file password_file} + @logger = nil if not options.nil? - # new( "sandbox", {:email=>"..."} ) - @profile = profile.to_s - options = options.stringify_keys - options.assert_valid_keys("environment", "email", "password", "developer_token", "mutable", "config_file") - f = options.delete("config_file") - self.config_file= f if f + # new( "profile", {:email=>"..."} ) + options = options.stringify_keys + options.assert_valid_keys(*all_keys) + + config_file = options.delete("config_file") + password_file = options.delete("password_file") + configure(config_file, password_file) + + @profile = profile.to_s @options = load_config(@profile) @options = @options.merge(options) elsif profile.respond_to?(:keys) # new( {:environment=>"...", email => "..." } ) - @options = profile.stringify_keys - @options.assert_valid_keys("environment", "email", "password", "developer_token", "mutable") + options = profile.stringify_keys + options.assert_valid_keys( *(all_keys-%w{config_file password_file}) ) + configure + @options = options @profile = "anonymous_" + @options["environment"] else # new( "sandbox" ) @profile = profile.to_s + configure @options = load_config(@profile) end if ["sandbox", "production"].include?(profile) if @options["environment"].nil? @options["environment"] = profile end if @options["environment"] != profile - raise "you cannot use profile '#{profile}' with environment #{@options["environment"]}" + raise "you cannot use profile '#{profile}' with environment '#{@options["environment"]}'" end end + + try_to_find_password_in_password_file unless @options["password"] end def to_s "adwords profile: '#{profile}' config file: '#{config_file}'" end - def config_file=(file_path) - config_filepath = File.expand_path(file_path) - if File.exists?( config_filepath) - @config_filepath = config_filepath - else - puts "#{config_filepath} not exists" - end - @config_filepath + # + # returns profiles contained into current config file + # + def profiles + self.class.profiles @config.config_file end - # return the config file name + # + # @return [String] the config file name + # def config_file - return @config_filepath if @config_filepath - @config_file_path = Adwords.search_config_file - @config_file_path + return @config.config_file if @config + @config = Adwords.search_config + unless @config + puts "cannot find configuration files" + end + @config.config_file end + ###################################################################################################### + # Password + + # + # password is defined? + # + def has_password? + !@options["password"].nil? + end + + # + # set password + # + def password=(pwd) + if @account + raise "already connected cannot change password" + end + @options["password"]=pwd + end + + def try_to_find_password_in_password_file + return unless @config.password_file + return unless File.exists?(@config.password_file) + passwords = YAML.load(File.open(@config.password_file)) + pass = passwords[@options["email"]] + @options["password"] = pass if pass + end + + def save_passwords + if @config.password_file.nil? + raise "cannot save password" + end + puts "save password in #{@config.password_file} (security warning!)" + passwords = {} + if File.exists?(@config.password_file) + passwords = YAML.load(File.open(@config.password_file)) + end + passwords[@options["email"]] = @options["password"] + File.open(@config.password_file, "w") do |f| + f.write(passwords.to_yaml) + end + end + + private + + # + # @private + # + # force config paths + # and the config file + # + def configure(config_file = nil, password_file = nil) + @config = Adwords.search_config + unless config_file.nil? + config_file = File.expand_path(config_file) + unless File.exists?(config_file) + raise "#{config_file} not exists" + end + @config.config_file = config_file + end + unless password_file.nil? + password_file = File.expand_path(password_file) + @config.password_file = password_file + end + @config + end + + + # @private + # load the configuration def load_config(profile) - unless config_file + unless @config.config_file raise Sem4rError, "config file 'sem4r' not found" end # puts "Loaded profiles from #{config_file}" - yaml = YAML::load( File.open( config_file ) ) - config = yaml['google_adwords'][profile] + yaml = YAML::load(File.open(@config.config_file)) + config = yaml['google_adwords'][profile] config || {} end + public + ########################################################################## # logging - def dump_soap_options( dump_options ) + def dump_soap_options(dump_options) @dump_soap_options = dump_options - @connector.dump_soap_options( dump_options ) if @connector + @connector.dump_soap_options(dump_options) if @connector end def dump_soap? not @dump_soap_options.nil? end @@ -164,31 +309,40 @@ end end def logger= logger unless logger.instance_of?(Logger) - file = File.open( logger, "a" ) - file.sync = true - logger = Logger.new(file) + file = File.open(logger, "a") + file.sync = true + logger = Logger.new(file) logger.formatter = proc { |severity, datetime, progname, msg| "#{datetime.strftime("%H:%M:%S")}: #{msg}\n" } - end + end @logger= logger @connector.logger= logger if @connector end def logger @logger end + ########################################################################## + # others + + # + # @return [Account] + # def account deferred_initialize unless @initialized @account end + # + # print api counters on stdout + # def p_counters operations = @counters[:operations] units = @counters[:units] response_time = @counters[:response_time] puts "#{units} unit spent for #{operations} operations in #{response_time}ms" @@ -197,47 +351,49 @@ ########################################################################## # methods only for internal use attr_reader :service - # + # @private # TODO: credentials are necessary because you might use more then account/credentials # at the same time # - def add_counters( credentials, counters ) - counters.each_pair { |k,v| + def add_counters(credentials, counters) + counters.each_pair { |k, v| @counters[k] ||= 0 @counters[k] += v } end private + # @private + # really initialize connections to the google server + # def deferred_initialize @initialized = true - @connector = SoapConnector.new + @connector = Sem4rSoap::HttpConnector.get(@logger, :http_client) @connector.dump_soap_options(@dump_soap_options) if @dump_soap_options - @connector.logger=(@logger) if @logger - @credentials = Credentials.new( - :environment => @options["environment"], - :email => @options["email"], - :password => @options["password"], - :useragent => "Sem4r Adwords Ruby Client Library (http://github.com/sem4r/sem4r)", - :developer_token => @options["developer_token"], - :mutable => @options["mutable"] + @credentials = Credentials.new( + :environment => @options["environment"], + :email => @options["email"], + :password => @options["password"], + :useragent => "Sem4r Adwords Ruby Client Library (http://github.com/sem4r/sem4r)", + :developer_token => @options["developer_token"], + :mutable => @options["mutable"] ) @credentials.connector = @connector - @service = Service.new(@connector) - @account = Account.new( self, @credentials ) - @counters = { - :operations => 0, - :response_time => 0, - :units => 0 + @service = Service.new(@connector) + @account = Account.new(self, @credentials) + @counters = { + :operations => 0, + :response_time => 0, + :units => 0 } end end -end +end # module Sem4r