adapi 0.0.8 → 0.0.9
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/.gitignore +1 -1
 - data/README.markdown +2 -1
 - data/adapi.gemspec +11 -11
 - data/examples/add_ad_group.rb +0 -0
 - data/examples/add_bare_ad_group.rb +3 -1
 - data/examples/add_bare_campaign.rb +6 -4
 - data/examples/add_campaign.rb +10 -3
 - data/examples/add_campaign_criteria.rb +5 -5
 - data/examples/add_invalid_ad_group.rb +4 -6
 - data/examples/add_invalid_keywords.rb +40 -0
 - data/examples/add_invalid_text_ad.rb +6 -6
 - data/examples/add_keywords.rb +0 -0
 - data/examples/add_negative_campaign_criteria.rb +0 -0
 - data/examples/add_text_ad.rb +5 -9
 - data/examples/custom_settings.yml +8 -0
 - data/examples/customize_configuration.rb +0 -0
 - data/examples/delete_ad_group.rb +11 -0
 - data/examples/delete_keyword.rb +0 -0
 - data/examples/delete_text_ad.rb +13 -0
 - data/examples/find_ad_group.rb +10 -0
 - data/examples/find_all_campaigns.rb +0 -0
 - data/examples/find_bare_campaign.rb +34 -0
 - data/examples/find_campaign.rb +0 -0
 - data/examples/find_campaign_ad_groups.rb +0 -0
 - data/examples/find_campaign_criteria.rb +0 -0
 - data/examples/find_locations.rb +0 -0
 - data/examples/log_to_specific_account.rb +0 -0
 - data/examples/rollback_campaign.rb +0 -0
 - data/examples/test_diacritics.rb +0 -0
 - data/examples/update_ad_group.rb +70 -0
 - data/examples/update_campaign.rb +34 -9
 - data/examples/update_campaign_ad_groups.rb +137 -0
 - data/examples/update_campaign_criteria.rb +33 -0
 - data/examples/update_complete_campaign.rb +177 -0
 - data/examples/update_text_ad.rb +18 -0
 - data/lib/adapi/ad/text_ad.rb +39 -49
 - data/lib/adapi/ad.rb +30 -19
 - data/lib/adapi/ad_group.rb +110 -30
 - data/lib/adapi/api.rb +25 -28
 - data/lib/adapi/campaign.rb +216 -70
 - data/lib/adapi/campaign_criterion.rb +44 -5
 - data/lib/adapi/config.rb +1 -4
 - data/lib/adapi/constant_data/language.rb +11 -2
 - data/lib/adapi/keyword.rb +60 -1
 - data/lib/adapi/version.rb +11 -2
 - data/lib/adapi.rb +10 -7
 - data/test/{config/adapi.yml.template → fixtures/adapi.yml} +11 -2
 - data/test/{config/adwords_api.yml.template → fixtures/adwords_api.yml} +6 -2
 - data/test/integration/find_location_test.rb +1 -1
 - data/test/unit/ad_group_test.rb +2 -2
 - data/test/unit/config_test.rb +8 -27
 - data/test/unit/constant_data/language_test.rb +34 -0
 - metadata +52 -108
 - data/examples/update_campaign_status.rb +0 -15
 - data/lib/savon_monkeypatch.rb +0 -43
 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'adapi'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative 'add_campaign_criteria'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            =begin this is an obsolete way to do this
         
     | 
| 
      
 8 
     | 
    
         
            +
            Adapi::CampaignCriterion.new(
         
     | 
| 
      
 9 
     | 
    
         
            +
              :campaign_id => $campaign[:id],
         
     | 
| 
      
 10 
     | 
    
         
            +
              :criteria => {
         
     | 
| 
      
 11 
     | 
    
         
            +
                :language => %w{ en cs},
         
     | 
| 
      
 12 
     | 
    
         
            +
              }
         
     | 
| 
      
 13 
     | 
    
         
            +
            ).destroy
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            Adapi::CampaignCriterion.new(
         
     | 
| 
      
 16 
     | 
    
         
            +
              :campaign_id => $campaign[:id],
         
     | 
| 
      
 17 
     | 
    
         
            +
              :criteria => {
         
     | 
| 
      
 18 
     | 
    
         
            +
                :language => %w{ sk },
         
     | 
| 
      
 19 
     | 
    
         
            +
              }
         
     | 
| 
      
 20 
     | 
    
         
            +
            ).create
         
     | 
| 
      
 21 
     | 
    
         
            +
            =end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            $new_criteria = Adapi::CampaignCriterion.new(
         
     | 
| 
      
 24 
     | 
    
         
            +
              :campaign_id => $campaign[:id],
         
     | 
| 
      
 25 
     | 
    
         
            +
              :criteria => {
         
     | 
| 
      
 26 
     | 
    
         
            +
                :language => %w{ sk },
         
     | 
| 
      
 27 
     | 
    
         
            +
              }
         
     | 
| 
      
 28 
     | 
    
         
            +
            )
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            $result = $new_criteria.update!
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            $campaign_criterion = Adapi::CampaignCriterion.find( :campaign_id => $campaign[:id] )
         
     | 
| 
      
 33 
     | 
    
         
            +
            pp $campaign_criterion
         
     | 
| 
         @@ -0,0 +1,177 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'adapi'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # create campaign by single command, with campaing targets, with ad_groups
         
     | 
