Permalink
Browse files

new model module to eventually replace resource and report

updated README with plans for Garb::Model rollout
  • Loading branch information...
1 parent aab84c7 commit ed33017bd9c041f41ca7b1effba87f221ae85626 @tpitale tpitale committed Oct 10, 2010
Showing with 228 additions and 5 deletions.
  1. +8 −1 README.md
  2. +2 −0 lib/garb.rb
  3. +0 −4 lib/garb/management/profile.rb
  4. +83 −0 lib/garb/model.rb
  5. +2 −0 lib/garb/report.rb
  6. +4 −0 test/test_helper.rb
  7. +129 −0 test/unit/garb/model_test.rb
View
@@ -6,7 +6,14 @@ Garb
Important Changes
=================
- Please read CHANGELOG
+ I've started to work on a new module for reporting. You can see that work
+ in lib/garb/model.rb. This will be the API to replace both Garb::Resource
+ and Garb::Report. However, it isn't complete yet, so I would advise against
+ using it right now. Resource and Report will be deprecated around 0.9.0 and
+ I'd like to remove them for 1.0.0. I will start this process after I've
+ updated the documentation and I feel that Garb::Model is in a solid place.
+
+ Please read CHANGELOG
Description
-----------
View
@@ -28,6 +28,8 @@
require 'garb/resource'
require 'garb/report'
+require 'garb/model'
+
# management
require 'garb/management/feed'
require 'garb/management/account'
@@ -31,10 +31,6 @@ def initialize(entry, session)
@web_property_id = properties['web_property_id']
end
- # def path
- # ['/accounts', self.account_id, 'webproperties', self.web_property_id, 'profiles', self.id].join('/')
- # end
-
# def goals
# end
end
View
@@ -0,0 +1,83 @@
+module Garb
+ module Model
+ MONTH = 2592000
+ URL = "https://www.google.com/analytics/feeds/data"
+
+ def metrics(*fields)
+ @metrics ||= ReportParameter.new(:metrics)
+ @metrics << fields
+ end
+
+ def dimensions(*fields)
+ @dimensions ||= ReportParameter.new(:dimensions)
+ @dimensions << fields
+ end
+
+ def set_instance_klass(klass)
+ @instance_klass = klass
+ end
+
+ def instance_klass
+ @instance_klass || OpenStruct
+ end
+
+ def results(profile, options = {})
+ default_params = build_default_params(profile)
+
+ param_set = [
+ default_params,
+ parse_filters(options).to_params,
+ parse_segment(options),
+ parse_sort(options).to_params,
+ build_page_params(options)
+ ]
+
+ data = send_request_for_data(profile, build_params(param_set))
+ ReportResponse.new(data, instance_klass).results
+ end
+
+ private
+ def send_request_for_data(profile, params)
+ request = DataRequest.new(profile.session, URL, params)
+ response = request.send_request
+ response.body
+ end
+
+ def build_params(param_set)
+ param_set.inject({}) {|p,i| p.merge(i)}.reject{|k,v| v.nil?}
+ end
+
+ def parse_filters(options)
+ filters = FilterParameters.new
+ filters.parameters << options[:filters] if options.has_key?(:filters)
+ filters
+ end
+
+ def parse_segment(options)
+ segment_id = "gaid::#{options[:segment_id].to_i}" if options.has_key?(:segment_id)
+ {'segment' => segment_id}
+ end
+
+ def parse_sort(options)
+ sort = ReportParameter.new(:sort)
+ sort << options[:sort] if options.has_key?(:sort)
+ sort
+ end
+
+ def build_default_params(profile)
+ {
+ 'ids' => Garb.to_ga(profile.id),
+ 'start-date' => format_time(Time.now - Model::MONTH),
+ 'end-date' => format_time(Time.now)
+ }
+ end
+
+ def build_page_params(options)
+ {'max-results' => options[:limit], 'start-index' => options[:offset]}
+ end
+
+ def format_time(t)
+ t.strftime('%Y-%m-%d')
+ end
+ end
+end
View
@@ -6,6 +6,8 @@ class Report
URL = "https://www.google.com/analytics/feeds/data"
def initialize(profile, opts={})
+ # ActiveSupport::Deprecation.warn("The use of report will be removed in favor of extend Garb::Scheme")
+
@profile = profile
@start_date = opts.fetch(:start_date, Time.now - MONTH)
View
@@ -28,6 +28,10 @@ class MiniTest::Unit::TestCase
def read_fixture(filename)
File.read(File.dirname(__FILE__) + "/fixtures/#{filename}")
end
+
+ def assert_data_params(expected)
+ assert_received(Garb::DataRequest, :new) {|e| e.with(Garb::Session, Garb::Model::URL, expected)}
+ end
end
MiniTest::Unit.autorun
@@ -0,0 +1,129 @@
+require 'test_helper'
+
+class ResultKlass
+ def initialize(attrs)
+ end
+end
+
+module Garb
+ class ModelTest < MiniTest::Unit::TestCase
+ context "A class extended with Garb::Model" do
+ setup do
+ @test_model = Class.new
+ @test_model.extend(Garb::Model)
+ end
+
+ # public API
+ should "be able to define metrics" do
+ report_parameter = stub(:<<)
+ ReportParameter.stubs(:new).returns(report_parameter)
+
+ @test_model.metrics :visits, :pageviews
+
+ assert_received(ReportParameter, :new) {|e| e.with(:metrics)}
+ assert_received(report_parameter, :<<) {|e| e.with([:visits, :pageviews])}
+ end
+
+ should "be able to define dimensions" do
+ report_parameter = stub(:<<)
+ ReportParameter.stubs(:new).returns(report_parameter)
+
+ @test_model.dimensions :page_path, :event_category
+
+ assert_received(ReportParameter, :new) {|e| e.with(:dimensions)}
+ assert_received(report_parameter, :<<) {|e| e.with([:page_path, :event_category])}
+ end
+
+ should "be able to se the instance klass" do
+ @test_model.set_instance_klass ResultKlass
+ assert_equal ResultKlass, @test_model.instance_klass
+ end
+
+ context "with a profile" do
+ setup do
+ entry = {
+ "title" => "Google Analytics Profile example.com",
+ "link" => [{"rel" => "self", "href" => Garb::Management::Feed::BASE_URL+"/accounts/1189765/webproperties/UA-1189765-1/profiles/98765"}],
+ "dxp:property" => [
+ {"name" => "ga:profileId", "value" => "98765"},
+ {"name" => "ga:accountId", "value" => "1189765"},
+ {"name" => "ga:webPropertyId", "value" => 'UA-1189765-1'}
+ ]
+ }
+
+ @profile = Garb::Management::Profile.new(entry, Session)
+ end
+
+ context "when getting results" do
+ setup do
+ @response = stub(:body => "raw report data")
+ DataRequest.stubs(:new).returns(stub(:send_request => @response))
+ ReportResponse.stubs(:new).returns(stub(:results => ['result']))
+
+ now = Time.now
+ Time.stubs(:new).returns(now)
+
+ @params = {'ids' => Garb.to_ga(@profile.id),
+ 'start-date' => (now - Model::MONTH).strftime('%Y-%m-%d'),
+ 'end-date' => now.strftime('%Y-%m-%d')}
+ end
+
+ should "get all results" do
+ assert_equal ['result'], @test_model.results(@profile)
+ assert_received(ReportResponse, :new) {|e| e.with("raw report data", OpenStruct)}
+ assert_data_params(@params)
+ end
+
+ should "be able to filter" do
+ filter_parameters = stub(:<<)
+ FilterParameters.stubs(:new).returns(stub(:parameters => filter_parameters, :to_params => {'filters' => "params"}))
+ assert_equal ['result'], @test_model.results(@profile, :filters => {:page_path => '/'})
+
+ assert_data_params(@params.merge({'filters' => 'params'}))
+ assert_received(filter_parameters, :<<) {|e| e.with({:page_path => '/'})}
+ end
+
+ should "be able to set the filter segment by id" do
+ assert_equal ['result'], @test_model.results(@profile, :segment_id => 1)
+ assert_data_params(@params.merge({'segment' => 'gaid::1'}))
+ end
+
+ should "be able to sort" do
+ sort_parameter = stub(:<<)
+ sort_parameter.stubs(:to_params => {'sort' => 'sort value'})
+ ReportParameter.stubs(:new).returns(sort_parameter)
+
+ assert_equal ['result'], @test_model.results(@profile, :sort => [:visits])
+ assert_received(sort_parameter, :<<) {|e| e.with([:visits])}
+ assert_data_params(@params.merge({'sort' => 'sort value'}))
+ end
+
+ should "be able to limit" do
+ assert_equal ['result'], @test_model.results(@profile, :limit => 20)
+ assert_data_params(@params.merge({'max-results' => 20}))
+ end
+
+ should "be able to offset" do
+ assert_equal ['result'], @test_model.results(@profile, :offset => 10)
+ assert_data_params(@params.merge({'start-index' => 10}))
+ end
+
+ # should "be able to shift the date range"
+
+ should "return a set of results in the defined class" do
+ @test_model.stubs(:instance_klass).returns(ResultKlass)
+
+ assert_equal ['result'], @test_model.results(@profile)
+ assert_received(ReportResponse, :new) {|e| e.with("raw report data", ResultKlass)}
+ end
+ end
+
+ # should "have a block syntax for filtering results"
+ # should "return results as an array of the class it belongs to, if that class is an ActiveRecord descendant"
+ # should "return results as an array of the class it belongs to, if that class is a DataMapper descendant"
+ # should "return results as an array of the class it belongs to, if that class is a MongoMapper descendant"
+ # should "return results as an array of the class it belongs to, if that class is a Mongoid descendant"
+ end
+ end
+ end
+end

0 comments on commit ed33017

Please sign in to comment.