Golang. Working with go module dependencies

In order to initialize go modules in our project just run the next command

go mod init github.com/<user>/<project>
As a result, go.mod and go.sum will be created. go.mod file describes all the dependencies in the project, as well as the current version of the language and initialized module name.

This file also contains the following elements:

require includes all dependency modules and the associated version that we are going to use in our project.

replace points to the local version of the dependency in Go, not git-web. It will create a local copy of the provider with available versions, so there is no need to install every time we want to refer to the provider.

//indirect implies that we do not use these dependencies inside our project, but there is some module that imports them.

go.sum maintains a checksum, so when we run the project again, it won't install all the packages again. It uses the cache, which is stored in the $GOPATH/pkg/mod directory (module cache directory).

It is possible to create a separate catalog of vendors with available versions. This copies all third-party dependencies to the vendor folder at the root of your project. This adds all the transitive dependencies needed to run the vendor’s package. When vendoring is enabled, the go command will download packages from the vendor directory instead of downloading modules from their sources into the module cache and using the already downloaded packages.

go mod vendor

There is a command to remove installed packages. This command is used to clear the mod cache, which is stored in $GOPATH/pkg/mod. The -modcache flag removes the entire module load cache, including unpacked dependency version source code.

go clean -modcache

To view a list of available package versions

go list -m -versions github.com/pkg/math

To view a list of available versions of all packages that are used in your project

go list -m all

The list of available versions will only be displayed if the package has a release version. In this case, if a package is used that does not have a release version, then go.mod will display an entry in the following format instead of the version - v0.0.0-<timestamp>-<commit-hash> In fact, the availability of a release version can also be found through git commands:

git ls-remote —tags git://github.com/.git | less
git ls-remote —heads git://github.com/.git | less

Upgrading dependency to the latest version

go get example.com/pkg

Upgrading dependency and all its dependencies to the latest version

go get -u example.com/pkg

Viewing available dependency upgrades. It will show you available minor and patch upgrades for all direct and indirect dependencies

go list -u -m all

Upgrading all dependencies at once. To upgrade all dependencies at once for a given module, just run the following from the root of your module. This upgrades to the latest or minor patch release

go get -u ./...

Or we can upgrade test dependencies

go get -t -u ./...

To also upgrade to a specific version using Go modules

go get foo@v1.2.3
go get github.com/pkg/math@v0.3.0

or specifying a commit hash

go get foo@f5801deq7

We can update the package to the latest available version and thus

go get github.com/pkg/math@latest

Thus, given the required version, we can perform downgrading and upgrading. After all such updates and changes in the project code, it is recommended to run the command

go mod tidy

This will then link the current imports in the project and packages listed in go.mod go mod tidy ensures that the go.mod file matches the source code of the module. It adds any missing module requirements needed to build the current module's packages and dependencies, if there are any unused dependencies, go mod tidy will remove them from go.mod accordingly. It also adds any missing entries to go.sum and removes unnecessary entries.

Golang. Encapsulation via interfaces

How we can use all power of encapsulation in golang?
To achieve that we can use so-called interfaces. Below you can see one example of that approach. Our Notifier interface is public and the notifier struct is private. It means that all external clients will be able to use notifiers package publicly only via public interface while other things are hidden. So it’s the power of using encapsulation.

package notifiers

import "fmt"

type Notifier interface {
	Send()
}

type notifier struct {
	client string
}

func NewNotifier(client string) Notifier {
	return &notifier{client: client}
}

func (n *notifier) Send() {
	fmt.Printf("Notifier sent notification via client: %v\n", n.client)
}

package main

import "notifiers"

func main() {
	notifiers.NewNotifier("html client").Send()
}
But what if we need to get access to the state of returning struct instance from the package instead of being able to work only with the public interface? In that case, we can change the return type in the signature of the public constructor NewNotifier.

func NewNotifier(client string) *notifier {
	return &notifier{client: client}
}
And it will be workable. But if you try to run golint then you will get the next error message:

exported func NewNotifier returns unexported type *notifier, which can be annoying to use (golint)


There is only one thing we can do to fix it. This is to make the return struct public.

package notifiers

import "fmt"

type Notifier struct {
	client string
}

func NewNotifier(client string) *Notifier {
	return &Notifier{client: client}
}

func (n *Notifier) Send() {
	fmt.Printf("Notifier sent notification via client: %v\n", n.client)
}
As you can see, the Notifier interface had to be removed. Thus, we threw more details out. In my opinion, it’s a less abstract approach. Despite this, we can still use encapsulation both at the package and at the level of public structure (by using private fields).

Ruby. Working with PostGIS extension

Postgres is well known as a modern and powerful database. One of the possibilities is support for working with geospatial data. Initially, this requires the installation of the PostGIS extension. Next, in your rails application, you need to use gems such as:
  • gem 'activerecord-postgis-adapter'
  • gem 'rgeo'
  • gem 'geocoder'

Below is an example of working with a truck model, in which one of the fields (let's call it waypoints) has a special data type - geometry(linestring).


# app/lib/geo.rb
class Geo
  SRID = 4326
  
  METHODS = %i[point line_string]
  
  class << self
    delegate *METHODS, prefix: 'cartesian', to: :cartesian_factory
    delegate *METHODS, prefix: 'spherical', to: :spherical_factory

    def cartesian_factory
      @cartesian_factory ||= RGeo::Cartesian.factory
    end

    def spherical_factory
      @spherical_factory ||= RGeo::Geographic.spherical_factory(srid: SRID)
    end

    def pairs_to_points(pairs)
      pairs.map { |pair| point(pair[0], pair[1]) }
    end
    
    def pairs_to_line_string(pairs)
      points = pairs_to_points(pairs)
      cartesian_line_string(points)
    end
  end
end


# app/models/concerns/geo_workable.rb
module GeoWorkable
  extend ActiveSupport::Concern

  METHODS = %i[pairs_to_points pairs_to_line_string].freeze

  included do
    delegate *METHODS, to: Geo
  end

  class_methods do
    delegate *METHODS, to: Geo
  end
end


# app/models/track.rb
class Track < ActiveRecord::Base
  include GeoWorkable
  
  def coordinates
    self.waypoints.coordinates
  end
  
  def update_waypoints(coordinates)
    self.waypoints = pairs_to_line_string(coordinates)
    self.save
  end
end


track = Track.find(...)
coordinates = track.coordinates # [[longitude, latitude], [longitude, latitude], ...]
# do some transformations on that coordinates
track.update_waypoints(coordinates)