Journal Entries

Journal Entry, July 12th to July 19th 2019

July 19, 2019 · 1 min read

MySQL

  1. Mon Jul 15 2019

    • Json field queries

      SELECT
          *, `payload`->"$[0].eCommerceOrderId" AS `e_commerce_order_id`
      FROM
          pendingOrderStatus
      WHERE
          `status` = 'sent';
      SELECT
          id, `order`->>'$.e_commerce_order_id' AS `e_commerce_order_id`
      FROM
          order_log_transforms
      WHERE
          `order`->>'$.e_commerce_order_id' IS NOT NULL;
  2. Wed Jul 17 2019

    • Importing from the source table in one database to the destination in another database.

      INSERT INTO `materials-staging`.`files` (`id`, `path`, `type`, `hash`, `material_id`, `active`, `created_at`, `updated_at`, `deleted_at`)
      SELECT
          *
      FROM
          `materials_staging`.`mat_files`;

Journal Entry, July 5th to July 12th 2019

July 12, 2019 · 5 min read

Laravel Nova

  1. Mon Jul 8

    • In this example the path has our directory and they're stored in a format like storage/app/public/images/HELL/originalFileName.extension. The image column saves only the name as it already appears.
    • The biggest takeaway from this is the usage $this->model()->path. At a resource level we can get the model columns used because it's defined as $this->model. Because we're on the detail view the path in this case is filled in. We don't need methods that include the models.
    • This is one of the biggest "aha!" moments with Nova where the docs aren't very intuitive nor do they promote this in any meaningful way in the examples I've seen.

      Image::make('Header Image', 'image')
          ->disk('public')
          ->store(function (Request $request, \App\Models\Bucket $model) {
              $path = "images/{$model->path}";
              $file = $request->image->storeAs($path, $request->image->getClientOriginalName(), 'public');
              return [
                  'image' => $request->image->getClientOriginalName(),
              ];
          })
          ->prunable()
          ->preview(function ($image, $disk) {
              return $image ? \Storage::disk($disk)->url("images/{$this->model()->path}/$image") : null;
          })
          ->thumbnail(function ($image, $disk) {
              return $image ? \Storage::disk($disk)->url("images/{$this->model()->path}/$image") : null;
          }),
  2. Tue Jul 9

    • In this example we're creating a read-only path column when the model is first created. Initially I looked to append the column ID but this is not possible without removing the image upload on the create form.
    • The getPathAttribute() essentially creates our path value in the same way from our slug and it's only used during the create model upload.
    • This is sleight of hand at its finest, the real path column is what we're looking for but we're hacking in functionality to work at the create form level, bypassing the need to restrict this to only during edits.

      public function getPathAttribute()
      {
          $path = (array_has($this->attributes, 'path') && $this->attributes['path']) ? $this->attributes['path'] : null;
          // We only care to calculate the path in the same manner as our creating method when the model is first used
          if ($path === null) {
              $slug = (array_has($this->attributes, 'slug') && $this->attributes['slug']) ? static::stripSpecialCharacters($this->attributes['slug']) : null;
              return $slug;
          }
          return $path;
      }
      
      protected static function boot()
      {
          parent::boot();
      
          static::created(function(Bucket $bucket) {
              $bucket->path = static::stripSpecialCharacters($bucket->slug);
              $bucket->save();
          });
      }
  3. Wed Jul 10

    • Redis, specifically Horizon troubleshooting
    • Filter the specific keys to look for:

      > keys horizon:queue*
      1) "horizon:queues:orders:transfer:reserved"
      2) "horizon:queue:orders:transform"
      3) "horizon:queue:orders:transfer"
      4) "horizon:queues:orders:transfer"
    • Get the type of our keys:

      > type "horizon:queues:orders:transfer:reserved"
      zset
      
      > type "horizon:queue:orders:transform"
      hash
      
      > type "horizon:queue:orders:transfer"
      hash
      
      > type "horizon:queues:orders:transfer"
      list
      
      > type "horizon:job:App\\Jobs\\PendingOrderTransformJob"
      hash
      
      > type "horizon:job:App\\Jobs\\PendingOrderTransferJob"
      hash
    • Look at the values for the specific keys:

      > zrange "horizon:queues:orders:transfer:reserved" 0 2 WITHSCORES
      1) "{\"type\":\"job\",\"timeout\":null,\"tags\":[\"App\\\\Model\\\\PendingOrderStatus:1258\"],\"id\":\"2520\",\"data\":{\"command\":\"O:32:\\\"App\\\\Jobs\\\\PendingOrderTransferJob\\\":8:{s:9:\\\"\\u0000*\\u0000status\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":3:{s:5:\\\"class\\\";s:28:\\\"App\\\\Model\\\\PendingOrderStatus\\\";s:2:\\\"id\\\";i:1258;s:10:\\\"connection\\\";s:5:\\\"mysql\\\";}s:6:\\\"\\u0000*\\u0000job\\\";N;s:10:\\\"connection\\\";s:13:\\\"redis:horizon\\\";s:5:\\\"queue\\\";s:15:\\\"orders:transfer\\\";s:15:\\\"chainConnection\\\";N;s:10:\\\"chainQueue\\\";N;s:5:\\\"delay\\\";N;s:7:\\\"chained\\\";a:0:{}}\",\"commandName\":\"App\\\\Jobs\\\\PendingOrderTransferJob\"},\"displayName\":\"App\\\\Jobs\\\\PendingOrderTransferJob\",\"timeoutAt\":1562789478,\"pushedAt\":\"1562787678.5943\",\"maxTries\":null,\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"attempts\":3}"
      2) "1562788306"
      
      > hgetall "horizon:queue:orders:transfer"
      1) "throughput"
      2) "52"
      3) "runtime"
      4) "5623.0248076923099"
      
      > lrange "horizon:queues:orders:transfer" 0 99
      ...
      47) "{\"type\":\"job\",\"timeout\":null,\"tags\":[\"App\\\\Model\\\\PendingOrderStatus:1280\"],\"id\":\"2564\",\"data\":{\"command\":\"O:32:\\\"App\\\\Jobs\\\\PendingOrderTransferJob\\\":8:{s:9:\\\"\\u0000*\\u0000status\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":3:{s:5:\\\"class\\\";s:28:\\\"App\\\\Model\\\\PendingOrderStatus\\\";s:2:\\\"id\\\";i:1280;s:10:\\\"connection\\\";s:5:\\\"mysql\\\";}s:6:\\\"\\u0000*\\u0000job\\\";N;s:10:\\\"connection\\\";s:13:\\\"redis:horizon\\\";s:5:\\\"queue\\\";s:15:\\\"orders:transfer\\\";s:15:\\\"chainConnection\\\";N;s:10:\\\"chainQueue\\\";N;s:5:\\\"delay\\\";N;s:7:\\\"chained\\\";a:0:{}}\",\"commandName\":\"App\\\\Jobs\\\\PendingOrderTransferJob\"},\"displayName\":\"App\\\\Jobs\\\\PendingOrderTransferJob\",\"timeoutAt\":1562789480,\"pushedAt\":\"1562787680.4057\",\"maxTries\":null,\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"attempts\":3}"
      
      > hgetall "horizon:job:App\\Jobs\\PendingOrderTransferJob"
      1) "throughput"
      2) "7"
      3) "runtime"
      4) "7738.0300000000007"
    • Dump our entire key list (the -n option specifies the database):

      > redis-cli -n 3 keys "*" > horizon.keys
      
      horizon:snapshot:job:App\\Jobs\\PendingOrderTransformJob
      horizon:supervisor:orange-business-rules-swap-f6S1:supervisor-order-transform
      horizon:job:App\\Jobs\\PendingOrderTransformJob
      horizon:failed_jobs
      horizon:snapshot:job:App\\Jobs\\PendingOrderTransferJob
      horizon:masters
      horizon:job_id
      horizon:queues:orders:transfer:reserved
      horizon:snapshot:queue:orders:transform
      horizon:supervisor:orange-business-rules-swap-f6S1:supervisor-default
      horizon:supervisor:orange-business-rules-swap-f6S1:supervisor-order-transfer
      horizon:supervisors
      horizon:recent_failed_jobs
      horizon:master:orange-business-rules-swap-f6S1
      horizon:job:App\\Jobs\\PendingOrderTransferJob
      horizon:measured_jobs
      horizon:queue:orders:transform
      horizon:measured_queues
      horizon:recent_jobs
      horizon:queue:orders:transfer
      horizon:last_snapshot_at
      horizon:monitor:time-to-clear
      horizon:snapshot:queue:orders:transfer
      horizon:metrics:snapshot
      horizon:queues:orders:transfer
    • Note: The list of keys was manually filtered by removing anything with a key like horizon:id or horizon:failed*.
  4. Thu Jul 11

    • Do not use Redis::throtle() with the retryUntil() method.

      Redis::throttle($key)->allow(1)->every(60)->then(function () {
          $this->execute();
      }, function () {
          // Could not obtain lock...
          return $this->release(10);
      });
      
      /**
       * Determine the time at which the job should timeout.
       *
       * @return \DateTime
       */
      public function retryUntil()
      {
          return now()->addMinutes(60);
      }
    • The retryUntil method is utilized in getJobExpiration. It seems to be more of a timeout per queue or per job class rather than per job instance like I was expecting.
  5. Fri Jul 12

    • Setting a redis override for our custom systemd configuration

      sudo systemctl edit redis-server
    • The command opens nano to an override file in /etc/systemd/system where we paste the following:

      [Service]
      ReadWriteDirectories=-/mnt/orange-business-rules/var/lib/redis
    • Now our custom directory can be saved to within redis. Without this, every time apt would update redis our service definition would be blown away. * Now we have something that will stand whenever redis is updated in the future.

