Half-Elf on Tech

Thoughts From a Professional Lesbian

Category: How To

  • Grunt Can’t Build Sass on High Sierra

    Grunt Can’t Build Sass on High Sierra

    Working on a site, I went to run my grunt command to compile sass, I got an error.

    Running "sass:dist" (sass) task
    /usr/local/Cellar/ruby/2.4.2_1/lib/ruby/2.4.0/rubygems.rb:271:in `find_spec_for_exe': can't find gem sass (>= 0.a) (Gem::GemNotFoundException)
        from /usr/local/Cellar/ruby/2.4.2_1/lib/ruby/2.4.0/rubygems.rb:299:in `activate_bin_path'
        from /usr/local/bin/sass:22:in `<main>'
    Warning: Exited with error code 1 Use --force to continue.

    No good, right? And googling around for that didn't help me at all until I drilled down and realized the sass error was a Ruby error.

    Upgrade Ruby

    First, install Homebrew. I know there are many other ways to handle installs on Macs, but Homebrew has been my go-to for a few years. It makes my life easier.

    Once Homebrew is installed run the following command: brew install rbenv ruby-build

    If this gives you an error, like missing xcrun, then run this: xcode-select --install

    Once you have rbenv installed, it's time to upgrade ruby:

    rbenv install 2.4.2
    rbenv global 2.4.2

    Awesome! But grunt still doesn't work. And this is because Sass isn't installed.

    Install Sass

    This should be easy, right? sudo gem install -n /usr/local/bin sass and off you go. The problem is you may get this error:

    ERROR:  While executing gem ... (TypeError)
        no implicit conversion of nil into String

    Not so helpful. The magic here is going backwards to go forward. Update your gems first: sudo gem update --system and then run the installer for sass.

    Why Did This Happen?

    The short answer is that Mac changed versions and everything got out of sync. I'm not a system code expert so I can't explain it better than that.

    How you fix these things is by understanding how to logically step backwards through the errors. Most of the time, we see it as "Grunt says Sass is broken!" and the trick here is to ask yourself "What runs Sass?" and the answer is "Ruby." So instead of looking up "Why can't I run sass on grunt?" we look up "Ruby can't compile SCSS on Mac High Sierra" because that's the ultimate answer.

    Walking backwards is the secret sauce for all solutions with software, I feel. Why can't Grunt run Sass? Because Ruby can't compile? Why can't Ruby compile SCSS? Let's reinstall Sass! Oh wait, that has an error. Okay, let's upgrade Ruby. Oh that has an error too? Let's solve that.

    It's not fun, unless you're me and think snarling at a laptop in the conference room of your friends' office is cheerful.

  • PHP Spaceship

    PHP Spaceship

    It’s no Jefferson Starship, but in PHP 7 there’s an awesome new comparison operator called ‘Spaceship.’

    Dragon Fly

    Operators are those things we use to tell PHP what ‘operations’ to perform on variables and values. Comparison operators are used to compare two values (like a number or string). And Spaceship — <=> — is a cool new one.

    It’s a combined comparison operator and returns:

    • 0 if values on either side are equal
    • 1 if value on the left is greater
    • -1 if the value on the right is greater

    Red Octopus

    So why would anyone use it?

    Let’s say you have an array of characters with lists of when they died. But the characters are ordered alphabetically. Sara Lance comes after Lexa, even though Sara died first. And let’s say you want to take the list of all the characters who died and put them in the order of death, not their names.

    To do this in PHP, you would use usort or uasort to ‘user sort’ the array. The A in the sort is if you have an associative array, and you want to keep ‘name’ and ‘date of death’ connected when you sort. Which you do.

    Spitfire

    In PHP 5, the sort would look like this:

    uasort( $death_list_array, function($a, $b) {
    	$return = '0';
    	if ( $a['died'] &lt; $b['died'] ) $return = '-1';
    	if ( $a['died'] &gt; $b['died'] ) $return = '1';
    	return $return;
    }
    

    Which isn’t bad, but it’s messy right?

    Earth

    In PHP 7, the sort would look like this:

    uasort( $death_list_array, function($a, $b) {
    	$return = $a['died'] &lt;=&gt; $b['died'];
    	return $return;
    }
    

    Way nicer, and faster, isn’t it?

    Freedom at Point Zero

    Why not use it all the time? Because it’s PHP 7+ only, and for some reason not all servers have PHP 7 as the command line version of PHP. Which means if you use it, sometimes wp-cli commands don’t run.

    However. If you have PHP 7 all across the board, then use the starship and save yourself some flying time.

  • Dynamic Amazon Associates

    Dynamic Amazon Associates

    I preface this with a note that there are actually a lot of WordPress plugins that purport to do this.

    Stable, Topical, Content

    Sometimes a blog is just a blog, and you write what you write. Other times you have a site that covers an ever expanding, diverse plethora of topics. When you have topical content that can be easily identified and codified, the game changes. You no longer are forced to rely on generic ads, you can pick and chose what works best for your specific content.

    There are two general ways to achieve this. The first way is adding a specific ad to each page, manually, and the second is figuring out how to automate it.

    Using an API

    Amazon has a Product Advertising API which gives developers access to the entire product catalog, which can let you programmatically determine what links to call and when. Like a lot of Amazon APIs, it’s not exactly written in low-geek levels of usage. This is my constant complaint about Amazon and their services: they were not written to be easily understood.

    Thankfully, unlike my experience with making a custom Alexa skill, I did not actually have to invent the wheel. This time there is a fully functional, if poorly documented, Amazon Product Adverstising Library based on PHP REST and SOAP using the Product Advertising API..

    A ‘Simple’ Application

    The API is best installed via composer, which isn’t my favorite method. Nothing against composer, it’s great to pull down a library and it’s dependancies. My issues with it are that people don’t properly flag their archives so even if you use --prefer-dist on your build, you still get all the tests and documentation and that annoys me.

    Anyway. Use composer, get the files, and then you can use it to call Amazon searches and build links:

    <?php
    include_once( 'vendor/autoload.php' );
    
    use ApaiIO\Configuration\GenericConfiguration;
    use ApaiIO\Operations\Search;
    use ApaiIO\ApaiIO;
    
    $conf = new GenericConfiguration();
    $client = new \GuzzleHttp\Client();
    $request = new \ApaiIO\Request\GuzzleRequest($client);
    
    $conf
        ->setCountry( 'com' )
        ->setAccessKey( AWS_API_KEY )
        ->setSecretKey( AWS_API_SECRET_KEY )
        ->setAssociateTag( AWS_ASSOCIATE_TAG )
        ->setRequest( $request );
    $apaiIO = new ApaiIO( $conf );
    
    $search = new Search();
    $search->setCategory( 'DVD' );
    $search->setActor( 'Lucy Lawless' );
    $search->setKeywords( 'Xena' );
    
    $formattedResponse = $apaiIO->runOperation( $search );
    
    var_dump( $formattedResponse );
    

    Obviously you don’t ‘var_dump’ for real, but it’s a good way to get an idea of what kind of data you’ll be getting back. You can also reformat the output by changing the response transformer. For example, if you want an array, you can add this to your $conf setting:

      ->setResponseTransformer( new \ApaiIO\ResponseTransformer\XmlToArray() )
    

    Customizing Keywords

    Of course not everyone wants every search result to be about a warrior princess. Pity, but that’s the world for you. These three aspects are the ones most people will care about:

    $search->setCategory( 'DVD' );
    $search->setActor( 'Lucy Lawless' );
    $search->setKeywords( 'Xena' );
    

    All of that data was actually saved in the post, so I wrote my code to extract it. That is the actual magic, though. On pages for characters, we had their actors. On pages for shows, we had the show title and genre. Using that, we were able to logically extract the information to generate the appropriate ads.

    Why Not a Plugin?

    I did mention there were plugins that do this. The problem was that I needed to mess with that customization so much. I had to hand code in the logic (which was not perfect) to show the ‘right’ links on the right pages. No plugin met all my needs nor permitted enough customization in the right ways.

  • FacetWP: Spinning While Updating

    FacetWP: Spinning While Updating

    When you use FacetWP you can do some cool things like change the ‘count’ output of a page. Using the function facetwp_display() you can add facetwp_display( 'counts' ) to your page title, and then a boring old archive title goes from “List of Characters (2022)” to “List of Characters (1-24 of 2022)”

    But… What if you could do more?

    What Could Be More?

    If you have a lot of data, sometimes a page can load and FacetWP spins while it collects everything in it’s wee javascripty brain. When that happens, you have a cognitive moment of “What?” And in order not to lose a user, you want to indicate, somehow, that an action is happening. A spinning icon is, I think, a great way to do that.

    So with that in mind, I want to do this:

    List of Characters showing a spinning icon for the number

    And I did.

    The Code

    This needs javascript. I used some logic from the FacetWP documentation and some memories about how you can replace text with javascript and came up with this:

    (function($) {
    	$(document).on('facetwp-refresh', function() {
    		$('.facetwp-count').html('<i class="fa fa-spinner fa-pulse fa-fw"></i><span class="sr-only">Loading...</span>');
    	});
        
        $(document).on('facetwp-loaded', function() {
    	   $('.facetwp-count').html('');
    	});
        
    })(jQuery);
    

    Then I slap it into my PHP code like so:

    $count_posts = facetwp_display( 'counts' );
    
    the_archive_title( '<h1 class="facetwp-page-title page-title">' . $title, ' (' . $count_posts . '<span class="facetwp-count"></span>)</h1>' );
    

    The content for $count_posts shows nothing while it’s loading, so the check for facetwp-loaded will handle it perfectly.

  • Alternate Symbols

    Alternate Symbols

    I like to remotely host my SVGs and then call them in my code. For the most part, this works well, and I wrote out some basic code to check if they’re defined and accessible before displaying.

    But what if you wanted your fallback to be a font-icon?

    And remember, you want your code to be DRY as a bone. So no repeating chunks of code all over the place, please and thank you.

    The Code

    There is but ONE requirement here. You have to define HALFELF_SYMBOLICONS_PATH as the path to your SVGs. In my case, mine is something like http://my-cool-icons.objects-us-west-1.dream.io/svgs/ because I’m using DreamObjects for them. Any cloud host works great for this, mind you.

    function halfelf_symbols( $svg = 'square.svg', $fontawesome = 'fa-square' ) {	
    
    	$icon = '<i class="fa ' . $fontawesome . '" aria-hidden="true"></i>';
    
    	if ( defined( 'HALFELF_SYMBOLICONS_PATH' ) ) {
    		$response      = wp_remote_get( HALFELF_SYMBOLICONS_PATH );
    		$response_code = wp_remote_retrieve_response_code( $response );
    		
    		if ( $response_code == '200' ) {
    			$get_svg      = wp_remote_get( LP_SYMBOLICONS_PATH . $svg );
    			$response_svg = wp_remote_retrieve_response_code( $get_svg );
    			$icon         = ( $response_svg == '200' )? $get_svg['body'] : 'square.svg';
    		}
    	}
    
    	return $icon;
    }
    

    This checks for the existence of the server used and if the actual icon exists before it sets things.

    Usage Example

    In my code, I define it like this:

    $icon  = halfelf_symbols( 'users.svg', 'fa-users' );
    $title = '<span role="img" aria-label="users" title="Users" class="TEMPLATE users">' . $icon . '</span>';
    

    This sets the icon and then calls the span which will help make this more accessibility friendly. I could have coded the span into the function, but since I often have it all dynamically generated, it worked more sustainably this way.

    In this example, I call it like this:

    the_archive_title( '<h1 class="page-title">' . $title . '</h1>' );
    

    And voila. Icons in my page title.

  • Make WordPress Gay

    Make WordPress Gay

    In September, WordPress.com changed their admin bar from the normal black or blue to a rainbow color. Why?

    Australia will be holding a national survey on marriage equality over the next two months. To show our support as a company for marriage equality, we’re showing the rainbow bar in the WordPress.com admin bar to all logged-in Australian visitors. You can read more about the marriage equality campaign here: http://www.equalitycampaign.org.au/

    And this was super cool. They’d done it before when Gay Marriage was legalized in the US and it’s much appreciated as a show of solidarity. But… The menu bar only shows if you’re visiting WordPress.com from Australia. This causes two problems for me:

    1) I’m in the US
    2) I self host

    Don’t worry, there’s a solution.

    The ‘Official’ Solution

    The code for what WordPress.com does can be found on the Github repository for Calypso. But that’s all mixed in with a lot of extra ‘stuff’ that has to be there for their services, like geo-location and so on.

    The rest of us don’t need that, and since Gary (the fellow who wrote the ticket) is a friend of mine, I asked him if the code was available. It is.

    Rainbow Bar Code

    <?php
    
    /*
     * Plugin Name: Rainbow Bar!
     */
    
    function rainbow_bar() {
    ?>
    	<style type="text/css">
    		#wpadminbar {
    			background: linear-gradient(
    				to bottom,
    				#e24c3e 0%,
    				#e24c3e 16.66667%,
    				#f47d3b 16.66667%,
    				#f47d3b 33.33333%,
    				#fdb813 33.33333%,
    				#fdb813 50%,
    				#74bb5d 50%,
    				#74bb5d 66.66667%,
    				#38a6d7 66.66667%,
    				#38a6d7 83.33333%,
    				#8c7ab8 83.33333%,
    				#8c7ab8 100% );
    		}
    
    		#wpadminbar,
    		#wpadminbar .quicklinks > ul > li {
    			-webkit-box-shadow: unset;
    			-moz-box-shadow: unset;
    			box-shadow: unset;
    		}
    
    		#wpadminbar .ab-top-menu > li > a {
    			background-color: rgba( 50, 55, 60, .85 );
    		}
    	</style>
    <?php
    }
    add_action( 'wp_before_admin_bar_render', 'rainbow_bar' );
    

    Install that to get this:

    Rainbow Pride WP Admin Bar

    Small Changes

    In order to make it look ‘right’ for me, I changed two things

    I removed this:

    	#wpadminbar .ab-top-menu > li > a {
    		background-color: rgba( 50, 55, 60, .85 );
    	}
    

    And I added this:

    	#wpadminbar .ab-item, #wpadminbar a.ab-item, #wpadminbar > #wp-toolbar span.ab-label, #wpadminbar > #wp-toolbar span.noticon,
    	#wpadminbar .ab-icon, #wpadminbar .ab-icon:before, #wpadminbar .ab-item:before, #wpadminbar .ab-item:after {
    		color: #000;
    	}
    

    Caveats

    Even with my changes, the color for my Jetpack stats looks wrong. It’s too washed out. And that’s because it’s apparently hardcoded into the plugin in a way I can’t overwrite. I can live with that problem.

    The other issue is that this will only show for logged in users (unless you’re using some code to always show the admin bar). That begs the question, of course, of why would you do this if only logged in users?

    If you’re running a queer themed website that happens to use WordPress … like, say, Autostraddle, then this makes perfect sense. For me, it just makes me feel happy to see that pride rainbow all the time.