From 33d01d37a9835de6c23f69a6629a0e7ba80c3e6f Mon Sep 17 00:00:00 2001 From: Adrien Rambert Date: Mon, 10 Jun 2013 14:27:30 +0200 Subject: [PATCH] Add report 'phrase_organic' + 'display_sort' & 'display_filter' parameters --- Rakefile | 2 +- lib/semrush.rb | 3 +- lib/semrush/report.rb | 31 +++++++--- spec/semrush/report_spec.rb | 109 ++++++++++++++++++++++++++++++++++++ spec/semrush_spec.rb | 86 +--------------------------- spec/spec_helper.rb | 1 + 6 files changed, 139 insertions(+), 93 deletions(-) create mode 100644 spec/semrush/report_spec.rb diff --git a/Rakefile b/Rakefile index 30f0b16..6faf2b1 100644 --- a/Rakefile +++ b/Rakefile @@ -21,7 +21,7 @@ require "rspec/core/rake_task" desc "Run all test with spec" RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = %w[--color] - t.pattern = 'spec/*_spec.rb' + t.pattern = 'spec/**/*_spec.rb' end desc "Run tests" task :default => :spec diff --git a/lib/semrush.rb b/lib/semrush.rb index 10466dc..d814442 100644 --- a/lib/semrush.rb +++ b/lib/semrush.rb @@ -1,4 +1,5 @@ require 'uri' +require 'cgi' require 'net/http' require 'csv' require 'rubygems' @@ -8,7 +9,7 @@ require 'semrush/report' module Semrush - API_REPORT_URL = "http://%DB%.api.semrush.com/?action=report&type=%REPORT_TYPE%&%REQUEST_TYPE%=%REQUEST%&key=%API_KEY%&display_limit=%LIMIT%&display_offset=%OFFSET%&export=api&export_columns=%EXPORT_COLUMNS%" + API_REPORT_URL = "http://%DB%.api.semrush.com/?action=report&type=%REPORT_TYPE%&%REQUEST_TYPE%=%REQUEST%&key=%API_KEY%&display_limit=%LIMIT%&display_offset=%OFFSET%&export=api&export_columns=%EXPORT_COLUMNS%&display_sort=%DISPLAY_SORT%&display_filter=%DISPLAY_FILTER%" API_UNITS_URL = "http://www.semrush.com/users/countapiunits.html?key=%API_KEY%" mattr_accessor :api_key @@api_key = "" diff --git a/lib/semrush/report.rb b/lib/semrush/report.rb index 271b4ba..a08061e 100644 --- a/lib/semrush/report.rb +++ b/lib/semrush/report.rb @@ -9,7 +9,7 @@ module Semrush class Report DBS = [:us, :uk, :ru, :de, :fr, :es, :it, :br, :au] #"us" - for Google.com, "uk" - for Google.co.uk, "ru" - for Google.ru, "de" for Google.de, "fr" for Google.fr, "es" for Google.es, "it" for Google.it Beta, "br" for Google.com.br Beta, "au" for Google.com.au Beta. REPORT_TYPES = [:domain_rank, :domain_organic, :domain_adwords, :domain_organic_organic, :domain_adwords_adwords, :domain_organic_adwords, :domain_adwords_organic, - :phrase_this, :phrase_related, + :phrase_this, :phrase_organic, :phrase_related, :url_organic, :url_adwords] REQUEST_TYPES = [:domain, :phrase, :url] @@ -111,7 +111,7 @@ def basics params = {} domain? ? request(params.merge(:report_type => :domain_rank)) : request(params.merge(:report_type => :phrase_this)) end - # Organic Keywords report + # Organic report # Can be called for a domain or a URL. # Default columns for a domain: # * Ph - The search query which the site has within the first 20 Google search results @@ -135,11 +135,22 @@ def basics params = {} # * Tc - The ratio of the estimated cost of buying the same number of visitors for this search query to the estimated cost of purchasing the same number of targeted visitors coming to this URL # * Nr - The number of search results - how many pages does Google know for this query # * Td - Dynamics of change in the number of search queries in the past 12 months (estimated) + # Default columns for a phrase: + # * Dn - A site name + # * Ur - Target URL + def organic params = {} + case + when url? then request(params.merge(:report_type => :url_organic)) + when phrase? then request(params.merge(:report_type => :phrase_organic)) + else request(params.merge(:report_type => :domain_organic)) + end + end def keywords_organic params = {} - url? ? request(params.merge(:report_type => :url_organic)) : request(params.merge(:report_type => :domain_organic)) + warn "[DEPRECATION] `keywords_organic` is deprecated. Please use `organic` instead." + organic(params) end - # AdWords keywords report + # AdWords report # Can be called for a domain or a URL. # Default columns for a domain: # * Ph - Search query which the site buys in AdWords in Google @@ -164,9 +175,13 @@ def keywords_organic params = {} # * Tc - The ratio of the estimated cost of buying the same number of visitors for this search query to the estimated cost of purchasing the same number of targeted visitors coming to this URL # * Nr - The number of search results - how many pages does Google know for this query # * Td - Dynamics of change in the number of search queries in the past 12 months (estimated) - def keywords_adwords params = {} + def adwords params = {} url? ? request(params.merge(:report_type => :url_adwords)) : request(params.merge(:report_type => :domain_adwords)) end + def keywords_adwords params = {} + warn "[DEPRECATION] `keywords_adwords` is deprecated. Please use `adwords` instead." + adwords(params) + end # Competitors in organic search report # Default columns: @@ -237,7 +252,7 @@ def request params = {} if v.blank? temp_url.gsub!(/&[^&=]+=%#{k.to_s}%/i, '') else - temp_url.gsub!("%#{k.to_s.upcase}%", URI.escape(v.to_s).gsub('&', '%26')) + temp_url.gsub!("%#{k.to_s.upcase}%", URI.escape(v.to_s).gsub('&', '%26').gsub('+', '%2B')) end } puts "[Semrush query] URL: #{temp_url}" if Semrush.debug @@ -261,6 +276,8 @@ def request params = {} # * limit - number of results returned # * offset - says to skip that many results before beginning to return results to you # * export_columns - list of column names, separated by coma. You may list just the column names you need in an order you need. + # * display_sort - a sorting as a String eg: 'am_asc' or 'am_desc'(read http://www.semrush.com/api) + # * display_filter - list of filters separated by "|" (maximum number - 25). A filter consists of ||| (read http://www.semrush.com/api) # # more details in http://www.semrush.com/api.html def validate_parameters params = {} @@ -268,7 +285,7 @@ def validate_parameters params = {} params.delete(:db) unless DBS.include?(params[:db].try(:to_sym)) params.delete(:report_type) unless REPORT_TYPES.include?(params[:report_type].try(:to_sym)) params.delete(:request_type) unless REQUEST_TYPES.include?(params[:request_type].try(:to_sym)) - @parameters = {:db => "us", :api_key => Semrush.api_key, :limit => "", :offset => "", :export_columns => ""}.merge(@parameters).merge(params) + @parameters = {:db => "us", :api_key => Semrush.api_key, :limit => "", :offset => "", :export_columns => "", :display_sort => "", :display_filter => ""}.merge(@parameters).merge(params) raise Semrush::Exception::Nolimit.new(self, "The limit parameter is missing: a limit is required.") unless @parameters[:limit].present? && @parameters[:limit].to_i>0 raise Semrush::Exception::BadArgument.new(self, "Request parameter is missing: Domain name, URL, or keywords are required.") unless @parameters[:request].present? raise Semrush::Exception::BadArgument.new(self, "Bad db: #{@parameters[:db]}") unless DBS.include?(@parameters[:db].try(:to_sym)) diff --git a/spec/semrush/report_spec.rb b/spec/semrush/report_spec.rb new file mode 100644 index 0000000..9742b19 --- /dev/null +++ b/spec/semrush/report_spec.rb @@ -0,0 +1,109 @@ +require 'spec_helper' + +describe "Reports:" do + before(:all) do #once (and could be modified by the following tests) + Semrush.config do |config| + config.api_key = API_KEY + config.debug = true + end + end + + describe Semrush, "running basic reports" do + it "works with the root method 'domain_rank'" do + lambda{Semrush::Report.new.domain_rank(:request_type => :domain, :request => "seobook.com", :db => :us, :limit => 5)}.should_not raise_error + end + end + + describe Semrush, "running domain reports" do + it "initializes correctly" do + lambda{Semrush::Report.domain("seobook.com", :db => :us)}.should_not raise_error + end + it "initializes correctly with params" do + lambda{Semrush::Report.domain("seobook.com", :db => :us)}.should_not raise_error + end + [:basics, :organic, :adwords].each do |method| + it "works with the method '#{method}'" do + lambda{@parsed = Semrush::Report.domain("seobook.com").send(method, :db => :us, :limit => 5)}.should_not raise_error + @parsed.should_not be_nil + @parsed.should be_a_kind_of(Array) + @parsed.first.should be_a_kind_of(Hash) if !@parsed.first.nil? + end + end + + end + + describe Semrush, "running url reports" do + it "initializes correctly" do + lambda{Semrush::Report.url("http://tools.seobook.com/", :db => :us)}.should_not raise_error + end + it "initializes correctly with params" do + lambda{Semrush::Report.url("http://tools.seobook.com/", :db => :us)}.should_not raise_error + end + [:organic, :adwords].each do |method| + it "works with the method '#{method}'" do + lambda{@parsed = Semrush::Report.url("http://tools.seobook.com/").send(method, :db => :us, :limit => 5)}.should_not raise_error + @parsed.should_not be_nil + @parsed.should be_a_kind_of(Array) + @parsed.first.should be_a_kind_of(Hash) if !@parsed.first.nil? + end + end + [:basics, :competitors_organic, :competitors_adwords, :competitors_organic_by_adwords, :competitors_adwords_by_organic].each do |method| + it "should not work with the method '#{method}'" do + lambda{@parsed = Semrush::Report.url("http://tools.seobook.com/").send(method, :db => :us, :limit => 5)}.should raise_error + end + end + end + + describe Semrush, "running phrase reports" do + it "initializes correctly" do + lambda{Semrush::Report.phrase("search+engine+optimization", :db => :us)}.should_not raise_error + end + it "initializes correctly with params" do + lambda{Semrush::Report.phrase("search+engine+optimization", :db => :us)}.should_not raise_error + end + [:basics, :related, :organic].each do |method| + it "works with the method '#{method}'" do + lambda{@parsed = Semrush::Report.phrase("search+engine+optimization").send(method, :db => :us, :limit => 5)}.should_not raise_error + @parsed.should_not be_nil + @parsed.should be_a_kind_of(Array) + @parsed.first.should be_a_kind_of(Hash) if !@parsed.first.nil? + end + end + [:basics, :related].each do |method| + it "deals correctly with & in phrase" do + lambda{Semrush::Report.phrase("calvin & hobbs").send(method, :db => :us, :limit => 5)}.should_not raise_error + end + end + end + + describe Semrush, "parameters positions in reports" do + it "could be set in the class method" do + lambda{Semrush::Report.domain("seobook.com", :db => :fr, :limit => 5, :offset => 2)}.should_not raise_error + end + it "could be set in the instance method" do + lambda{Semrush::Report.domain("seobook.com").organic(:db => :fr, :limit => 5, :offset => 2)}.should_not raise_error + end + it "both methods get the same results" do + in_class = Semrush::Report.domain("seobook.com", :db => :fr, :limit => 5, :offset => 2).organic + in_object = Semrush::Report.domain("seobook.com").organic(:db => :fr, :limit => 5, :offset => 2) + in_class.should == in_object + end + end + + describe Semrush, "using display_sort parameter" do + it "works with domain_organic" do + lambda{Semrush::Report.domain("wikipedia.org", :limit => 5, :display_sort => 'tr_asc').organic}.should_not raise_error + end + end + describe Semrush, "using display_filter parameter" do + it "returns an Array with elements if Po<5" do + r = Semrush::Report.domain("wikipedia.org", :limit => 5, :display_filter => '+|Po|Lt|5').organic + r.should be_a_kind_of Array + r.count.should > 0 + end + it "returns empty Array if Po<1" do + Semrush::Report.domain("wikipedia.org", :limit => 5, :display_filter => '+|Po|Lt|1').organic.should == [] + end + end + +end \ No newline at end of file diff --git a/spec/semrush_spec.rb b/spec/semrush_spec.rb index d27418b..8c474a8 100644 --- a/spec/semrush_spec.rb +++ b/spec/semrush_spec.rb @@ -1,5 +1,4 @@ require 'spec_helper' -API_KEY = ENV['API_KEY'] || "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" describe Semrush, "config" do it "comes from a module" do #simple test to init tests @@ -13,6 +12,7 @@ }.should raise_error(Semrush::Exception::BadApiKey) end end + describe "Requests:" do before(:all) do #once (and could be modified by the following tests) Semrush.config do |config| @@ -32,12 +32,6 @@ end end - describe Semrush, "running basic reports" do - it "works with the root method 'domain_rank'" do - lambda{Semrush::Report.new.domain_rank(:request_type => :domain, :request => "seobook.com", :db => :us, :limit => 5)}.should_not raise_error - end - end - describe Semrush, "log reports with before & after" do before(:all) do #once (and could be modified by the following tests) Semrush.config do |config| @@ -48,84 +42,8 @@ end end it "works" do - lambda{Semrush::Report.domain("seobook.com", :db => :us).keywords_organic(:limit => 5)}.should_not raise_error - end - end - - describe Semrush, "running domain reports" do - it "initializes correctly" do - lambda{Semrush::Report.domain("seobook.com", :db => :us)}.should_not raise_error - end - it "initializes correctly with params" do - lambda{Semrush::Report.domain("seobook.com", :db => :us)}.should_not raise_error - end - [:basics, :keywords_organic, :keywords_adwords].each do |method| - it "works with the method '#{method}'" do - lambda{@parsed = Semrush::Report.domain("seobook.com").send(method, :db => :us, :limit => 5)}.should_not raise_error - @parsed.should_not be_nil - @parsed.should be_a_kind_of(Array) - @parsed.first.should be_a_kind_of(Hash) if !@parsed.first.nil? - end - end - - end - - describe Semrush, "running url reports" do - it "initializes correctly" do - lambda{Semrush::Report.url("http://tools.seobook.com/", :db => :us)}.should_not raise_error - end - it "initializes correctly with params" do - lambda{Semrush::Report.url("http://tools.seobook.com/", :db => :us)}.should_not raise_error - end - [:keywords_organic, :keywords_adwords].each do |method| - it "works with the method '#{method}'" do - lambda{@parsed = Semrush::Report.url("http://tools.seobook.com/").send(method, :db => :us, :limit => 5)}.should_not raise_error - @parsed.should_not be_nil - @parsed.should be_a_kind_of(Array) - @parsed.first.should be_a_kind_of(Hash) if !@parsed.first.nil? - end - end - [:basics, :competitors_organic, :competitors_adwords, :competitors_organic_by_adwords, :competitors_adwords_by_organic].each do |method| - it "should not work with the method '#{method}'" do - lambda{@parsed = Semrush::Report.url("http://tools.seobook.com/").send(method, :db => :us, :limit => 5)}.should raise_error - end - end - end - - describe Semrush, "running phrase reports" do - it "initializes correctly" do - lambda{Semrush::Report.phrase("search+engine+optimization", :db => :us)}.should_not raise_error - end - it "initializes correctly with params" do - lambda{Semrush::Report.phrase("search+engine+optimization", :db => :us)}.should_not raise_error - end - [:basics, :related].each do |method| - it "works with the method '#{method}'" do - lambda{@parsed = Semrush::Report.phrase("search+engine+optimization").send(method, :db => :us, :limit => 5)}.should_not raise_error - @parsed.should_not be_nil - @parsed.should be_a_kind_of(Array) - @parsed.first.should be_a_kind_of(Hash) if !@parsed.first.nil? - end - end - [:basics, :related].each do |method| - it "deals correctly with & in phrase" do - lambda{Semrush::Report.phrase("calvin & hobbs").send(method, :db => :us, :limit => 5)}.should_not raise_error - end + lambda{Semrush::Report.domain("seobook.com", :db => :us).organic(:limit => 5)}.should_not raise_error end end - describe Semrush, "parameters in reports" do - it "could be set in the class method" do - lambda{Semrush::Report.domain("seobook.com", :db => :fr, :limit => 5, :offset => 2)}.should_not raise_error - end - it "could be set in the instance method" do - lambda{Semrush::Report.domain("seobook.com").keywords_organic(:db => :fr, :limit => 5, :offset => 2)}.should_not raise_error - end - it "both methods get the same results" do - in_class = Semrush::Report.domain("seobook.com", :db => :fr, :limit => 5, :offset => 2).keywords_organic - in_object = Semrush::Report.domain("seobook.com").keywords_organic(:db => :fr, :limit => 5, :offset => 2) - in_class.should == in_object - end - - end end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cb16dbc..fca5909 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,7 @@ require 'bundler/setup' require 'semrush' # and any other gems you need +API_KEY = ENV['API_KEY'] || "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" RSpec.configure do |config| # some (optional) config here