Elixir Koans

  1. Thu Jul 11

    • Maps

      • Map.fetch(@person, :age) returns {:ok, value}
    • MapSets

      • MapSet.size(new_set) returns an integer for the size (think array length in JavaScript)
      • MapSet.member?(@set, 3) returns a boolean
      • MapSet.new(@set, fn x -> 3 * x end) creates a new set using the function results of 3 * (each value in the set).
      • MapSet.to_list(@set) looks for brackets not braces. I confused a list with a tuple.

Journal Entry, June 28th to July 5th 2019

July 5, 2019 · 1 min read

Laravel

  1. Tue Jul 2

    • Actions in Laravel pass in a collection of models. On the index screen this could be hundreds but on the detail screen it is always exactly 1, the parent model.
    • This wasn't quite as easy to grok without attaching a debugger but this makes complete sense now.

      /**
       * Perform the action on the given models.
       *
       * @param  \Laravel\Nova\Fields\ActionFields  $fields
       * @param  \Illuminate\Support\Collection  $models
       * @return mixed
       */
      public function handle(ActionFields $fields, Collection $models)
      {
          $count = 1;
          foreach ($models as $model) {
              $count = $this->generateCodes($model->id, $model->code_type_id, $fields->number);
          }
          return Action::message("$count codes were successfully created.");
      }

