legato 0.0.1
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 +5 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/README.md +171 -0
- data/Rakefile +31 -0
- data/legato.gemspec +29 -0
- data/lib/legato/core_ext/array.rb +13 -0
- data/lib/legato/core_ext/string.rb +49 -0
- data/lib/legato/filter.rb +57 -0
- data/lib/legato/filter_set.rb +27 -0
- data/lib/legato/list_parameter.rb +29 -0
- data/lib/legato/management/account.rb +31 -0
- data/lib/legato/management/finder.rb +14 -0
- data/lib/legato/management/profile.rb +33 -0
- data/lib/legato/management/web_property.rb +28 -0
- data/lib/legato/model.rb +42 -0
- data/lib/legato/profile_methods.rb +16 -0
- data/lib/legato/query.rb +183 -0
- data/lib/legato/reports.rb +16 -0
- data/lib/legato/request.rb +21 -0
- data/lib/legato/response.rb +91 -0
- data/lib/legato/result_set.rb +21 -0
- data/lib/legato/user.rb +34 -0
- data/lib/legato/version.rb +3 -0
- data/lib/legato.rb +51 -0
- data/spec/cassettes/management/accounts.json +1 -0
- data/spec/cassettes/management/profiles.json +1 -0
- data/spec/cassettes/management/web_properties.json +1 -0
- data/spec/cassettes/model/basic.json +1 -0
- data/spec/fixtures/simple_response.json +1 -0
- data/spec/integration/management_spec.rb +34 -0
- data/spec/integration/model_spec.rb +20 -0
- data/spec/lib/legato/filter_spec.rb +49 -0
- data/spec/lib/legato/list_parameter_spec.rb +35 -0
- data/spec/lib/legato/management/account_spec.rb +39 -0
- data/spec/lib/legato/management/profile_spec.rb +45 -0
- data/spec/lib/legato/management/web_property_spec.rb +34 -0
- data/spec/lib/legato/model_spec.rb +77 -0
- data/spec/lib/legato/query_spec.rb +324 -0
- data/spec/lib/legato/response_spec.rb +15 -0
- data/spec/lib/legato/user_spec.rb +38 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/examples/management_finder.rb +18 -0
- data/spec/support/macros/oauth.rb +26 -0
- metadata +182 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Legato: Google Analytics Model/Mapper #
|
|
2
|
+
|
|
3
|
+
## [Check out the Wiki!](https://github.com/tpitale/legato/wiki) ##
|
|
4
|
+
|
|
5
|
+
## Google Analytics Management ##
|
|
6
|
+
|
|
7
|
+
1. Get an OAuth2 Access Token from Google, Read about [OAuth2](https://github.com/tpitale/legato/wiki/OAuth2-and-Google)
|
|
8
|
+
|
|
9
|
+
access_token = OAuth2 Access Token # from Google
|
|
10
|
+
|
|
11
|
+
2. Create a New User with the Access Token
|
|
12
|
+
|
|
13
|
+
user = Legato::User.new(access_token)
|
|
14
|
+
|
|
15
|
+
3. List the Accounts and Profiles of the first Account
|
|
16
|
+
|
|
17
|
+
user.accounts
|
|
18
|
+
user.accounts.first.profiles
|
|
19
|
+
|
|
20
|
+
4. List all the Profiles the User has Access to
|
|
21
|
+
|
|
22
|
+
user.profiles
|
|
23
|
+
|
|
24
|
+
5. Get a Profile
|
|
25
|
+
|
|
26
|
+
profile = user.profiles.first
|
|
27
|
+
|
|
28
|
+
6. The Profile Carries the User
|
|
29
|
+
|
|
30
|
+
profile.user == user #=> true
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## Google Analytics Model ##
|
|
34
|
+
|
|
35
|
+
class Exit
|
|
36
|
+
extend Legato::Model
|
|
37
|
+
|
|
38
|
+
metrics :exits, :pageviews
|
|
39
|
+
dimensions :page_path, :operating_system, :browser
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
profile.exits #=> returns a Legato::Query
|
|
43
|
+
profile.exits.each {} #=> any enumerable kicks off the request to GA
|
|
44
|
+
|
|
45
|
+
## Metrics & Dimensions ##
|
|
46
|
+
|
|
47
|
+
http://code.google.com/apis/analytics/docs/gdata/dimsmets/dimsmets.html
|
|
48
|
+
|
|
49
|
+
metrics :exits, :pageviews
|
|
50
|
+
dimensions :page_path, :operating_system, :browser
|
|
51
|
+
|
|
52
|
+
## Filtering ##
|
|
53
|
+
|
|
54
|
+
Create named filters to wrap query filters.
|
|
55
|
+
|
|
56
|
+
Here's what google has to say: http://code.google.com/apis/analytics/docs/gdata/v3/reference.html#filters
|
|
57
|
+
|
|
58
|
+
### Examples ###
|
|
59
|
+
|
|
60
|
+
Return entries with exits counts greater than or equal to 2000
|
|
61
|
+
|
|
62
|
+
filter :high_exits, lambda {gte(:exits, 2000)}
|
|
63
|
+
|
|
64
|
+
Return entries with pageview metric less than or equal to 200
|
|
65
|
+
|
|
66
|
+
filter :low_pageviews, lambda {lte(:pageviews, 200)}
|
|
67
|
+
|
|
68
|
+
Filters with dimensions
|
|
69
|
+
|
|
70
|
+
filter :for_browser, lambda {|browser| matches(:broswer, browser)}
|
|
71
|
+
|
|
72
|
+
Filters with OR
|
|
73
|
+
|
|
74
|
+
filter :browsers, lambda {|*browsers| browsers.map {|browser| matches(:broswer, browser)}}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## Using and Chaining Filters ##
|
|
78
|
+
|
|
79
|
+
Pass the profile as the first or last parameter into any filter.
|
|
80
|
+
|
|
81
|
+
Exit.for_browser("Safari", profile)
|
|
82
|
+
|
|
83
|
+
Chain two filters.
|
|
84
|
+
|
|
85
|
+
Exit.high_exits.low_pageviews(profile)
|
|
86
|
+
|
|
87
|
+
Profile gets a method for each class extended by Legato::Model
|
|
88
|
+
|
|
89
|
+
Exit.results(profile) == profile.exit
|
|
90
|
+
|
|
91
|
+
We can chain off of that method, too.
|
|
92
|
+
|
|
93
|
+
profile.exit.high_exits.low_pageviews.by_pageviews
|
|
94
|
+
|
|
95
|
+
Chaining order doesn't matter. Profile can be given to any filter.
|
|
96
|
+
|
|
97
|
+
Exit.high_exits(profile).low_pageviews == Exit.low_pageviews(profile).high_exits
|
|
98
|
+
|
|
99
|
+
Be sure to pass the appropriate number of arguments matching the lambda for your filter.
|
|
100
|
+
|
|
101
|
+
For a filter defined like this:
|
|
102
|
+
|
|
103
|
+
filter :browsers, lambda {|*browsers| browsers.map {|browser| matches(:broswer, browser)}}
|
|
104
|
+
|
|
105
|
+
We can use it like this, passing any number of arguments:
|
|
106
|
+
|
|
107
|
+
Exit.browsers("Firefox", "Safari", profile)
|
|
108
|
+
|
|
109
|
+
## Google Analytics Supported Filtering Methods ##
|
|
110
|
+
|
|
111
|
+
Google Analytics supports a significant number of filtering options.
|
|
112
|
+
|
|
113
|
+
Here is what we can do currently:
|
|
114
|
+
(the operator is a method available in filters for the appropriate metric or dimension)
|
|
115
|
+
|
|
116
|
+
Operators on metrics (method => GA equivalent):
|
|
117
|
+
|
|
118
|
+
eql => '==',
|
|
119
|
+
not_eql => '!=',
|
|
120
|
+
gt => '>',
|
|
121
|
+
gte => '>=',
|
|
122
|
+
lt => '<',
|
|
123
|
+
lte => '<='
|
|
124
|
+
|
|
125
|
+
Operators on dimensions:
|
|
126
|
+
|
|
127
|
+
matches => '==',
|
|
128
|
+
does_not_match => '!=',
|
|
129
|
+
contains => '=~',
|
|
130
|
+
does_not_contain => '!~',
|
|
131
|
+
substring => '=@',
|
|
132
|
+
not_substring => '!@'
|
|
133
|
+
|
|
134
|
+
## Accounts, WebProperties, Profiles, and Goals ##
|
|
135
|
+
|
|
136
|
+
> Legato::Management::Account.all(user)
|
|
137
|
+
> Legato::Management::WebProperty.all(user)
|
|
138
|
+
> Legato::Management::Profile.all(user)
|
|
139
|
+
|
|
140
|
+
## Other Parameters Can be Passed to a call to #results ##
|
|
141
|
+
|
|
142
|
+
* :start_date - The date of the period you would like this report to start
|
|
143
|
+
* :end_date - The date to end, inclusive
|
|
144
|
+
* :limit - The maximum number of results to be returned
|
|
145
|
+
* :offset - The starting index
|
|
146
|
+
* :order - metric/dimension to order by
|
|
147
|
+
|
|
148
|
+
## License ##
|
|
149
|
+
|
|
150
|
+
(The MIT License)
|
|
151
|
+
|
|
152
|
+
Copyright (c) 2012 Tony Pitale
|
|
153
|
+
|
|
154
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
155
|
+
a copy of this software and associated documentation files (the
|
|
156
|
+
'Software'), to deal in the Software without restriction, including
|
|
157
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
158
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
159
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
160
|
+
the following conditions:
|
|
161
|
+
|
|
162
|
+
The above copyright notice and this permission notice shall be
|
|
163
|
+
included in all copies or substantial portions of the Software.
|
|
164
|
+
|
|
165
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
166
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
167
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
168
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
169
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
170
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
171
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
|
|
3
|
+
require 'oauth2'
|
|
4
|
+
|
|
5
|
+
namespace :oauth do
|
|
6
|
+
def client
|
|
7
|
+
# This is my test client account for Legato.
|
|
8
|
+
OAuth2::Client.new('779170787975.apps.googleusercontent.com', 'mbCISoZiSwyVQIDEbLj4EeEc', {
|
|
9
|
+
:authorize_url => 'https://accounts.google.com/o/oauth2/auth',
|
|
10
|
+
:token_url => 'https://accounts.google.com/o/oauth2/token'
|
|
11
|
+
})
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def auth_url
|
|
15
|
+
client.auth_code.authorize_url({
|
|
16
|
+
:scope => 'https://www.googleapis.com/auth/analytics.readonly',
|
|
17
|
+
:redirect_uri => 'http://localhost'
|
|
18
|
+
})
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
desc "Get a new OAuth2 Token"
|
|
22
|
+
task :token do
|
|
23
|
+
`open "#{auth_url}"`
|
|
24
|
+
|
|
25
|
+
print 'OAuth2 Code: '
|
|
26
|
+
code = $stdin.gets
|
|
27
|
+
|
|
28
|
+
access_token = client.auth_code.get_token(code.strip, :redirect_uri => 'http://localhost')
|
|
29
|
+
puts access_token.token
|
|
30
|
+
end
|
|
31
|
+
end
|
data/legato.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "legato/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "legato"
|
|
7
|
+
s.version = Legato::VERSION
|
|
8
|
+
s.authors = ["Tony Pitale"]
|
|
9
|
+
s.email = ["tpitale@gmail.com"]
|
|
10
|
+
s.homepage = "http://github.com/tpitale/legato"
|
|
11
|
+
s.summary = %q{Access the Google Analytics API with Ruby}
|
|
12
|
+
s.description = %q{Access the Google Analytics Core Reporting and Management APIs with Ruby. Create models for metrics and dimensions. Filter your data to tell you what you need.}
|
|
13
|
+
|
|
14
|
+
s.rubyforge_project = "legato" # ?
|
|
15
|
+
|
|
16
|
+
s.files = `git ls-files`.split("\n")
|
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
19
|
+
s.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
# specify any dependencies here; for example:
|
|
22
|
+
s.add_development_dependency "rspec"
|
|
23
|
+
s.add_development_dependency "mocha"
|
|
24
|
+
s.add_development_dependency "bourne"
|
|
25
|
+
s.add_development_dependency "vcr", "2.0.0.beta2"
|
|
26
|
+
s.add_development_dependency "fakeweb"
|
|
27
|
+
|
|
28
|
+
s.add_runtime_dependency "oauth2"
|
|
29
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Pull in some AS String utilities (not loaded if AS is available)
|
|
2
|
+
unless Object.const_defined?("ActiveSupport")
|
|
3
|
+
class String
|
|
4
|
+
def camelize(first_letter = :upper)
|
|
5
|
+
case first_letter
|
|
6
|
+
when :upper then Legato::Inflector.camelize(self, true)
|
|
7
|
+
when :lower then Legato::Inflector.camelize(self, false)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
alias_method :camelcase, :camelize
|
|
11
|
+
|
|
12
|
+
def underscore
|
|
13
|
+
Legato::Inflector.underscore(self)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def demodulize
|
|
17
|
+
Legato::Inflector.demodulize(self)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
module Legato
|
|
23
|
+
module Inflector
|
|
24
|
+
extend self
|
|
25
|
+
|
|
26
|
+
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
|
27
|
+
if first_letter_in_uppercase
|
|
28
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
|
29
|
+
else
|
|
30
|
+
lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def underscore(camel_cased_word)
|
|
35
|
+
word = camel_cased_word.to_s.dup
|
|
36
|
+
word.gsub!(/::/, '/')
|
|
37
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
|
38
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
|
39
|
+
word.tr!("-", "_")
|
|
40
|
+
word.downcase!
|
|
41
|
+
word
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def demodulize(class_name_in_module)
|
|
45
|
+
class_name_in_module.to_s.gsub(/^.*::/, '')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Legato
|
|
2
|
+
class Filter
|
|
3
|
+
attr_accessor :field, :operator, :value, :join_character
|
|
4
|
+
|
|
5
|
+
OPERATORS = {
|
|
6
|
+
:eql => '==',
|
|
7
|
+
:not_eql => '!=',
|
|
8
|
+
:gt => '>',
|
|
9
|
+
:gte => '>=',
|
|
10
|
+
:lt => '<',
|
|
11
|
+
:lte => '<=',
|
|
12
|
+
:matches => '==',
|
|
13
|
+
:does_not_match => '!=',
|
|
14
|
+
:contains => '=~',
|
|
15
|
+
:does_not_contain => '!~',
|
|
16
|
+
:substring => '=@',
|
|
17
|
+
:not_substring => '!@',
|
|
18
|
+
:desc => '-',
|
|
19
|
+
:descending => '-'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def initialize(field, operator, value, join_character=';')
|
|
23
|
+
self.field = field
|
|
24
|
+
self.operator = operator
|
|
25
|
+
self.value = value
|
|
26
|
+
self.join_character = join_character
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def google_field
|
|
30
|
+
Legato.to_ga_string(field)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def google_operator
|
|
34
|
+
OPERATORS[operator]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def escaped_value
|
|
38
|
+
CGI.escape(value.to_s.gsub(/([,;\\])/) {|c| '\\'+c})
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_param
|
|
42
|
+
"#{google_field}#{google_operator}#{escaped_value}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def join_with(param)
|
|
46
|
+
param << join_character unless param.nil?
|
|
47
|
+
param.nil? ? to_param : (param << to_param)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def ==(other)
|
|
51
|
+
field == other.field &&
|
|
52
|
+
operator == other.operator &&
|
|
53
|
+
value == other.value &&
|
|
54
|
+
join_character == other.join_character
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Legato
|
|
2
|
+
class FilterSet
|
|
3
|
+
include Enumerable
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@filters = []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def each(&block)
|
|
10
|
+
@filters.each(&block)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_a
|
|
14
|
+
@filters
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def <<(filter)
|
|
18
|
+
@filters << filter
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_params
|
|
22
|
+
@filters.inject(nil) do |params, filter|
|
|
23
|
+
filter.join_with(params)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Legato
|
|
2
|
+
class ListParameter
|
|
3
|
+
|
|
4
|
+
attr_reader :name, :elements
|
|
5
|
+
|
|
6
|
+
def initialize(name, elements=[])
|
|
7
|
+
@name = name
|
|
8
|
+
@elements = Array.wrap(elements)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def name
|
|
12
|
+
@name.to_s
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def <<(element)
|
|
16
|
+
(@elements += Array.wrap(element)).compact!
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_params
|
|
21
|
+
value = elements.map{|element| Legato.to_ga_string(element)}.join(',')
|
|
22
|
+
value.empty? ? {} : {name => value}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def ==(other)
|
|
26
|
+
name == other.name && elements == other.elements
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Legato
|
|
2
|
+
module Management
|
|
3
|
+
class Account
|
|
4
|
+
extend Finder
|
|
5
|
+
|
|
6
|
+
def self.default_path
|
|
7
|
+
"/accounts"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def path
|
|
11
|
+
"/accounts/#{id}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_accessor :id, :name, :user
|
|
15
|
+
|
|
16
|
+
def initialize(attributes, user)
|
|
17
|
+
self.user = user
|
|
18
|
+
self.id = attributes['id']
|
|
19
|
+
self.name = attributes['name']
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def web_properties
|
|
23
|
+
WebProperty.for_account(self)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def profiles
|
|
27
|
+
Profile.for_account(self)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Legato
|
|
2
|
+
module Management
|
|
3
|
+
module Finder
|
|
4
|
+
def base_uri
|
|
5
|
+
"https://www.googleapis.com/analytics/v3/management"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def all(user, path=default_path)
|
|
9
|
+
json = user.access_token.get(base_uri + path).body
|
|
10
|
+
JSON.parse(json)['items'].map {|item| new(item, user)}
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Legato
|
|
2
|
+
module Management
|
|
3
|
+
class Profile
|
|
4
|
+
|
|
5
|
+
extend Finder
|
|
6
|
+
include ProfileMethods
|
|
7
|
+
|
|
8
|
+
def self.default_path
|
|
9
|
+
"/accounts/~all/webproperties/~all/profiles"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def path
|
|
13
|
+
self.class.default_path + "/" + id.to_s
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_accessor :id, :name, :user
|
|
17
|
+
|
|
18
|
+
def initialize(attributes, user)
|
|
19
|
+
self.user = user
|
|
20
|
+
self.id = attributes['id']
|
|
21
|
+
self.name = attributes['name']
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.for_account(account)
|
|
25
|
+
all(account.user, account.path+'/webproperties/~all/profiles')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.for_web_property(web_property)
|
|
29
|
+
all(web_property.user, web_property.path+'/profiles')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Legato
|
|
2
|
+
module Management
|
|
3
|
+
class WebProperty
|
|
4
|
+
extend Finder
|
|
5
|
+
|
|
6
|
+
def self.default_path
|
|
7
|
+
"/accounts/~all/webproperties"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def path
|
|
11
|
+
self.class.default_path + "/" + id.to_s
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_accessor :id, :name, :website_url, :user
|
|
15
|
+
|
|
16
|
+
def initialize(attributes, user)
|
|
17
|
+
self.user = user
|
|
18
|
+
self.id = attributes['id']
|
|
19
|
+
self.name = attributes['name']
|
|
20
|
+
self.website_url = attributes['websiteUrl']
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.for_account(account)
|
|
24
|
+
all(account.user, account.path+'/webproperties')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/legato/model.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Legato
|
|
2
|
+
module Model
|
|
3
|
+
def self.extended(base)
|
|
4
|
+
ProfileMethods.add_profile_method(base)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def metrics(*fields)
|
|
8
|
+
@metrics ||= ListParameter.new(:metrics)
|
|
9
|
+
@metrics << fields
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def dimensions(*fields)
|
|
13
|
+
@dimensions ||= ListParameter.new(:dimensions)
|
|
14
|
+
@dimensions << fields
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def filters
|
|
18
|
+
@filters ||= {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def filter(name, block)
|
|
22
|
+
filters[name] = block
|
|
23
|
+
|
|
24
|
+
(class << self; self; end).instance_eval do
|
|
25
|
+
define_method(name) {|*args| Query.new(self).apply_filter(*args, block)}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# def set_instance_klass(klass)
|
|
30
|
+
# @instance_klass = klass
|
|
31
|
+
# end
|
|
32
|
+
|
|
33
|
+
# def instance_klass
|
|
34
|
+
# @instance_klass || OpenStruct
|
|
35
|
+
# end
|
|
36
|
+
|
|
37
|
+
def results(profile, options = {})
|
|
38
|
+
Query.new(self).results(profile, options)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Legato
|
|
2
|
+
module ProfileMethods
|
|
3
|
+
def self.add_profile_method(klass)
|
|
4
|
+
# demodulize leaves potential to redefine
|
|
5
|
+
# these methods given different namespaces
|
|
6
|
+
method_name = klass.name.to_s.demodulize.underscore
|
|
7
|
+
return unless method_name.length > 0
|
|
8
|
+
|
|
9
|
+
class_eval <<-CODE
|
|
10
|
+
def #{method_name}(opts={})
|
|
11
|
+
#{klass}.results(self, opts)
|
|
12
|
+
end
|
|
13
|
+
CODE
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|