| 
      
 6 
     | 
    
         
            +
            # including keywords and ads
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            $ad_group_names = [
         
     | 
| 
      
 9 
     | 
    
         
            +
              "AdGroup 01 #%d" % (Time.new.to_f * 1000).to_i,
         
     | 
| 
      
 10 
     | 
    
         
            +
              "AdGroup 02 #%d" % (Time.new.to_f * 1000).to_i
         
     | 
| 
      
 11 
     | 
    
         
            +
            ]
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            campaign_data = {
         
     | 
| 
      
 14 
     | 
    
         
            +
              # basic data for campaign
         
     | 
| 
      
 15 
     | 
    
         
            +
              :name => "Campaign #%d" % (Time.new.to_f * 1000).to_i,
         
     | 
| 
      
 16 
     | 
    
         
            +
              :status => 'PAUSED',
         
     | 
| 
      
 17 
     | 
    
         
            +
              :bidding_strategy => 'ManualCPC',
         
     | 
| 
      
 18 
     | 
    
         
            +
              :budget => 50,
         
     | 
| 
      
 19 
     | 
    
         
            +
              :network_setting => {
         
     | 
| 
      
 20 
     | 
    
         
            +
                :target_google_search => true,
         
     | 
| 
      
 21 
     | 
    
         
            +
                :target_search_network => true,
         
     | 
| 
      
 22 
     | 
    
         
            +
                :target_content_network => false,
         
     | 
| 
      
 23 
     | 
    
         
            +
                :target_content_contextual => false
         
     | 
| 
      
 24 
     | 
    
         
            +
              },
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              :criteria => {
         
     | 
| 
      
 27 
     | 
    
         
            +
                :language => [ :en, :cs ],
         
     | 
| 
      
 28 
     | 
    
         
            +
                :geo => { :proximity => { :geo_point => '38.89859,-77.035971', :radius => '10 km' } }
         
     | 
| 
      
 29 
     | 
    
         
            +
              },
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              :ad_groups => [
         
     | 
| 
      
 32 
     | 
    
         
            +
                {
         
     | 
| 
      
 33 
     | 
    
         
            +
                  :name => $ad_group_names[0],
         
     | 
| 
      
 34 
     | 
    
         
            +
                  :status => 'ENABLED',
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  :keywords => [ 'neo', 'dem codez', '"top coder"', "[-code]" ],
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  :ads => [
         
     | 
| 
      
 39 
     | 
    
         
            +
                    {
         
     | 
| 
      
 40 
     | 
    
         
            +
                      :headline => "Code like Neo",
         
     | 
| 
      
 41 
     | 
    
         
            +
                      :description1 => 'Need mad coding skills?',
         
     | 
| 
      
 42 
     | 
    
         
            +
                      :description2 => 'Check out my new blog!',
         
     | 
| 
      
 43 
     | 
    
         
            +
                      :url => 'http://www.demcodez.com',
         
     | 
| 
      
 44 
     | 
    
         
            +
                      :display_url => 'http://www.demcodez.com'
         
     | 
| 
      
 45 
     | 
    
         
            +
                    }
         
     | 
| 
      
 46 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 47 
     | 
    
         
            +
                },
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                {
         
     | 
| 
      
 50 
     | 
    
         
            +
                  :name => $ad_group_names[1],
         
     | 
| 
      
 51 
     | 
    
         
            +
                  :status => 'PAUSED',
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  :keywords => [ 'dem codez', 'trinity', 'morpheus', '"top coder"', "[-code]" ],
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  :ads => [
         
     | 
| 
      
 56 
     | 
    
         
            +
                    {
         
     | 
| 
      
 57 
     | 
    
         
            +
                      :headline => "Code like Trinity",
         
     | 
| 
      
 58 
     | 
    
         
            +
                      :description1 => 'The power of awesomeness?',
         
     | 
| 
      
 59 
     | 
    
         
            +
                      :description2 => 'Check out my new blog!',
         
     | 
| 
      
 60 
     | 
    
         
            +
                      :url => 'http://www.demcodez.com',
         
     | 
| 
      
 61 
     | 
    
         
            +
                      :display_url => 'http://www.demcodez.com'
         
     | 
| 
      
 62 
     | 
    
         
            +
                    },
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    {
         
     | 
| 
      
 65 
     | 
    
         
            +
                      :headline => "Code like Morpheus",
         
     | 
| 
      
 66 
     | 
    
         
            +
                      :description1 => 'Unleash the power of Matrix',
         
     | 
| 
      
 67 
     | 
    
         
            +
                      :description2 => 'Check out my new blog',
         
     | 
| 
      
 68 
     | 
    
         
            +
                      :url => 'http://www.demcodez.com',
         
     | 
| 
      
 69 
     | 
    
         
            +
                      :display_url => 'http://www.demcodez.com'
         
     | 
| 
      
 70 
     | 
    
         
            +
                    }        
         
     | 
| 
      
 71 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 72 
     | 
    
         
            +
                }    
         
     | 
| 
      
 73 
     | 
    
         
            +
              ]
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            }
         
     | 
| 
      
 76 
     | 
    
         
            +
             
         
     | 
| 
      
 77 
     | 
    
         
            +
            $campaign = Adapi::Campaign.create(campaign_data)
         
     | 
| 
      
 78 
     | 
    
         
            +
            p "Created campaign ID #{$campaign.id}"
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
            # PS: changes in ad_groups:
         
     | 
| 
      
 81 
     | 
    
         
            +
            # * delete first ad_group
         
     | 
| 
      
 82 
     | 
    
         
            +
            # * change second ad_group
         
     | 
| 
      
 83 
     | 
    
         
            +
            # * add new ad_group
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
            Adapi::Campaign.update(
         
     | 
| 
      
 86 
     | 
    
         
            +
              :id => $campaign[:id],
         
     | 
| 
      
 87 
     | 
    
         
            +
              :status => 'ACTIVE',
         
     | 
| 
      
 88 
     | 
    
         
            +
              :name => "UPDATED #{$campaign[:name]}",
         
     | 
| 
      
 89 
     | 
    
         
            +
              :bidding_strategy => { 
         
     | 
| 
      
 90 
     | 
    
         
            +
                :xsi_type => 'BudgetOptimizer', 
         
     | 
| 
      
 91 
     | 
    
         
            +
                :bid_ceiling => 20 
         
     | 
| 
      
 92 
     | 
    
         
            +
              },
         
     | 
| 
      
 93 
     | 
    
         
            +
              :budget => 75,
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
              # deletes all criteria (except :platform) and create these new ones
         
     | 
| 
      
 96 
     | 
    
         
            +
              :criteria => {
         
     | 
| 
      
 97 
     | 
    
         
            +
                :language => [ :sk ],
         
     | 
| 
      
 98 
     | 
    
         
            +
              },
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              :ad_groups => [
         
     | 
| 
      
 101 
     | 
    
         
            +
                # no match here for $ad_group_names[0], so it's going to be deleted
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                # this ad_group will be created
         
     | 
| 
      
 104 
     | 
    
         
            +
                {
         
     | 
| 
      
 105 
     | 
    
         
            +
                  :name => "UPDATED " + $ad_group_names[0],
         
     | 
| 
      
 106 
     | 
    
         
            +
                  :status => 'ENABLED',
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  :keywords => [ 'neo update', 'dem codezzz', '"top coder"' ],
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  :ads => [
         
     | 
| 
      
 111 
     | 
    
         
            +
                    {
         
     | 
| 
      
 112 
     | 
    
         
            +
                      :headline => "Update like Neo",
         
     | 
| 
      
 113 
     | 
    
         
            +
                      :description1 => 'Need mad coding skills?',
         
     | 
| 
      
 114 
     | 
    
         
            +
                      :description2 => 'Check out my new blog!',
         
     | 
| 
      
 115 
     | 
    
         
            +
                      :url => 'http://www.demcodez.com',
         
     | 
| 
      
 116 
     | 
    
         
            +
                      :display_url => 'http://www.demcodez.com'
         
     | 
| 
      
 117 
     | 
    
         
            +
                    }
         
     | 
| 
      
 118 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 119 
     | 
    
         
            +
                },
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                # this ad_group is going to be updated
         
     | 
| 
      
 122 
     | 
    
         
            +
                {
         
     | 
| 
      
 123 
     | 
    
         
            +
                  :name =>  $ad_group_names[1],
         
     | 
| 
      
 124 
     | 
    
         
            +
                  :status => 'ENABLED', # from PAUSED
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                  :keywords => [ 'dem updatez', 'update trinity', 'update morpheus' ],
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                  :ads => [
         
     | 
| 
      
 129 
     | 
    
         
            +
                    {
         
     | 
| 
      
 130 
     | 
    
         
            +
                      :headline => "Update like Trinity",
         
     | 
| 
      
 131 
     | 
    
         
            +
                      :description1 => 'The power of updates?',
         
     | 
| 
      
 132 
     | 
    
         
            +
                      :description2 => 'Check out my new blog!',
         
     | 
| 
      
 133 
     | 
    
         
            +
                      :url => 'http://www.demcodez.com',
         
     | 
| 
      
 134 
     | 
    
         
            +
                      :display_url => 'http://www.demcodez.com'
         
     | 
| 
      
 135 
     | 
    
         
            +
                    },
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                    {
         
     | 
| 
      
 138 
     | 
    
         
            +
                      :headline => "Update like Morpheus",
         
     | 
| 
      
 139 
     | 
    
         
            +
                      :description1 => 'Unleash the power of updates',
         
     | 
| 
      
 140 
     | 
    
         
            +
                      :description2 => 'Check out my new blog',
         
     | 
| 
      
 141 
     | 
    
         
            +
                      :url => 'http://www.demcodez.com',
         
     | 
| 
      
 142 
     | 
    
         
            +
                      :display_url => 'http://www.demcodez.com'
         
     | 
| 
      
 143 
     | 
    
         
            +
                    }        
         
     | 
| 
      
 144 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 145 
     | 
    
         
            +
                }    
         
     | 
| 
      
 146 
     | 
    
         
            +
              ]
         
     | 
| 
      
 147 
     | 
    
         
            +
            )
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
            unless $campaign.errors.empty?
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
              puts "ERROR WHEN UPDATING AD GROUPS:"
         
     | 
| 
      
 152 
     | 
    
         
            +
              puts $campaign.errors.full_messages.join("\n")
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
            else
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
              # reload campaign
         
     | 
| 
      
 157 
     | 
    
         
            +
              $campaign = Adapi::Campaign.find_complete($campaign.id)
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
              $campaign_attributes = $campaign.attributes
         
     | 
| 
      
 160 
     | 
    
         
            +
              $criteria = $campaign_attributes.delete(:criteria)
         
     | 
| 
      
 161 
     | 
    
         
            +
              $ad_groups = $campaign_attributes.delete(:ad_groups)
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
              puts "\nCAMPAIGN UPDATED\n"
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
              puts "\nCAMPAIGN DATA:"
         
     | 
| 
      
 166 
     | 
    
         
            +
              pp $campaign_attributes
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
              puts "\nCAMPAIGN CRITERIA:"
         
     | 
| 
      
 169 
     | 
    
         
            +
              pp $criteria
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
              puts "\nAD GROUPS (#{$ad_groups.size}):"
         
     | 
| 
      
 172 
     | 
    
         
            +
              $ad_groups.each_with_index do |ad_group, i| 
         
     | 
| 
      
 173 
     | 
    
         
            +
                puts "\nAD GROUP #{i + 1}:\n"
         
     | 
| 
      
 174 
     | 
    
         
            +
                pp ad_group.attributes
         
     | 
| 
      
 175 
     | 
    
         
            +
              end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
            require 'adapi'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'add_text_ad'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            $new_ad = $ad.update(
         
     | 
| 
      
 7 
     | 
    
         
            +
              :status => 'ACTIVE',
         
     | 
| 
      
 8 
     | 
    
         
            +
              :headline => "Code like Trinity",
         
     | 
| 
      
 9 
     | 
    
         
            +
              :description1 => 'Need mad update skills?',
         
     | 
| 
      
 10 
     | 
    
         
            +
              :description2 => 'Check out my updates!',
         
     | 
| 
      
 11 
     | 
    
         
            +
              :url => 'http://www.demupdatez.com',
         
     | 
| 
      
 12 
     | 
    
         
            +
              :display_url => 'http://www.demupdatez.com'
         
     | 
| 
      
 13 
     | 
    
         
            +
            )
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            $new_ad = Adapi::Ad::TextAd.find(:first, :ad_group_id => $new_ad.ad_group_id, :id => $new_ad.id )
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            puts "\nUPDATED (AND RELOADED) AD:"
         
     | 
| 
      
 18 
     | 
    
         
            +
            pp $new_ad.attributes
         
     | 
    
        data/lib/adapi/ad/text_ad.rb
    CHANGED
    
    | 
         @@ -7,19 +7,23 @@ module Adapi 
     | 
|
| 
       7 
7 
     | 
    
         
             
              #
         
     | 
| 
       8 
8 
     | 
    
         
             
              class Ad::TextAd < Ad
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                 
     | 
| 
      
 10 
     | 
    
         
            +
                ATTRIBUTES = [ :headline, :description1, :description2 ]
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                attr_accessor *ATTRIBUTES
         
     | 
| 
       11 
13 
     | 
    
         | 
| 
       12 
14 
     | 
    
         
             
                def attributes
         
     | 
| 
       13 
     | 
    
         
            -
                  super.merge 
     | 
| 
      
 15 
     | 
    
         
            +
                  super.merge Hash[ ATTRIBUTES.map { |k| [k, self.send(k)] } ]
         
     | 
| 
       14 
16 
     | 
    
         
             
                end
         
     | 
| 
       15 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
                alias to_hash attributes
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       16 
20 
     | 
    
         
             
                def initialize(params = {})
         
     | 
| 
       17 
21 
     | 
    
         
             
                  params[:service_name] = :AdGroupAdService
         
     | 
| 
       18 
22 
     | 
    
         | 
| 
       19 
23 
     | 
    
         
             
                  @xsi_type = 'TextAd'
         
     | 
| 
       20 
24 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
       22 
     | 
    
         
            -
                    self.send 
     | 
| 
      
 25 
     | 
    
         
            +
                  ATTRIBUTES.each do |param_name|
         
     | 
| 
      
 26 
     | 
    
         
            +
                    self.send("#{param_name}=", params[param_name])
         
     | 
| 
       23 
27 
     | 
    
         
             
                  end
         
     | 
| 
       24 
28 
     | 
    
         | 
| 
       25 
29 
     | 
    
         
             
                  super(params)
         
     | 
| 
         @@ -30,57 +34,58 @@ module Adapi 
     | 
|
| 
       30 
34 
     | 
    
         
             
                end
         
     | 
| 
       31 
35 
     | 
    
         | 
| 
       32 
36 
     | 
    
         
             
                def create
         
     | 
| 
      
 37 
     | 
    
         
            +
                  operand = self.attributes.delete_if do |k|
         
     | 
| 
      
 38 
     | 
    
         
            +
                    [ :campaign_id, :ad_group_id, :id, :status ].include?(k.to_sym)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end.symbolize_keys
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
       33 
41 
     | 
    
         
             
                  operation = {
         
     | 
| 
       34 
42 
     | 
    
         
             
                    :operator => 'ADD',
         
     | 
| 
       35 
43 
     | 
    
         
             
                    :operand => {
         
     | 
| 
       36 
44 
     | 
    
         
             
                      :ad_group_id => @ad_group_id,
         
     | 
| 
       37 
45 
     | 
    
         
             
                      :status => @status,
         
     | 
| 
       38 
     | 
    
         
            -
                      :ad =>  
     | 
| 
      
 46 
     | 
    
         
            +
                      :ad => operand
         
     | 
| 
       39 
47 
     | 
    
         
             
                    }
         
     | 
| 
       40 
48 
     | 
    
         
             
                  }
         
     | 
| 
       41 
49 
     | 
    
         | 
| 
       42 
50 
     | 
    
         
             
                  response = self.mutate(operation)
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                  # check for  
     | 
| 
       45 
     | 
    
         
            -
                  #  
     | 
| 
       46 
     | 
    
         
            -
                   
     | 
| 
       47 
     | 
    
         
            -
                     
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                      { :key => error }
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  # check for PolicyViolationErrors, set exemptions and try again
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # TODO for now, this is only done once. how about setting a number of retries?
         
     | 
| 
      
 54 
     | 
    
         
            +
                  unless self.errors[:PolicyViolationError].empty?
         
     | 
| 
      
 55 
     | 
    
         
            +
                    operation[:exemption_requests] = self.errors[:PolicyViolationError].map do |error_key|
         
     | 
| 
      
 56 
     | 
    
         
            +
                      { :key => error_key }
         
     | 
| 
       50 
57 
     | 
    
         
             
                    end
         
     | 
| 
       51 
58 
     | 
    
         | 
| 
       52 
59 
     | 
    
         
             
                    self.errors.clear
         
     | 
| 
       53 
60 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
                    response = self.mutate(operation) 
     | 
| 
      
 61 
     | 
    
         
            +
                    response = self.mutate(operation)
         
     | 
| 
       55 
62 
     | 
    
         
             
                  end
         
     | 
| 
       56 
63 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
                  return false unless  
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
      
 64 
     | 
    
         
            +
                  return false unless self.errors.empty?
         
     | 
| 
      
 65 
     | 
    
         
            +
                      
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # set ad id
         
     | 
| 
       59 
67 
     | 
    
         
             
                  self.id = response[:value].first[:ad][:id] rescue nil
         
     | 
| 
       60 
68 
     | 
    
         | 
| 
       61 
69 
     | 
    
         
             
                  true
         
     | 
| 
       62 
70 
     | 
    
         
             
                end
         
     | 
| 
       63 
71 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
                #  
     | 
| 
       65 
     | 
    
         
            -
                #  
     | 
| 
       66 
     | 
    
         
            -
                #  
     | 
| 
      
 72 
     | 
    
         
            +
                # except for status, we cannot edit ad fields
         
     | 
| 
      
 73 
     | 
    
         
            +
                # gotta delete an ad and create a new one instead
         
     | 
| 
      
 74 
     | 
    
         
            +
                # this means that this method returns new ad id! 
         
     | 
| 
      
 75 
     | 
    
         
            +
                # 
         
     | 
| 
      
 76 
     | 
    
         
            +
                # REFACTOR this method shpould be removed (but that might break something,
         
     | 
| 
      
 77 
     | 
    
         
            +
                # os let's keep it here for the moment)
         
     | 
| 
       67 
78 
     | 
    
         
             
                #
         
     | 
| 
       68 
79 
     | 
    
         
             
                def update(params = {})
         
     | 
| 
       69 
     | 
    
         
            -
                  # set  
     | 
| 
       70 
     | 
    
         
            -
                   
     | 
| 
       71 
     | 
    
         
            -
                   
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
                  
         
     | 
| 
       74 
     | 
    
         
            -
                   
     | 
| 
       75 
     | 
    
         
            -
                    :operator => 'SET', 
         
     | 
| 
       76 
     | 
    
         
            -
                    :operand => {
         
     | 
| 
       77 
     | 
    
         
            -
                      :ad_group_id => self.ad_group_id,
         
     | 
| 
       78 
     | 
    
         
            -
                      :ad => updated_params.merge(:id => self.id),
         
     | 
| 
       79 
     | 
    
         
            -
                      :status => updated_status
         
     | 
| 
       80 
     | 
    
         
            -
                    }
         
     | 
| 
       81 
     | 
    
         
            -
                  )
         
     | 
| 
      
 80 
     | 
    
         
            +
                  # set attributes for the "updated" ad
         
     | 
| 
      
 81 
     | 
    
         
            +
                  create_attributes = self.attributes.merge(params).symbolize_keys
         
     | 
| 
      
 82 
     | 
    
         
            +
                  create_attributes.delete(:id)
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  # delete current ad
         
     | 
| 
      
 85 
     | 
    
         
            +
                  return false unless self.destroy
         
     | 
| 
       82 
86 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
                   
     | 
| 
      
 87 
     | 
    
         
            +
                  # create new add
         
     | 
| 
      
 88 
     | 
    
         
            +
                  TextAd.create(create_attributes)
         
     | 
| 
       84 
89 
     | 
    
         
             
                end
         
     | 
| 
       85 
90 
     | 
    
         | 
| 
       86 
91 
     | 
    
         
             
                def find # == refresh
         
     | 
| 
         @@ -101,13 +106,12 @@ module Adapi 
     | 
|
| 
       101 
106 
     | 
    
         
             
                  # supported condition parameters: ad_group_id and id
         
     | 
| 
       102 
107 
     | 
    
         
             
                  predicates = [ :ad_group_id, :id ].map do |param_name|
         
     | 
| 
       103 
108 
     | 
    
         
             
                    if params[param_name]
         
     | 
| 
       104 
     | 
    
         
            -
                       
     | 
| 
       105 
     | 
    
         
            -
                      {:field => param_name.to_s.camelcase, :operator => 'IN', :values => value }
         
     | 
| 
      
 109 
     | 
    
         
            +
                      { :field => param_name.to_s.camelcase, :operator => 'IN', :values => Array( params[param_name] ) }
         
     | 
| 
       106 
110 
     | 
    
         
             
                    end
         
     | 
| 
       107 
111 
     | 
    
         
             
                  end.compact
         
     | 
| 
       108 
112 
     | 
    
         | 
| 
       109 
113 
     | 
    
         
             
                  selector = {
         
     | 
| 
       110 
     | 
    
         
            -
                    :fields => ['Id', 'Headline'],
         
     | 
| 
      
 114 
     | 
    
         
            +
                    :fields => ['Id', 'AdGroupId', 'Headline' ],
         
     | 
| 
       111 
115 
     | 
    
         
             
                    :ordering => [{:field => 'Id', :sort_order => 'ASCENDING'}],
         
     | 
| 
       112 
116 
     | 
    
         
             
                    :predicates => predicates
         
     | 
| 
       113 
117 
     | 
    
         
             
                  }
         
     | 
| 
         @@ -121,22 +125,8 @@ module Adapi 
     | 
|
| 
       121 
125 
     | 
    
         
             
                  end
         
     | 
| 
       122 
126 
     | 
    
         | 
| 
       123 
127 
     | 
    
         
             
                  # TODO convert to TextAd instances
         
     | 
| 
       124 
     | 
    
         
            -
                  # PS: we already have ad_group_id parameter
         
     | 
| 
       125 
128 
     | 
    
         
             
                  first_only ? response.first : response
         
     | 
| 
       126 
129 
     | 
    
         
             
                end
         
     | 
| 
       127 
130 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
                # Converts text ad data to hash - of the same structure which is used when
         
     | 
| 
       129 
     | 
    
         
            -
                # creating a complete campaign.
         
     | 
| 
       130 
     | 
    
         
            -
                #
         
     | 
| 
       131 
     | 
    
         
            -
                def to_hash
         
     | 
| 
       132 
     | 
    
         
            -
                  {
         
     | 
| 
       133 
     | 
    
         
            -
                    :headline => self[:headline],
         
     | 
| 
       134 
     | 
    
         
            -
                    :description1 => self[:description1],
         
     | 
| 
       135 
     | 
    
         
            -
                    :description2 => self[:description2],
         
     | 
| 
       136 
     | 
    
         
            -
                    :url => self[:url],
         
     | 
| 
       137 
     | 
    
         
            -
                    :display_url => self[:display_url]
         
     | 
| 
       138 
     | 
    
         
            -
                  }
         
     | 
| 
       139 
     | 
    
         
            -
                end
         
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
131 
     | 
    
         
             
              end
         
     | 
| 
       142 
132 
     | 
    
         
             
            end
         
     | 
    
        data/lib/adapi/ad.rb
    CHANGED
    
    | 
         @@ -6,7 +6,9 @@ module Adapi 
     | 
|
| 
       6 
6 
     | 
    
         
             
              # wraps all types of ads: text ads, image ads...
         
     | 
| 
       7 
7 
     | 
    
         
             
              class Ad < Api
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                 
     | 
| 
      
 9 
     | 
    
         
            +
                # REFACTOR attributes
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                attr_accessor :id, :ad_group_id, :url, :display_url, :approval_status,
         
     | 
| 
       10 
12 
     | 
    
         
             
                  :disapproval_reasons, :trademark_disapproved
         
     | 
| 
       11 
13 
     | 
    
         | 
| 
       12 
14 
     | 
    
         
             
                validates_presence_of :ad_group_id
         
     | 
| 
         @@ -14,7 +16,8 @@ module Adapi 
     | 
|
| 
       14 
16 
     | 
    
         
             
                # PS: create won't work with id and ad_group_id
         
     | 
| 
       15 
17 
     | 
    
         
             
                # 'id' => id, 'ad_group_id' => ad_group_id, 
         
     | 
| 
       16 
18 
     | 
    
         
             
                def attributes
         
     | 
| 
       17 
     | 
    
         
            -
                  super.merge(' 
     | 
| 
      
 19 
     | 
    
         
            +
                  super.merge( 'id' => id, 'ad_group_id' => ad_group_id, 
         
     | 
| 
      
 20 
     | 
    
         
            +
                    'url' => url, 'display_url' => display_url )
         
     | 
| 
       18 
21 
     | 
    
         
             
                end
         
     | 
| 
       19 
22 
     | 
    
         | 
| 
       20 
23 
     | 
    
         
             
                def initialize(params = {})
         
     | 
| 
         @@ -33,15 +36,14 @@ module Adapi 
     | 
|
| 
       33 
36 
     | 
    
         
             
                  response = self.mutate(
         
     | 
| 
       34 
37 
     | 
    
         
             
                    :operator => 'REMOVE',
         
     | 
| 
       35 
38 
     | 
    
         
             
                    :operand => {
         
     | 
| 
       36 
     | 
    
         
            -
                      :ad_group_id =>  
     | 
| 
       37 
     | 
    
         
            -
                      :ad => { :id =>  
     | 
| 
      
 39 
     | 
    
         
            +
                      :ad_group_id => @ad_group_id,
         
     | 
| 
      
 40 
     | 
    
         
            +
                      :ad => { :id => @id, :xsi_type => 'Ad' }
         
     | 
| 
       38 
41 
     | 
    
         
             
                    }
         
     | 
| 
       39 
42 
     | 
    
         
             
                  )
         
     | 
| 
       40 
43 
     | 
    
         | 
| 
       41 
44 
     | 
    
         
             
                  (response and response[:value]) ? true : false
         
     | 
| 
       42 
45 
     | 
    
         
             
                end
         
     | 
| 
       43 
46 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
47 
     | 
    
         
             
                # ad-specific mutate wrapper, deals with PolicyViolations for ads
         
     | 
| 
       46 
48 
     | 
    
         
             
                #
         
     | 
| 
       47 
49 
     | 
    
         
             
                def mutate(operation)
         
     | 
| 
         @@ -53,25 +55,34 @@ module Adapi 
     | 
|
| 
       53 
55 
     | 
    
         
             
                    op
         
     | 
| 
       54 
56 
     | 
    
         
             
                  end
         
     | 
| 
       55 
57 
     | 
    
         | 
| 
       56 
     | 
    
         
            -
                  begin 
     | 
| 
      
 58 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 59 
     | 
    
         
            +
                    
         
     | 
| 
       57 
60 
     | 
    
         
             
                    response = @service.mutate(operation)
         
     | 
| 
       58 
     | 
    
         
            -
                
         
     | 
| 
       59 
     | 
    
         
            -
                  rescue AdsCommon::Errors::HttpError => e
         
     | 
| 
       60 
     | 
    
         
            -
                    self.errors.add(:base, e.message)
         
     | 
| 
       61 
61 
     | 
    
         | 
| 
       62 
     | 
    
         
            -
                   
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                    # return PolicyViolations so they can be sent again
         
     | 
| 
      
 62 
     | 
    
         
            +
                  rescue *API_EXCEPTIONS => e
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    # return PolicyViolations in specific format so they can be sent again
         
     | 
| 
      
 65 
     | 
    
         
            +
                    # see adwords-api gem example for details: handle_policy_violation_error.rb
         
     | 
| 
       65 
66 
     | 
    
         
             
                    e.errors.each do |error|
         
     | 
| 
       66 
     | 
    
         
            -
                       
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
                       
     | 
| 
       69 
     | 
    
         
            -
                         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
      
 67 
     | 
    
         
            +
                      # error[:xsi_type] seems to be broken, so using also alternative key
         
     | 
| 
      
 68 
     | 
    
         
            +
                      # also could try: :"@xsi:type" (but api_error_type seems to be more robust)
         
     | 
| 
      
 69 
     | 
    
         
            +
                      if (error[:xsi_type] == 'PolicyViolationError') || (error[:api_error_type] == 'PolicyViolationError')
         
     | 
| 
      
 70 
     | 
    
         
            +
                        if error[:is_exemptable]
         
     | 
| 
      
 71 
     | 
    
         
            +
                          self.errors.add(:PolicyViolationError, error[:key])
         
     | 
| 
      
 72 
     | 
    
         
            +
                        end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                        # return also exemptable errors, operation may fail even with them
         
     | 
| 
      
 75 
     | 
    
         
            +
                        self.errors.add(:base, "violated %s policy: \"%s\" on \"%s\"" % [
         
     | 
| 
      
 76 
     | 
    
         
            +
                          error[:is_exemptable] ? 'exemptable' : 'non-exemptable', 
         
     | 
| 
      
 77 
     | 
    
         
            +
                          error[:key][:policy_name], 
         
     | 
| 
      
 78 
     | 
    
         
            +
                          error[:key][:violating_text]
         
     | 
| 
      
 79 
     | 
    
         
            +
                        ])
         
     | 
| 
      
 80 
     | 
    
         
            +
                      else
         
     | 
| 
      
 81 
     | 
    
         
            +
                        self.errors.add(:base, e.message)
         
     | 
| 
       71 
82 
     | 
    
         
             
                      end
         
     | 
| 
       72 
     | 
    
         
            -
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end # of errors.each
         
     | 
| 
       73 
84 
     | 
    
         
             
                  end
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
       75 
86 
     | 
    
         
             
                  response
         
     | 
| 
       76 
87 
     | 
    
         
             
                end
         
     | 
| 
       77 
88 
     | 
    
         | 
    
        data/lib/adapi/ad_group.rb
    CHANGED
    
    | 
         @@ -1,31 +1,49 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # encoding: utf-8
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            # This class handles operations with ad_groups
         
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            # https://developers.google.com/adwords/api/docs/reference/latest/AdGroupService
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
       3 
7 
     | 
    
         
             
            module Adapi
         
     | 
| 
       4 
8 
     | 
    
         
             
              class AdGroup < Api
         
     | 
| 
       5 
9 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
                 
     | 
| 
      
 10 
     | 
    
         
            +
                ATTRIBUTES = [ :id, :campaign_id, :name, :status, :bids, :keywords, :ads ]
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                attr_accessor *ATTRIBUTES 
         
     | 
| 
       7 
13 
     | 
    
         | 
| 
       8 
14 
     | 
    
         
             
                validates_presence_of :campaign_id, :name, :status
         
     | 
| 
       9 
15 
     | 
    
         
             
                validates_inclusion_of :status, :in => %w{ ENABLED PAUSED DELETED }
         
     | 
| 
       10 
16 
     | 
    
         | 
| 
       11 
17 
     | 
    
         
             
                def attributes
         
     | 
| 
       12 
     | 
    
         
            -
                  super.merge 
     | 
| 
      
 18 
     | 
    
         
            +
                  super.merge Hash[ ATTRIBUTES.map { |k| [k, self.send(k)] } ]
         
     | 
| 
       13 
19 
     | 
    
         
             
                end
         
     | 
| 
       14 
20 
     | 
    
         | 
| 
      
 21 
     | 
    
         
            +
                alias to_hash attributes
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       15 
23 
     | 
    
         
             
                def initialize(params = {})
         
     | 
| 
       16 
24 
     | 
    
         
             
                  params[:service_name] = :AdGroupService
         
     | 
| 
       17 
25 
     | 
    
         | 
| 
       18 
26 
     | 
    
         
             
                  @xsi_type = 'AdGroup'
         
     | 
| 
       19 
27 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
       21 
     | 
    
         
            -
                    self.send 
     | 
| 
      
 28 
     | 
    
         
            +
                  ATTRIBUTES.each do |param_name|
         
     | 
| 
      
 29 
     | 
    
         
            +
                    self.send("#{param_name}=", params[param_name])
         
     | 
| 
       22 
30 
     | 
    
         
             
                  end
         
     | 
| 
       23 
31 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                   
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                   
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                   
     | 
| 
      
 32 
     | 
    
         
            +
                  @keywords ||= []
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  @ads ||= []
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  super(params)
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                # convert bids to GoogleApi format
         
     | 
| 
      
 40 
     | 
    
         
            +
                #
         
     | 
| 
      
 41 
     | 
    
         
            +
                # can be either string (just xsi_type) or hash (xsi_type with params)
         
     | 
| 
      
 42 
     | 
    
         
            +
                # although I'm not sure if just string makes sense in this case
         
     | 
| 
      
 43 
     | 
    
         
            +
                #
         
     | 
| 
      
 44 
     | 
    
         
            +
                def bids=(params = {})
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @bids = params
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
       29 
47 
     | 
    
         
             
                  if @bids
         
     | 
| 
       30 
48 
     | 
    
         
             
                    unless @bids.is_a?(Hash)
         
     | 
| 
       31 
49 
     | 
    
         
             
                      @bids = { :xsi_type => @bids }
         
     | 
| 
         @@ -42,11 +60,6 @@ module Adapi 
     | 
|
| 
       42 
60 
     | 
    
         
             
                      end
         
     | 
| 
       43 
61 
     | 
    
         
             
                    end
         
     | 
| 
       44 
62 
     | 
    
         
             
                  end
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                  @keywords ||= []
         
     | 
| 
       47 
     | 
    
         
            -
                  @ads ||= []
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                  super(params)
         
     | 
| 
       50 
63 
     | 
    
         
             
                end
         
     | 
| 
       51 
64 
     | 
    
         | 
| 
       52 
65 
     | 
    
         
             
                def create
         
     | 
| 
         @@ -90,22 +103,98 @@ module Adapi 
     | 
|
| 
       90 
103 
     | 
    
         | 
| 
       91 
104 
     | 
    
         
             
                  true
         
     | 
| 
       92 
105 
     | 
    
         
             
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                def update(params = {})
         
     | 
| 
      
 108 
     | 
    
         
            +
                  # step 1. update core attributes
         
     | 
| 
      
 109 
     | 
    
         
            +
                  core_attributes = [ :id, :campaign_id, :name, :status, :bids ]
         
     | 
| 
      
 110 
     | 
    
         
            +
                  # get operand in google format 
         
     | 
| 
      
 111 
     | 
    
         
            +
                  # parse the given params by initialize method...
         
     | 
| 
      
 112 
     | 
    
         
            +
                  ad_group = Adapi::AdGroup.new(params)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  # HOTFIX remove :service_name param inserted by initialize method
         
     | 
| 
      
 114 
     | 
    
         
            +
                  params.delete(:service_name)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  # ...and load parsed params back into the hash
         
     | 
| 
      
 116 
     | 
    
         
            +
                  core_params = Hash[ core_attributes.map { |k| [k, ad_group.send(k)] if params[k].present? }.compact ]
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  response = ad_group.mutate(
         
     | 
| 
      
 119 
     | 
    
         
            +
                    :operator => 'SET', 
         
     | 
| 
      
 120 
     | 
    
         
            +
                    :operand => core_params.merge( :id => @id, :campaign_id => @campaign_id )
         
     | 
| 
      
 121 
     | 
    
         
            +
                  )
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  return false unless (response and response[:value])
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                  # step 2. update keywords
         
     | 
| 
      
 126 
     | 
    
         
            +
                  # delete everything and create new keywords
         
     | 
| 
      
 127 
     | 
    
         
            +
                  if params[:keywords] and not params[:keywords].empty?
         
     | 
| 
      
 128 
     | 
    
         
            +
                    # delete existing keywords
         
     | 
| 
      
 129 
     | 
    
         
            +
                    # OPTIMIZE should be all in one request
         
     | 
| 
      
 130 
     | 
    
         
            +
                    Keyword.find(:all, :ad_group_id => @id).keywords.each do |keyword|
         
     | 
| 
      
 131 
     | 
    
         
            +
                      Keyword.new(:ad_group_id => @id).delete(keyword[:text][:criterion][:id])
         
     | 
| 
      
 132 
     | 
    
         
            +
                    end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                    # create new keywords
         
     | 
| 
      
 135 
     | 
    
         
            +
                    result = Adapi::Keyword.create(
         
     | 
| 
      
 136 
     | 
    
         
            +
                      :ad_group_id => @id,
         
     | 
| 
      
 137 
     | 
    
         
            +
                      :keywords => params[:keywords]
         
     | 
| 
      
 138 
     | 
    
         
            +
                    )
         
     | 
| 
      
 139 
     | 
    
         
            +
                    
         
     | 
| 
      
 140 
     | 
    
         
            +
                    unless result.errors.empty?
         
     | 
| 
      
 141 
     | 
    
         
            +
                      self.errors.add("Keyword", result.errors.to_a)
         
     | 
| 
      
 142 
     | 
    
         
            +
                      return false 
         
     | 
| 
      
 143 
     | 
    
         
            +
                    end
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                  # step 3. update ads
         
     | 
| 
      
 147 
     | 
    
         
            +
                  # ads can't be updated, gotta remove them all and add new ads
         
     | 
| 
      
 148 
     | 
    
         
            +
                  if params[:ads] and not params[:ads].empty?
         
     | 
| 
      
 149 
     | 
    
         
            +
                    # remove all existing ads
         
     | 
| 
      
 150 
     | 
    
         
            +
                    self.find_ads.each do |ad| 
         
     | 
| 
      
 151 
     | 
    
         
            +
                      unless ad.destroy
         
     | 
| 
      
 152 
     | 
    
         
            +
                        self.errors.add("Ad \"#{ad.headline}\"", ["cannot be deleted"])
         
     | 
| 
      
 153 
     | 
    
         
            +
                        return false 
         
     | 
| 
      
 154 
     | 
    
         
            +
                      end
         
     | 
| 
      
 155 
     | 
    
         
            +
                    end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                    # create new ads
         
     | 
| 
      
 158 
     | 
    
         
            +
                    params[:ads].each do |ad|
         
     | 
| 
      
 159 
     | 
    
         
            +
                      ad = Adapi::Ad::TextAd.create( ad.merge(:ad_group_id => @id) )
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                      unless ad.errors.empty?
         
     | 
| 
      
 162 
     | 
    
         
            +
                        self.errors.add("Ad \"#{ad.headline}\"", ad.errors.to_a)
         
     | 
| 
      
 163 
     | 
    
         
            +
                        return false 
         
     | 
| 
      
 164 
     | 
    
         
            +
                      end
         
     | 
| 
      
 165 
     | 
    
         
            +
                    end
         
     | 
| 
      
 166 
     | 
    
         
            +
                  end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                  true
         
     | 
| 
      
 169 
     | 
    
         
            +
                end
         
     | 
| 
       93 
170 
     | 
    
         | 
| 
      
 171 
     | 
    
         
            +
                # PS: perhaps also change the ad_group name when deleting
         
     | 
| 
      
 172 
     | 
    
         
            +
                def delete
         
     | 
| 
      
 173 
     | 
    
         
            +
                  update(:status => 'DELETED')  
         
     | 
| 
      
 174 
     | 
    
         
            +
                end
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
       94 
176 
     | 
    
         
             
                def self.find(amount = :all, params = {})
         
     | 
| 
       95 
177 
     | 
    
         
             
                  params.symbolize_keys!
         
     | 
| 
       96 
178 
     | 
    
         
             
                  first_only = (amount.to_sym == :first)
         
     | 
| 
      
 179 
     | 
    
         
            +
                  # by default, exclude ad_groups with status DELETED
         
     | 
| 
      
 180 
     | 
    
         
            +
                  params[:status] ||= %w{ ENABLED PAUSED }
         
     | 
| 
       97 
181 
     | 
    
         | 
| 
       98 
182 
     | 
    
         
             
                  raise "Campaign ID is required" unless params[:campaign_id]
         
     | 
| 
       99 
183 
     | 
    
         | 
| 
       100 
     | 
    
         
            -
                  predicates = [ :campaign_id, :id ].map do |param_name|
         
     | 
| 
       101 
     | 
    
         
            -
                    if params[param_name]
         
     | 
| 
       102 
     | 
    
         
            -
                       
     | 
| 
       103 
     | 
    
         
            -
                      {:field => param_name.to_s.camelcase, :operator => 'IN', :values => value }
         
     | 
| 
      
 184 
     | 
    
         
            +
                  predicates = [ :campaign_id, :id, :name, :status ].map do |param_name|
         
     | 
| 
      
 185 
     | 
    
         
            +
                    if params[param_name].present?
         
     | 
| 
      
 186 
     | 
    
         
            +
                      {:field => param_name.to_s.camelcase, :operator => 'IN', :values => Array( params[param_name] ) }
         
     | 
| 
       104 
187 
     | 
    
         
             
                    end
         
     | 
| 
       105 
188 
     | 
    
         
             
                  end.compact
         
     | 
| 
       106 
189 
     | 
    
         | 
| 
      
 190 
     | 
    
         
            +
                  select_fields = %w{ Id CampaignId Name Status } 
         
     | 
| 
      
 191 
     | 
    
         
            +
                  # add Bids atributes
         
     | 
| 
      
 192 
     | 
    
         
            +
                  select_fields += %w{ EnhancedCpcEnabled 
         
     | 
| 
      
 193 
     | 
    
         
            +
                    ProxyKeywordMaxCpc ProxySiteMaxCpc 
         
     | 
| 
      
 194 
     | 
    
         
            +
                    KeywordMaxCpc KeywordContentMaxCpc }
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
       107 
196 
     | 
    
         
             
                  selector = {
         
     | 
| 
       108 
     | 
    
         
            -
                    :fields =>  
     | 
| 
      
 197 
     | 
    
         
            +
                    :fields => select_fields,
         
     | 
| 
       109 
198 
     | 
    
         
             
                    :ordering => [{:field => 'Name', :sort_order => 'ASCENDING'}],
         
     | 
| 
       110 
199 
     | 
    
         
             
                    :predicates => predicates
         
     | 
| 
       111 
200 
     | 
    
         
             
                  }
         
     | 
| 
         @@ -124,6 +213,8 @@ module Adapi 
     | 
|
| 
       124 
213 
     | 
    
         
             
                    )
         
     | 
| 
       125 
214 
     | 
    
         
             
                  end
         
     | 
| 
       126 
215 
     | 
    
         | 
| 
      
 216 
     | 
    
         
            +
                  ad_groups.map! { |ad_group| AdGroup.new(ad_group) }
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
       127 
218 
     | 
    
         
             
                  first_only ? ad_groups.first : ad_groups
         
     | 
| 
       128 
219 
     | 
    
         
             
                end
         
     | 
| 
       129 
220 
     | 
    
         | 
| 
         @@ -136,16 +227,5 @@ module Adapi 
     | 
|
| 
       136 
227 
     | 
    
         
             
                  Ad::TextAd.find( (first_only ? :first : :all), :ad_group_id => self.id )
         
     | 
| 
       137 
228 
     | 
    
         
             
                end
         
     | 
| 
       138 
229 
     | 
    
         | 
| 
       139 
     | 
    
         
            -
                # Converts ad group data to hash - of the same structure which is used when
         
     | 
| 
       140 
     | 
    
         
            -
                # creating an ad group.
         
     | 
| 
       141 
     | 
    
         
            -
                #
         
     | 
| 
       142 
     | 
    
         
            -
                def to_hash
         
     | 
| 
       143 
     | 
    
         
            -
                  ad_group_hash = {
         
     | 
| 
       144 
     | 
    
         
            -
                    :id => self[:id],
         
     | 
| 
       145 
     | 
    
         
            -
                    :name => self[:name],
         
     | 
| 
       146 
     | 
    
         
            -
                    :status => self[:status]
         
     | 
| 
       147 
     | 
    
         
            -
                  }
         
     | 
| 
       148 
     | 
    
         
            -
                end
         
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
230 
     | 
    
         
             
              end
         
     | 
| 
       151 
231 
     | 
    
         
             
            end
         
     |