“...I've been working since 2008 with Ruby / Ruby on Rails, love a bit of Elixir / Phoenix and learning Rust. I also poke through other people's code and make PRs for OpenSource Ruby projects that sometimes make it. Currently working for InPay...”

Rob Lacey (contact@robl.me)
Senior Software Engineer, Brighton, UK

Logging Unpermitted Parameters

Found out Unpermitted Parameters has instrumentation. So we can hijack it

*config/initializer/unpermittted.rb

logger = Logger.new(Rails.root.join('log', 'unpermitted.log'))
ActiveSupport::Notifications.subscribe 'unpermitted_parameters.action_controller' do |name, start, finish, id, payload|
  msg = "Unpermitted_parameters: #{payload[:keys].map { |k| ":#{k}" }.join(', ')}"
  puts Rainbow(msg).red.underline
  logger.debug(msg)
end

And now I have STDOUT and logs full of debug with no context whatsoever but hey, thats’s in Rails 7

https://blog.saeloun.com/2021/06/16/rails-7-provides-context-when-logging-unpermitted-parameters.html#after

Robs-MacBook-Pro:sard rl$ tail -f log/unpermitted.log 
# Logfile created on 2021-07-23 16:37:05 +0100 by logger.rb/66358
D, [2021-07-23T16:37:34.610337 #27379] DEBUG -- : Unpermitted_parameters: :id

A case of the Fridays

The Brighton Ruby Meetup by the bins round the back of Greggs, London Road went well. Greggs ran out of Vegan pastry flakes, but Sally brought us up to speed on Class inheritance and calculating the volume of a glazed doughnut under a graph.

Write code with code so you don't have to write code

I’m currently trying to bring in Strong Parameters, replacing attr_accessible with a custom class for each model class we want to sanitize params for. We don’t want to fill controllers with massive blah_params methods if we’re likely to re-use them everywhere. So if we hand this over to a class that can manage it….all the better.

BlahPermit.with(param.fetch(:blah, {})).permit

I’d rather not go through every file and create an accompanying class for what, at this stage, will only require a small tweak to each attr_accessible call.

So here we can cope with both classes we can load or define on the fly if they don’t already exist.

module NotAtAllAttrAccessible
  extend ActiveSupport::Concern

  module ClassMethods
    def attr_accessible(*attrs)
      klass = define_permit_class
      klass.permits += attrs

      define_method :accessible_attributes do
        klass.permits
      end unless method_defined? :accessible_attributes
    end

    private

    def define_permit_class
      name = "#{self.name}Permit"
      Object.const_get(name)

    rescue NameError
      Object.const_set(name, Class.new(Permit))
    end
  end
end

And that will equate to

class Blah
  attr_accessible :thing, :the, :blair, :turnip
end

class Permit
  cattr_accessor :permits, default: []

  class << self
    def klass(klass)
      "#{klass}Permit".constantize
    rescue NameError
      Permit
    end

    def with(params)
      new.with(params)
    end
  end

  def with(params)
    @params = params.is_a?(ActionController::Parameters) ? params : ActionController::Parameters.new(params)
    self
  end

  def permit!
    permits.any? ? params.permit(*permits) : params.permit!
  end

  def permits
    self.class.permits
  end

  def params
    @params || ActionController::Parameters.new
  end
end

class BlahPermit < Permit
  @@permits = [:thing, :the, :blair, :turnip]
end

And we can then crush Parameters like a boss.

def resource_params
  Permit.klass(Blah).with(param.fetch(:blah, {})).permit
end

This Space Between Us

Grouped and Non-Grouped Options for Select

module ActionView
  module Helpers
     module FormOptionsHelper
      def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
        collection.map do |group|
          next options_for_select([group], selected_key) if value_for_collection(group, group_method).is_a?(String)

          option_tags = options_from_collection_for_select(
            value_for_collection(group, group_method), option_key_method, option_value_method, selected_key)
          content_tag("optgroup", option_tags, label: value_for_collection(group, group_label_method))
        end.join.html_safe
      end
    end
  end
end

D3 Colour Wheel

Quick, dirty colour wheel for Uncle Simon easily adapted from this example code.

https://www.essycode.com/posts/create-color-wheel-javascript-d3/

Should probably Stimulusorizificate it.

node-sass moaning like a boss

And again…

/Users/rl/.node-gyp/16.5.0/include/node/v8-internal.h:454:38: error: no template named 'remove_cv_t' in namespace 'std'; did you mean 'remove_cv'?
            !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
                                ~~~~~^~~~~~~~~~~
                                     remove_cv
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/type_traits:776:50: note: 'remove_cv' declared here
template <class _Tp> struct _LIBCPP_TEMPLATE_VIS remove_cv
                                                 ^
1 error generated.
make: *** [Release/obj.target/binding/src/binding.o] Error 1
gyp ERR! build error 
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/Users/rl/repos/rostering/vendor/engines/sard_core/ui/node_modules/node-gyp/lib/build.js:262:23)
gyp ERR! stack     at ChildProcess.emit (node:events:394:28)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12)
gyp ERR! System Darwin 20.5.0
gyp ERR! command "/usr/local/Cellar/node/16.5.0/bin/node" "/Users/rl/repos/rostering/vendor/engines/sard_core/ui/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library="
gyp ERR! cwd /Users/rl/repos/rostering/vendor/engines/sard_core/ui/node_modules/node-sass
gyp ERR! node -v v16.5.0
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok

