GithubHelp home page GithubHelp logo

Comments (11)

twoixter avatar twoixter commented on August 22, 2024

Not in the current version, but I think this is an interesting feature. I will think on adding something like this in a future release. You always have the option of adding those methods yourself, for example:

...
def total_views_today
    views_on_web.today + views_on_app.today
end
...

This would give you more fine grained control over what methods-do-what, without messing excessively with tracked fields. For example, I mean, reader methods like "today" or "yesterday" does not return simple numbers (Fixnum), they return a customized class of ReaderExtender. This class acts like a Fixnum, but adds the "hourly" method so that you can also view a detailed hourly array. For example, try this over one of your models:

# Pretend @web is one of your models from the database
puts @web.views_on_web.today    # Ok, nothing new
puts @web.views_on_web.today.hourly   # This returns a 24 items array for a detailed hourly drill down
puts @web.views_on_web.today.class.name   # Surprise! This is not a Fixnum

If you really want to add a method who acts like a super-tracking field, this is more elaborate. I coded for you a simple class called "Assimilator" that behaves like a "proxy" object or something like that:

# Assimilator class, produces "proxy" objects that assimilates other tracking fields
# Resistance is futile... :-)
class Assimilator
    def initialize(owner, *fields)
        @owner, @fields = owner, fields
    end

    def method_missing(name, *args, &blk)
        # First "send" retrieves the tracking field, second forwards the current call
        result = @owner.send(@fields.first).send(name, *args, &blk)

        # Iterate over remaining trackers
        @fields[1..-1].inject(result) do |res, field|
            res + @owner.send(field).send(name, *args, &blk)
        end
    end
end

You can include the above code before defining the model, then, inside the model definition:

# Track exposure in search on site
track :views_on_web

# Track exposure in search on mobile
track :views_on_app

# Track combined exposure
def views_total
    Assimilator.new self, :views_on_web, :views_on_app
end

Standard Disclaimer This example is done in about 5 minutes! I don't know how it will perform in production. :-) (However, I tested it agains the Trackoid specs, so it at least... work).

from trackoid.

twoixter avatar twoixter commented on August 22, 2024

While writing my last comment, I was thinking about two solutions that might fit better to you. You can test what of those works for you balancing easy of implementation, data size, etc, but I show here for you to decide:

Solution 1 Simply adding another tracker for total_views.

You'll need to call "inc" twice, one for either :views_on_web / :views_on_app and another for :views_total. Your data will grow, but you gain in flexibility since the calculation of web + app views must be done in all the code that retrieves it (think another process outside Rails, like fast access with Node.js to the data or whatever).

Solution 2 Why don't you use aggregators?

This will be my preferred solution, since this adds you the flexibility of adding more "sources" later, not only "web" or "app", but might be "facebook" or "twitter" or wathever.

If you don't know how aggregation works, check this example. (Again, I'm typing while thinking, beware of the typos) :-)

# Inside the "web" model class
track :views

aggregate :sources do |src|
    # You must return a string to be the 'aggregation' key
    return "web" if is_from_web(src)
    return "app" if is_from_app(src)
    nil  # Returning nil here does NOT add as aggregation
         # but it counts against the 'views' as total.
end

Key important things:

  • You can use @web.views.inc like normal, but you don't get aggregated information. (beware!)
  • You must use @web.views(obj).inc and obj is an object you will receive in the aggregate method above.
  • If you use "aggregate", it adds aggregators to ALL track fields.

For example, you can use the "referer" string for the aggregation key, if you reduce it to "web" or "app" perhaps using some calculations. Or either you can pass the "request" object if you must look into headers or something.

I suggest yo to test the aggregation thing inside some test environment, so you can check how it works and to test if it fits your needs. For the above example, you can do things like:

# Add one page view today for each source
# In real world, "web" and "app" strings will be the referer string
# or something you can test as the "source" of the view
@web.views("web").inc
@web.views("app").inc
@web.views.inc   # Note that we don't use aggregation parameter here

# Results:
# @web.views.today == 3
# @web.views.sources.today == [["web", 1], ["app", 1]]
# @web.views.sources("web").today == 1
# @web.views.sources("app").today == 1
# @web.sources.count == 2  -- This is how many sources, not views!

I have not tested the example, I'm writing this on the fly but I think this is how aggregation works... :-) Please, tell me if this helps.

from trackoid.

mattiassvedhem avatar mattiassvedhem commented on August 22, 2024

Thanks for your extensive response! Seems to me that doing it with aggregators is the best way to go. I'll try that and get back to you if I find a better solution.

from trackoid.

mattiassvedhem avatar mattiassvedhem commented on August 22, 2024

What would these methods do? "is_from_web", "is_from_app"

from trackoid.

twoixter avatar twoixter commented on August 22, 2024

I included these methods as an example of what you can / should do inside an aggregation method. It's supposed to return some key as a string. When you declare an aggregation like:

aggregate :source do |obj|
    # Inspect 'obj' and return a string as the key of the aggregation
end

There, the 'obj' is simply whatever you pass as parameter of @model.views(obj).inc and it's up to you to decide what to pass in there.

As an example, imagine an aggregation like "browsers". If you aggregate browsers, you don't usually want to have a key for every combination of vendor, version, subversion, os, etc, etc. What you will do is "reduce" the user agent string to some common key like "safari", "mozilla", "chrome"... you name it.

aggregate :browsers do |user_agent|
    case
    when user_agent.match(/Mozilla/)
        "mozilla"
    when user_agent.match(/Chrome/)
        "chrome"
    .... every other major browser ...
    else
        "other"
    end
end

See what happens? You will end up easily growing the aggregate method. You can move all this hassle out of here doing an external method like get_browser_key(user_agent) and returning that instead of populating the model declaration with that big switch statement. However, it works for relatively short calculations like:

aggregate :weekday do |date|
    date.strftime("%A")
end

Remember the intended use of aggregations and Trackoid in general for that matter: avoid doing calculations while displaying data by letting the server do this for you in advance. You need to balance what to save and reduce in aggregations, for example if you don't care about browser versions, don't save it! :-)

from trackoid.

mattiassvedhem avatar mattiassvedhem commented on August 22, 2024

Cool, sorry for the delayed answer, Thanks for your detailed explanation =)

from trackoid.

mattiassvedhem avatar mattiassvedhem commented on August 22, 2024

Works great by the way!

from trackoid.

mattiassvedhem avatar mattiassvedhem commented on August 22, 2024

However. When I reload! my console I get this error message:

/Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/trackoid-0.3.7/lib/trackoid/aggregates.rb:136: warning: already initialized constant AdAggregates 

Where I aggregate and track Ad

Any idea why?

from trackoid.

twoixter avatar twoixter commented on August 22, 2024

This has to do with how Rails does the reloading and the config.cache_classes option.

If this is a warning, go ahead and forget it. It will work as expected.

If you do reload! many times a day, probably you would want to set config.cache_classes = true but I think this will defeat the effect of the reload! to begin with. :-)

from trackoid.

twoixter avatar twoixter commented on August 22, 2024

Anyway, it's a good issue to remove in a future Trackoid version. :-) Good catch!

from trackoid.

mattiassvedhem avatar mattiassvedhem commented on August 22, 2024

Ok, cool! :-) closing this.

from trackoid.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.