Phoenix Framework

  1. Sat Jun 29

    • Initial commit
    • Changes to .gitignore for VSCode integration and to remove /config/*.secret.exs. It's unclear if this was present in the earlier version of Phoenix and I weirdly need to add it. It's also possible that I just followed along with the up and running guide.
    • Added editorconfig from .editorconfig for Elixir projects · GitHub

Journal Entry, June 21st to June 28th 2019

June 28, 2019 · 1 min read

Laravel

  1. Wed June 26

    • How Laravel prevents your scheduled jobs from overlapping - Diving Laravel

      • The obr-sage-sync scheduler was broken in production due to network outages affecting Digital Ocean droplets. The sync:customers command would not execute via the scheduler because withoutOverlap() detected the command was still executing.
      • In this instance multiple copies were hung so I rebooted the machine and took a bit hunting this down. As a companion, whenever you kill any command as part of the scheduler, always clear the cache. It's a simple command that'll almost always save time trying to uncover the problem again.

Phoenix Framework

  1. Fri Jun 28

    • Started over with Phoenix 1.4.8 using asdf global packages for the following:

      elixir 1.9.0 (set by /Users/jbrayton/.tool-versions)
      erlang 22.0.4 (set by /Users/jbrayton/.tool-versions)
      nodejs 12.5.0 (set by /Users/jbrayton/.tool-versions)
    • Followed Using ASDF with Elixir and Phoenix but ran into some issues with the homebrew install method.
    • Essentially, I changed the shim for node to:

      #!/usr/bin/env bash
      # asdf-plugin: nodejs 10.15.3
      # asdf-plugin: nodejs 8.11.4
      # asdf-plugin: nodejs 12.5.0
      exec $(brew --prefix asdf)/bin/asdf exec "npm" "$@"
    • The path is really /usr/local/opt/asdf/bin/asdf and seems to work for the other packages. It could be that I was trying to do a local install before a global.

Journal Entry, June 7th to June 14th 2019

June 14, 2019 · 1 min read

Laravel

  1. Mon Jun 10

    • Returning files sorted by creation time.

      $files = collect(\File::files($realDirectory))
          ->filter(function(\SplFileInfo $file) {
              return strpos($file->getFileName(), "{$this->fileName}");
          })->sortBy(function (\SplFileInfo $file) {
              return $file->getCTime();
          }, SORT_NUMERIC)->map(function (\SplFileInfo $file) {
              return [
                  'base' => $file->getBasename(),
                  'name' => $file->getFilename(),
                  'created' => $file->getCTime(),
                  'modified' => $file->getMTime(),
                  'size' => $file->getSize(),
              ];
          });
  2. Fri June 14

Journal Entry, April 12th to April 19th 2019

April 19, 2019 · 1 min read

Elixir Koans

  1. Thu Apr 18

    • Numbers

      • 2 / 2 = 1.0? Division seems to coerce to floats
      • div() returns the divisor or 2, not 2.5. It's an integer
      • rem() returns the remainder or 1, not 0.5. The .5 is dividing the remainder by 2.
      • (2 * 2.0) = 4.0 as the types coerce the result
      • Float.round(1.2) === round(1.2) == false
      • Individual digits builds an array, not count of digits.

        • i.e. Use that fancy peek feature to determine what each method is doing before blindly answering.
      • Remaining unparsable portion counts the space.
      • Integer.parse returns the unparsable part as a string ".2"
      • Float.ciel seems to round > 0 up to the next integer, even 0.25
    • Atoms
    • Tuples

      • List.delete_at is zero-based or more importantly pretty much all these methods are zero based to match array indexes
    • Lists
    • Keyword Lists koan.

      • It's interesting that it's foo: like json but the tuple reads :foo
      • {:foo , "bar"} Needs the space after :foo

Journal Entry, March 22nd to March 29th 2019

March 29, 2019 · 1 min read

MySQL

  1. Mon Mar 25

Specifically using JSON fields as virtual calculations versus setting an explicit string/integer field and filling it in that way. This way we only store the JSON and the rest is done for us.

Elixir Koans

  1. Wed Mar 27

    • Strings

Journal Entry, March 15th to March 22nd 2019

March 22, 2019 · 1 min read

VSCode usage patterns

  • Highlighting the method name like Integer.is_odd gives the definition (hell yes).

    • Peek reminds me of PHPStorm.

JavaScript

  1. Mon Mar 18

Elixir Koans

  1. Wed Mar 20

    • Initially tried using my elixir version but ran into minor issues

      • Changes the file mix.lock due to differences in runtime.
      • Various warnings that come up in the official docker image.
    • Equalities
    • Prepped git repository for common changes and using a feature/pass_1 branch to show our first pass.

      Erlang/OTP 21 [erts-10.2.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
      
      Elixir 1.8.1 (compiled with Erlang/OTP 21)

Journal Entry, February 22nd to March 1st 2019

March 1, 2019 · 1 min read

Phoenix Framework

  1. Mon Feb 25

  2. Tue Feb 26

    • Created self-signed certificate via mix phx.gen.cert
    • Added self-signed port to dev config

      • Unlike valet these self-signed certs are a major eyesore.
    • Added force_ssl configuration though I'm unsure if it's working correctly
    • Changed medicine route to a resource. Added show method and initial view.

      • It took way too long to figure out the <%= @medicine.id %> syntax. The key is the @ symbol.
      • This likely gets easier as I move through the guide.
    • Added title from layout view. Will want to make it Supple - Page dynamically as you move about the app

      • This will seem a little tricky but I suspect it's doable.