Yeah, srsly dude. This isn’t right for us either. I think that brew upgrade I did last night broke something, must have upgraded node. Apparently upgrading beyond node-sass 6.0.1 is fine now. Last time this happened I had to downgrade node.

https://stackoverflow.com/questions/67241196/error-no-template-named-remove-cv-t-in-namespace-std-did-you-mean-remove

No, can’t see anything above 6.0.1 out there :S I need to get back to node@14 again.

Robs-MacBook-Pro:ui rl$ brew unlink node
Unlinking /usr/local/Cellar/node/16.5.0... 7 symlinks removed.
Robs-MacBook-Pro:ui rl$ brew link --overwrite node@14
Linking /usr/local/Cellar/node@14/14.17.0... 3872 symlinks created.

What a faff. Now I can play with d3

Porting Ruby to JS.

module Rack
  module Utils
    def append_to_query_string(url, params = {})
      base, query = url.split('?')
      params = parse_query(query).merge(params.stringify_keys)
      params = params.sort_by { |k, _v| k.to_s }.to_h
      query = build_query(params)
      [base, query].reject(&:blank?).join('?')
    end
    module_function :append_to_query_string
  end
end
window.BLAHUtils = {
  isPresent: function(object) {
    return object != undefined &&  object.toString().trim() !== ''
  },
  presense: function(object) {
    return this.isPresent(object) ? object : null
  },
  appendToQueryString: function(url, params = {}) {
    [base, query] = url.split('?')
    params = {
      ...this.parseQuery(query),
      ...params
    }
    query = this.buildQuery(params)
    return [base, query].filter(v => this.isPresent(v)).join('?')
  },
  parseQuery: function(query) {
    if (!this.isPresent(query)) {
      return {}
    }
    return query.split('&').reduce((h, pair) => {
      [k, v] = pair.split('=')
      h[k] = decodeURIComponent(v)
      return h
    }, {})
  },
  buildQuery: function(params) {
    return Object.entries(params).map(([k, v]) => {
      return [k, encodeURIComponent(v)].join('=')
    }).join('&')
  }
}

USB debugging on Redmi Android phones

USB debugging on a Redmi.

https://www.syncios.com/android/how-to-debug-xiaomi-redmi-phone.html

JS Mystery #667

This doesn’t appear anywhere in our codebase. Does iPhone Safari inject this under some circumstances?

undefined is not an object (evaluating 'document.getElementsByTagName('video')[0].webkitExitFullScreen')