Matt Parker, a Pivotal Labs developer, explores some of the downsides of test-driven development. Unmanageably large test suites, suites that don’t pass. “TDD is a means to an end.” “Go Fast Forever.” Clean code. Being slowed down by bad code.
Problem #1: The BDD proponents say “move from the outside in.” Parker argues that this is misunderstood to mean that the outside is the GUI. (He says it is not.)
Problem #2: Misapplied mocking: Everything gets mocked out. One the one hand, the tests are very fast. On the other hand, the tests are meaningless because they test implementation and not behavior.
In particular, he says people should start with SOLID principles and understand the five types of test doubles before getting too entralled with mocking, hench it will lead to over-mocking.
Problem #3: Misapplied unit testing. “Every class should be paired with a well-designed unit test.” or “Every public method should be paired with a well-designed unit test.” Problem: This creates test coupling. The tests become aware of the design pattern you are using underneath. By coupling your tests to those design patterns, you’ve made it harder to refactor away from those design patterns when the time comes. Instead, every behavior should be paired with a well-designed unit test.
My take:Parker presents some good points about the pitfalls, particularly with behavioral patterns that early developers can fall into. Most of these can be summed up as extremity positions, which is typically where developers go wrong. It’s a short and to-the-point talk worth watching but exists in the realm of ‘identify-without-solving’ in that it identifies certain bad behaviors without offering the alternatives.
The talk in which Uncle Bob Martin pedantically pontificates on the merrits of test-first development, specifically advocating the common argument that testing first leads you to write better code.
He also pontificates on other topics too (mocking, coupling, static type checking, testing accross module boundaries), so although this is long it is worth a watch.
In the middle of the video he does the Prime factors problem, quite impressively and somewhat quickly, in fact, in Kotlin. This is something great for Kotlin enthusiasts— as well as any programmer who wants to check out this famous CS problem. (He implements it in a matter of 5 minutes and then goes on to claim he “did not have the algorithm pre-planned” comes off as disingenuous.)
ITT 2016 – Seven Ineffective Coding Habits of Many Programmers — Kevlin Henney
GPTP 2017 The Dehumanisation of Agile and Objects — James Coplien
Group of paper airplanes, orange one is the first place, can be used leadership/individuality concepts. ( 3d render )
Confusingly, bundler can have more than one version of Rails installed at once. if you had many versions, when you ran rails new, it probably used the default one, which could have been a very old one for you. This often confuses new developers, and especially if you installed Rails years ago and then come back to pick it up again.
To see which versions of Rails you have installed in bundler, use
gem list |grep rails
(here you are grepping, or searching against, the output for the string “rails”; without grep you would see all of your gems)
You’ll see some other gems with the name “rails” in them too, fortunately, all the rails gems are numbered concurrently.
TO install a different version of Rails in your bundler (remember, this just installs the gem code in your bundler’s system ready for use)
gem install rails -v 5.2.4.3
Finally, if you want to force rails new to use a specific version, use underscores before the “new” (that is, between “rails” and “new”)
Common Core JS am pleased to announce a rapid application development tool for Rails: It’s called “Common Core JS.”
The source code is on Github. When you install it, the gem is pulled from RubyGems.
The finished result of the example app we will build today can be seen here. (I recommend you type out each step and only refer to this if you need to.)
It’s based on the idea that you will embrace a common naming convention for your Rails app, will use AJAX rendered-everything (that means all the forms are remote: true), and want a common set of tools for rolling out a quick, dashboard-like app in Rails.
It comes with poor-man’s authentication built-in. Poor man’s auth is safe and works fine, but it isn’t designed for you to grow your entire app on and you should graduate out of if you have any granularity to your access control.
Common Core is a fantastic tool for rapidly building prototypes. It can also be used to create API back-ends for Javascript-heavy apps.
It is a blunt instrument and you should wield it carefully. In particular, you’ll want to remove specific code that it creates because it can create situations where users may have the access they shouldn’t.
Quick Setup
Let’s build the fastest app you can possibly build in Rails.
I’ll call it MyGreatApp, but yours can be named anything of coruse.
rails new MyGreatApp
Once you have the app generated (and even on faster processors Rails new still takes a hot minute or two), go to modify the Gemfile
Add the Gem
gem 'common_core_js'
To your Gemfile.
Then run: bundle install
Setup The Common Core
Then run the Common core install generator (which is implemented as a generator, not a rake task)
bundle exec rails generate common_core:install
Now you’ll create a User objeect, with a migration and model.
do this using. Note that you should not add any fields that will conflict with Devise fields, like email (I’ll add those in the next step).
bundle exec rails generate model User name:string joined_date:date timezone:integer
Add Devise
Add devise to your Gemfile
gem 'devise'
run bundle install, then install devise on the User model:
rails generate devise:install
rails generate devise User
Take a look at the fields on your User database table now. (Here I’m using DB Browser for SQLite)
Notice the fields added name, joined_date, and timezone I’ve put a red box around and the devise fields, including email and encrypted_password, I’ve put a blue box around.
Now you have the most bare-bones Rails app with Devise gem installed. This is great because you get free sign up, login and forgot password fundionality immediately.
Go to
http://127.0.0.1:3000/users/sign_up
Add jQuery
Add jQuery to your package.json via yarn
yarn add jquery
Go to config/webpack/environment.js and add this code in between the existing two lines
Now go ahead and start your rails server with bundle exec rails server
Go to /users/sign_up and create a new user account (be sure to enter the same password twice)
You will then be redirected to the Home page, which is now the Ruby on Rails “Yay you’re on Rails.” That’s fine— go ahead and customize your root URL.
Today’s Example App
I’ll make a super-simple system today where Users have many Events An Event belongs to a Format, which in our fictional world is only two choices: Zoom or Outdoor.
Events have a name, a starting datetime, and an ending datetime, and a “publicize” date on which should be before the starting time.
The two datetime fields (starting_at and ending_at) and the date field (publicize_on) can be empty (nil), but if set they are enforced to be: starting_at must be before ending_at. publicize_on must be before starting_at.
An Event will belong_to a Format (class & table). Formats will have a name field and only two records: Zoom and Outdoor. (We can assume they will be id 1 and id 2 in the formats table.)
All Events must belong_to a format, and when we create or edit an Event we can switch its format, but the format cannot be blank (null).
We already have the User object, and we also already have all the login, logout forgot password, and more provided by Devise.
We want the users to log-in and go to a dashboard of their own events.
They should be able to create, edit, & delete their own events with only the validations discussed above.
They should not be able to edit or create events belonging to other users, even by hacking the query parameters.
Finally, the user should be able to edit their own name and timezone, but not any other user’s name or timezone.
Because we want to name our routes from the perspective of the context-interaction, we’ll namespace the controller to Dashboard:: in our Ruby code and /dashboard in the URL. The controller will be at controllers/dashboard and the views will be at views/dashboard
Make the models
Since you already made the User model in the devise setup, let’s go ahead and create the Events and Formats tables.
Then open up the migration file and ed the description line, adding a larger than 256 limit, like 400
Next run bundle exec rake db:migrate to create the table.
Before I go further, let’s edit our models just a bit.
Open models/user.rb and add has_many :events
also add validates_presence_of :name
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :events
validates_presence_of :name
end
Likewise, on the Event object, defined models/event.rb, you’ll need to add the reflexive relationship for belongs_to :user and belongs_to :format
class Event < ApplicationRecord
belongs_to :user
belongs_to :format
end
Now make a formats table
bundle exec rails generate model Format name:string
class CreateFormats < ActiveRecord::Migration[6.0]
def change
create_table :formats do |t|
t.string :name
t.timestamps
end
Format.create(name: "Outdoor")
Format.create(name: "Zoom")
end
end
Then run the migration itself, which will now make the table & the two format records.
In order to show dates, we use a localized date display: the date & time is always shown to the user in their own date. To do this you have a few choices: (1) You can save the timezone to the user’s table, and let them set it for themselves, (2) You can show everybody the server’s date
Option #1 – Store timezone On the User object
We already took care of this by adding timezone to our User object.
Option #2 – Use the Server’s Timezone
If the auth object (current_user) does not respond to timezone, the Rails “system clock” will be used. The system clock’s timezone as set by the Rails app is used. This is often the timezone of the headquarter’s of the company that owns the application. (That is, if you do not know the user’s context, you simply use your own company’s context instead.)
Make the Controller, Views & Specs
Next we’re going to do the thing. We’ll make two controllers: Dashboard::EventsController for editing events and Dashboard::UsersController for the user editing their own name.
When passing the model name, pass it in the singular form
Here I’ve provided the namespace= with a value of dashboard. You will this is important to how our code comes out.
Here is the heart & soul of the common core: 5 .js.erb files, 5 .haml files, a controller, and a controller spec. Also along for the ride came controllers/dashboard/base_controller.rb as well as views/dashboard/_errors.haml, and layouts/_flash_notices.haml
Take a peak through all the generated code now.
Pay particular attention to _line.haml and _form.haml. You will note _form.haml conveniently is used for both the new/create actions and also for the update action, unifying the layout of your record across CRUD.
You can use both to customize your app quickly and easily.
One more quick step, add this to your routes.rbfile.
Make sure to nest the :events route within the :dashboard namespace, as shown here. If you aren’t familiar with namespacing in Rails, check out this blog post on my other blog, The Rails Coach.
Rails.application.routes.draw do
devise_for :users
namespace :dashboard do
resources :events
end
end
Start your server with
bundle exec rails server
If the Common Core finds null for timezone on your User object, it will default to either (1) whatever is set for your Rails app in either application.rb or an environment file, or, if don’t have this set (2) the clock timezone of the server that is running your Ruby application.
It’s generally a good idea to set your Rails app timezone to the same timezone of your company’s headquarters, and then don’t change it, because that way if your server happens to move from one timezone to another (for example, you migrate from a server on the East coast to the West coast), your app will be unaffected. If your company changes timezones, you can either leave the Rails app as-is or change it, but be sure to note any place where your default timezone comes through.
config.time_zone = 'Eastern Time (US & Canada)'
Done!
We can now do all of these fancy thigns.
Create an event. Leave name or format blank, get an error.
When you create a new event, you must give it a name and Format. Notice how if you don’t, the Rails-side logic will return the form shown with the erroneous fields marked in red.
Edit an Event
Your model-level validations — name and format as required — are enforced in the update action as well.
Deleting An Event
Adding Validation
Add this to your Event class in app/models/event.rb
validate :start_at_before_end_at, if: -> {!start_at.nil? && !end_at.nil?}
def start_at_before_end_at
if end_at < start_at
errors.add(:start_at, "can't be after end at")
errors.add(:end_at, "can't be before start at")
end
end
*Validation magic*
Finally, to add validation on all the date fields, here’s our completed Event model
class Event < ApplicationRecord
belongs_to :user
belongs_to :format
validates_presence_of :name
validate :start_at_before_end_at, if: -> {!start_at.nil? && !end_at.nil?}
validate :promote_on_before_start_at, if: -> {!promote_on.nil? && !start_at.nil?}
def start_at_before_end_at
if end_at < start_at
errors.add(:start_at, "can't be after end at")
errors.add(:end_at, "can't be before start at")
end
end
def promote_on_before_start_at
if start_at < promote_on
errors.add(:promote_on, "can't be after start at")
end
end
end
Account Dashboard
Next we’re going to create the very simplest of Account Dashboards. Remember that Devise already handles log in, log out, and forgot password, meaning most of the heavy lifting of user authentication has been taken care of.
In this simple app, we want an Account dashboard that let’s us edit only two fields: name and timezone.
First let’s add the route to routes.rb
Rails.application.routes.draw do
devise_for :users
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
namespace :dashboard do
resources :events
resources :users
end
end
Next let’s generate some scaffold
rails generate common_core:scaffold User namespace=dashboard
We now instantaly have a very basic dashboard for the User to edit their own details
The final finishing touch here will be to make the Timezone into a drop-down.
Remember, make your models first: Add limits and defaults to your database fields by modifying your migrations. Then add the relasionships between the tables using standard ActiveRercord has_many, belongs_to, and has_one.
Then build the common core scaffolding & customize the views and controllers it produces.
With these powerful tools, you can build a dashboard-like app in minutes, complete with simple interface buttons that let your users accomplish most of what they’ll need. The philosophy is that you will want this dashboard as you initially introduce people to your product. The main logic of your application will likely live more in the models, service objects, and domain layer (business logic) parts of your Rails app. For this reason, you are encouraged to customize the files only lightly. (Add some verbiage or change the CSS to customize the look & feel.)
The code you build with common core is cheap and disposable. It is not very modern, but it gets the job done. It is just “good enough” to launch a sophisticated app on, but it isn’t good enough to impress your users with a really good UI.
For that, you’ll want to throw away the front-end code and replace it with a modern JS UI like React, Vue, Ember, or Angular.
By that time, the Common core will have already helped you build (1) a prototype, (2) your business logic, and (3) possibly even some controllers you can re-use (for example, remove the format.js responses and replace them with format.json to change the controllers into a JSON-responding API controller.)
What does that mean? Upon inspection you realize that someone has typed into your website the all-too-favorite Millenial communication tool: the emoji. ?✨
MySQL needs a little massaging. For steps 1 & 2 in Rails, create a new database migration and use execute to run these commands directly onto your database.
Make sure you actually alter the database’s character set
ALTER DATABASE <DATABASE NAME> CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
Where <DATABASE NAME>is your database. in Rails, because we have different database names for different environments, try this inside of your migration…
execute "ALTER DATABASE #{ActiveRecord::Base.connection.current_database} CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;"
Make sure you ALTER the TABLE AND FIELD where you want to insert the character set
ALTER TABLE CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE modify TEXT charset utf8mb4;
(If you made a Rails migration, you can do this in the same migration using another execute.)
IMPORTANT: I have assumed above that your field is of type TEXT. If it is not, change your field’s type definition before running this. If you don’t, you’ll convert your existing field to a TEXT field even if you didn’t want to.
Restart your MySQL instance (DO NOT FORGET)
In Rails, you must also specify the encoding & collation in the MySQL adapter which you will find in your config/database.yml file (where you have specified mysql2 as the adapter).
prop-types is used for validating the properties are what they are supposed to be. This is invaluable for speed in development. In a nutshell, you should get used to the idea of declaring what the valid types for your properties are (string, array, number, etc)
You will need react and thunk if you want to create a Redux tree of data. You will have to learn how to do fun things like mapStateToProps and mapDispatchToProps, and then you will have a giant data tree that will be mapped to your react components.
Query string will check those query parameters (like ?search or ?utm_campaign, etc) to let you grab them from the browser’s URL.
• react-helmet You use React helmet if you want to set the page’s HEAD tags, for example, if you want unique meta or content tags to be generated depending on what is on the page.
With these you do fun things like unit testing, watching all your specs as you develop, and run a coverage report of how many lines of code you have covered.
This is what you use if you want to integration testing. You’ll note that Cypress is written in jQuery, ironically, because you aren’t supposed to use jQuery with React.
React’s Virtual DOM arguably revolutionized web development. In short: React will keep a copy of the DOM in its own memory space. When you make updates, for example, several updates to component state, the React will manipulate the Virtual DOM first, performing all of the mutations on it in succession. Once all of your logic has run, React knows how to compare the Virtual DOM to the real DOM, and then it makes small, isolated updates one-by-one, only changing the least amount necessary.
Why does it do this? Well, for one thing, when the browser performs DOM operations it is famously slow at doing so, that is non-performant when many updates are happening. If each mutation of the DOM was done in the browser, you couldn’t achieve the fast effects you can with React.
You’ll note then that the objects you code in React, then, don’t expose direct access to the DOM elements. This is a good thing, because it means that to change the DOM you must always go through the React re-render cycle when you want to change your DOM.
Most of the time, this can get you most of what you need doing. But occasionally, if you need to access DOM elements to inspect them, you’ll want to use a feature of React called React Refs.
Other cases of using Refs is to manage text highlighting, managing focus, and media playback. Refs are also used for some advanced purposes too, like managing interactions with 3rd parties and multistep form state.
Today I will explore three patterns for using Refs in React: 1) A basic Ref implementation, 2) A forwarded Ref, that is a ref that has been sent to another component, and 3) A forwarded Ref used within a Styled Components setup.
Remember, your Ref will simply point to the DOM elements. What’s important here to keep in mind is that you 1) first need to create your ref, 2) then assign it to a component using ref= , and 3) let React maintain the Ref for you during the rendering.
Example #1 — Basic Usage
First, you need to create a Ref object. You do this before your component is rendered using React.createRef()
First let’s start with the most basic React component. (To see this, edit your App.js file and remove the default content and add <ThingOne />)
Notice that in the constructor, we create the Ref using this.ref1 = React.createRef(). At this point, if you examine your object, you will see something interestingly empty:
Notice that your variable now holds an object with a single key, current, that points to nothing (null).
Now, let’s add an input checkbox and also tell React to use this ref (ref1) for that input checkbox.
import React from 'react'
class ThingOne extends React.Component{
constructor(props) {
super(props)
this.ref1 = React.createRef()
}
render() {
return (
<div>
this is an experiment into forwarded refs
<br />
Please check this box: <input type={"checkbox"} ref={this.ref1} />
</div>
)
}
}
export default ThingOne
We use this Ref (ref1) in the render method, when we call
<input type={"checkbox"} ref={this.ref1} />
Here, we’re telling React when it renders the dom, a reference will be stored back onto the value in the current slot in our Ref.
If you examine the Ref after React has rendered, you get something different:
Now, the Ref’s current value is a reference to the DOM element. That means that it can be treated like a “native DOM element” because it is one.
Finally, in our little baby example, we’ll attach an onClick handler to the boxChecked handler.
import React from 'react'
class ThingOne extends React.Component{
constructor(props) {
super(props)
this.ref1 = React.createRef()
// if you don't bind your method then boxChecked will be called
// without `this` in scope
this.boxChecked = this.boxChecked.bind(this)
}
boxChecked(event) {
const dom_elem = this.ref1.current
const is_checked = dom_elem.checked
alert("This box is now " + (is_checked ? 'CHECKED' : 'UNCHECKED'))
}
render() {
return (
<div>
this is an experiment into forwarded refs
<br />
Please check this box:
<input type={"checkbox"} ref={this.ref1} onClick={this.boxChecked} />
</div>
)
}
}
export default ThingOne
Be sure to bind it to the this object (because, err.. javascript) in the constructor, and Bob’s your uncle… once clicked, this.ref1.current now returns {current: input} as you see above. As you can see from the code in boxChecked, you can pull the DOM element out from this.ref1.current and then examine it using native HTML properties (.clicked is implemented in the browser and is a native DOM element property.)
Now it’s time to go down the rabbit hole a little deeper. And with ref forwarding, we really are traveling down a rabbit hole. Let’s stay we want to nest ThingTwo inside of ThingOne, but be able to look at the ref for ThingTwo from code inside of ThingOne.
Let’s say we try something like this
// src/thing_one.js
import React from 'react' import ThingTwo from './thing_two'
class ThingOne extends React.Component{ constructor(props) { super(props) this.ref1 = React.createRef()
// if you don't bind your method then boxChecked will be called // without `this` in scope this.boxChecked = this.boxChecked.bind(this) } boxChecked(event) { const dom_elem = this.ref1.current alert("ThingTwo is " + dom_elem.getBoundingClientRect()) }
render() { return ( <div style={{position: 'relative', border: 'solid 1px green'}}> this is an experiment into forwarded refs <br /> Please <button onClick={this.boxChecked} type="button" > click here </button>
<ThingTwo ref={this.ref1}/> </div> ) } }
export default ThingOne
// src/thing_two.js
import React from 'react'
class ThingTwo extends React.Component{
render() { return ( <div style={{position: 'relative', top: 0, left: 0, border: 'solid 1px red'}}> This is thing Two </div> ) } }
export default ThingTwo
Warning: The above code doesn’t actually work!
It makes sense that we might think we can pass a ref as a prop from one component to another. However, because of how React passes props around, this doesn’t actually work. Moreso, it actually produces no error message or indication to the developer that this is wrong. But here’s how you know it’s wrong: If you examine the ref once the UI is rendered, you get this wrong result:
If you see the name of the Component, something is wrong. it won’t work and your ref will not be usable. What’s wrong here is that you need to forward your ref from one component to another.
Interestingly, the code in thing_one.js doesn’t change — you’re still going to pass the ref down to ThingTwo via the props.
However, for ThingTwo, you need to wrap ThingTwo’s entire implementation in React.forwardRef. This utility method will take a function and will call that function with two arguments: the props passed and the ref passed, but it will allow you to pipe the ref into ThingTwo like so:
import React from 'react'
class ThingTwo extends React.Component{
render() {
const {selfRef} = this.props
return (
<div ref={selfRef} style={{position: 'relative', top: 0, left: 0, border: 'solid 1px red'}}>
This is thing Two
</div>
)
}
}
export default React.forwardRef((props, ref) => <ThingTwo {...props} selfRef={ref} />)
Many examples online use a pattern like ref={ref} but I personally find this confusing for demonstration. What is happening is that one variable name exists in the ForwadedRef constructor, and a distinct variable also named ref then also exists inside your component, they just happen to both be called ‘ref.’ To eliminate the confusion, I named the ref that ThingTwo receives selfRef, indicating that this variable is only ever used as the reference to DOM element that maps to the component (to ‘itself’). You can pick any variable names you want, but this example demonstrates that the variable occupies a distinct namespace in ThingTwo.
Now, if you examine your ref after rendering, it looks like so:
Note that in some cases your DOM element object looks slightly different. For example, if React renders multiple of the same component, it will keep letter-based class names on your objects (it does this as its internal mapping to the DOM).
Here’s an example from a different app (you aren’t seeing the code for this). In this case, my component has a built-in class for logo-container and the strange-looking characters that make up the other classes are correct.
The example code for example #2 can be found here.
Example 3 — With Ref Forwarding in a Styled Component
Forwarding refs is slightly tricky. Once you get the hang of it, it makes sense. Always remember that the receiving component must be wrapped in the forwarder. The final pattern to show you here is how a ForwardedRef can be used with Styled Components.
This example is just like #2, except that ThingTwo uses a StyledThingTwo to display itself
You will note that because StyledThingTwo is a functional styled component, it doesn’t need its own forwarded ref wrapper. Only the ThingTwo needs to be wrapped in the forwardRef call.
It’s a plug-and-play Rails engine that you install into your Ruby on Rails application with just three simple steps (see the README). You can then immediately pick up your visitors’:
IP address
Ad campaign where they came from
the browser they are using
In my next version, I’ll add support for http referrer and more too. Give it a try today.
It’s an great little utility project that will surely have appeal to many websites and developers.
Visitors come to your site every day. Along with their visits, four key pieces of information come along for the ride:
— IP address
— browser name (which lets you infer operating system and sometimes device)
— UTM parameters (if they clicked from another site or an ad campaign)
— HTTP referrer, (which shows if they clicked directly from another site to your site, even when no UTMs are set)
Universal Track Manager, a play-on-words that shares an acronym with “UTM Parameters,” is a plug & play gem to automatically scoop up this information and stash it into your database. You can think of it like a built-in Google Analytics (without the fancy dashboard).
With a tiny bit of trickery, support for Viewport size too is possible (width X height of the users’s window), which can let you determine if the user is on a desktop or mobile browser.
Today I’ve bumped the version up to 0.4. (I realize I made a 10x version change but this Gem is nearing its ‘version 1.0’ release so I am anticipating that when it is feature complete.)
This is the second version I’m dubbing as ‘public beta.’ Although this is production-quality code, it should be used with caution until it is no longer in BETA status. Please give it a try on your Rails projects today. With an easy 3-step installation into any Rails 4+ app and you can sit back and sweep up tracking info on your visitors.
*MOST* of the core functionality now works! This version 0.4 implements fully support for timestamping your visits, the user’s IP address, browser, and UTMs.
Support for HTTP referrer and more coming soon! Kindly submit feedback via Github.
Today I’m announcing ‘a first look’ at my new Gem: Universal Track Manager. It’s an ambitious project that’s going to have nearly universal appeal and utility.
Visitors come to your site every day. Along with their visits, 4 key pieces of information come along for the ride:
— IP address
— browser name (which lets you infer operating system)
— UTMs showing if they clicked from another site, or if they came from online advertising (typically you can “auto-tag” your ad campaigns and your UTMs will be magically populated)
— Http referrer, which shows if they clicked directly from another site to your site (Even when no UTMs are set)
Universal Track Manager, a play-on-words that shares an acronym with “UTM Parameters,” is your one-stop shop to automatically pick up this information and stash it into your database. You can think of it like a built-in Google Analytics (without the fancy dashboard).
As if that weren’t ambitious enough, with a tiny bit of trickery I’m planning support for optional Viewport size (width X height), which can let you determine if the user is on a desktop or mobile browser. (coming soon)
I’m pleased to announce Version 0.0.3, the first version I’m dubbing as ‘public beta.’ Although this is production-quality code, it should be used with caution until it is no longer in BETA status. You are welcome to give it a whirl on your Rails projects today. With an easy 3-step installation into any Rails 4+ app and you can sit back and sweep up tracking info on your visitors.
*MOST* of the core functionality now works! This version 0.0.3 implements fully support for timestamping your visits, the user’s IP address and browser. Support for UTMs & HTTP referrer and more coming soon! If you are curious now’s a great time to try it out, please submit feedback via Github.
This website uses cookies to improve your experience while you navigate through the website. Out of these cookies, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may have an effect on your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.