├── .bowerrc ├── .bundle └── config ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── bower.json ├── examples ├── example.scss └── index.html ├── lib ├── sassy-maps.rb ├── sassy-maps │ └── version.rb └── tapout │ └── reporters │ └── navigator_reporter.rb ├── package.json ├── sache.json ├── sass ├── _memo.scss ├── _sassy-maps.scss └── sassy-maps │ ├── _map-get.scss │ ├── _map-set.scss │ └── _map-to-string.scss ├── sassy-maps.gemspec └── tests ├── README.md ├── controls ├── 01-map-get.css ├── 02-map-set.css └── 03-memo.css ├── navigator.rb ├── output └── .gitkeey ├── sassy_maps_test.rb └── tests ├── 01-map-get.scss ├── 02-map-set.scss └── 03-memo.scss /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "components" 3 | } 4 | -------------------------------------------------------------------------------- /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: vendor 3 | BUNDLE_DISABLE_SHARED_GEMS: '1' 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /components 3 | .www 4 | .sass-cache 5 | Gemfile.lock 6 | export 7 | *.gem 8 | /vendor 9 | /tests/output/**/*.diff 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | - 2.1.1 5 | gemfile: 6 | - Gemfile 7 | branches: 8 | only: 9 | - 0.x.x 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to Sassy Maps 2 | 3 | We love contributors! Yes we do! If you would like to contribute to Sassy Maps, please follow the following guidelines: 4 | 5 | * **DO NOT ISSUE A PULL REQUEST WITHOUT RELATED ISSUE!!** All pull requests must reference an issue in the issue queue and will only be looked at after discussion about that issue has taken place. Any pull request created that does not reference an issue will be closed. 6 | * Each individual feature you would like add, or bug you would like to squash, should be an individual pull request. Each pull request should be from an individual feature branch to either the latest stable or development branch. **The current *stable* branch is 0.x.x. The current *development* branch is 0.x.x**. Contributions that are not in the form of a pull request will not be considered. If your pull request does not apply cleanly we will ask you to fix that before we will look into pulling it in. We may ask you to update or make changes to the code you've submitted, please don't take this the wrong way. If a pull request smells (such as if a large amount of code is all within a single commit, or the coding standards aren't in line with core Singularity) we may ask you to rewrite your commit. -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sam Richard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sassy Maps [![Gem Version](https://badge.fury.io/rb/sassy-maps.svg)](http://badge.fury.io/rb/sassy-maps) [![Build Status](https://travis-ci.org/asm-helpful/helpful-web.svg?branch=master)](https://travis-ci.org/asm-helpful/helpful-web) 2 | Sassy Maps adds a variety of functions aimed at helping you work with Sass 3.3 maps much easier. 3 | 4 | ## Table of Contents 5 | 6 | 1. [Requirements](#requirements) 7 | 2. [Installation](#installation) 8 | 3. [Using Sassy Maps](#using-sassy-maps) 9 | 4. [Functions](#functions) 10 | 5. [Optional Modules](#optional-modules) 11 | * [Memo](#memo) 12 | 13 | ## Requirements 14 | 15 | Sassy Maps is a Sass extension, so you're going to need Sass installed. If you do not already have Sass installed, please read [Install Sass](http://sass-lang.com/install). Sassy Maps 0.3.x requires *Sass 3.3.0* or higher. 16 | 17 | **It is HIGHLY RECOMMENDED that you run all Sass (or Compass if using it) commands through [Bundler](http://bundler.io/)** 18 | 19 | If the compiler you are using is not compatible with the above minimum versions, it will not compile correctly. 20 | 21 | **BE AWARE that CodeKit and most other GUI compilers *do not support Bundler* and are therefore NOT RECOMMENDED** 22 | 23 | ## Installation 24 | 25 | The preferred way to install and use Sassy Maps is through Bundler. To do so, add the following to your Gemfile (Sass provided to ensure the correct versions are used): 26 | 27 | ```ruby 28 | gem "sass", "~>3.3.0" 29 | gem "sassy-maps", "~>0.3.2" 30 | ``` 31 | 32 | Once you have done so, run `bundle install` to install your dependencies, and remember to run all of your compilation through `bundle exec`. 33 | 34 | You can also install Sassy Maps through [Bower](http://bower.io/) as it has no Ruby requirements. To do so, run the following command: 35 | 36 | ```bash 37 | bower install sassy-maps --save-dev 38 | ``` 39 | 40 | *BE AWARE* that while you can install Sassy Maps through Bower, Sassy Maps still only works with Sass compilers that have full compatibility with Sass 3.3.x and greater. If using Compass, you are also going to need to add the folder using Compass's [`add_import_path`](http://compass-style.org/help/tutorials/configuration-reference/) config option instead of the standard `require`. 41 | 42 | ## Using Sassy Maps 43 | 44 | If you already have a project made and you'd like to use Sassy Maps with it, add the following to your `config.rb` file: 45 | 46 | `require 'sassy-maps'` 47 | 48 | Then, add the following to your Sass file towards the top: 49 | 50 | `@import "sassy-maps";` 51 | 52 | ## Functions 53 | 54 | * `map-get-deep($map, $keys...)` - Returns the value of the final item in `$keys` from the map `$map`. Will return `null` if no value is present and will warn of errors in search (such as if an intermediate depth is `null` or not a `map`). 55 | * `map-set($map, $key, $value)` - Returns a map that has the `$key` in `$map` set to the to given `$value`. 56 | * `map-set-deep($map, $keys, $value)` - Returns a map that has the `$key` in `$map` set to the given `$value`. `$key` should be single-depth list of keys, for instance `map-set-deep($map, ('foo' 'bar' 'baz'), "Hello World")`. 57 | * `map-to-string($map)` - Returns a string representation of the given `$map`. 58 | 59 | 60 | ## Optional Modules 61 | 62 | Sassy Maps comes with optional modules that extend upon the base functionality of Sassy Maps to provide additional map-based functionality. The following are optional modules available with Sassy Maps: 63 | 64 | * [Memo](#memo) 65 | 66 | ### Memo 67 | 68 | Memo is a [Memoization](http://en.wikipedia.org/wiki/Memoization) framework for Sass. Designed with framework developers in mind, it makes it easy to store and retrieve the output of functions quickly and easily without needing to run the function again. For complex functions this should greatly speed up overall compilation time for repeat function calls with identical input. 69 | 70 | To use Memo, simply include `@import "memo";` and you're good to go (normal [Sassy Maps installation](#installation) still applies). Memo comes with two functions: 71 | 72 | * `memo-set($module, $key, $value)` - Sets a memoization `$key` to the given `$value` for the prescribed `$module` (framework). The function will return `true`. Also available as a mixin (`@include memo-set($module, $key, $value)`). 73 | * `memo-get($module, $key)` - Returns the value of the memoization `$key` for the prescribed `$module`. 74 | 75 | Using Memo is fairly simple, just check to see if there is a memoization value for your key (and it's not `null`); if there is, return that, if not, run through the function, set the memoization, and return that result. The following example stores whether Memo is available in a variable and uses the function name as the memoization module, but if building a framework such as [Breakpoint](http://github.com/team-sass/breakpoint), that framework should be the name of the memoization module. 76 | 77 | ```scss 78 | $Memo-Exists: function-exists(memo-get) and function-exists(memo-set); 79 | 80 | @function percentage($target, $context) { 81 | @if $Memo-Exists { 82 | $result: memo-get(percentage, $target $context); 83 | 84 | @if $result != null { 85 | @return $result; 86 | } 87 | } 88 | 89 | $result: $target / $context * 100%; 90 | 91 | @if $Memo-Exists { 92 | $holder: memo-set(percentage, $target $context, $result); 93 | } 94 | 95 | @return $result; 96 | } 97 | 98 | $half: percentage(20px, 40px); // No memoization exists, will run through the function 99 | $half-again: percentage(20px, 40px); // Memoization exists, will just return that result 100 | ``` 101 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rake/testtask' 3 | 4 | Rake::TestTask.new do |t| 5 | t.libs = ['lib','tests'] 6 | t.test_files = Dir.glob('tests/**/*_test.rb').sort 7 | t.verbose = false 8 | t.options = "- --tapy | tapout navigator --require tapout/reporters/navigator_reporter" 9 | end 10 | 11 | task :default => :test 12 | 13 | desc 'Re-render all test Sass files to new control files.' 14 | task 'render' => ['environment'] do 15 | require File.expand_path('../tests/navigator', __FILE__) 16 | Navigator::Renderer.render_controls 17 | end 18 | 19 | task 'environment' do 20 | lib = File.expand_path('../lib', __FILE__) 21 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 22 | require 'sassy-maps' 23 | end 24 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sassy-maps", 3 | "version": "0.4.0", 4 | "authors": [ 5 | "Sam Richard " 6 | ], 7 | "main": "sass/_sassy-maps.scss", 8 | "devDependencies": {}, 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "components", 14 | "tests", 15 | "Gemfile", 16 | "images", 17 | "fonts", 18 | "js", 19 | "package.json", 20 | "config.*", 21 | "*.gem" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /examples/example.scss: -------------------------------------------------------------------------------- 1 | @import "sassy-maps"; 2 | 3 | $map: (400: "value", "key2": "value2", "key3": ("foo": "bar", "baz": ("waldo": "qux"), 'steve': 'perry'), "bob": "george"); 4 | // @debug $map; 5 | $string: inspect($map); 6 | // @debug type-of($string); 7 | // @debug $string; 8 | 9 | $map: map-set($map, 'key2', 'value3'); 10 | // @debug $map; 11 | 12 | @debug map-get-deep($map, 'key4', 'nine', 'eight'); 13 | 14 | @debug $map; 15 | $map: map-set-deep($map, ('key3' 'baz' 'waldo'), "molly"); 16 | $map: map-set-deep($map, ('key3' 'baz' 'qux'), 'quark'); 17 | $map: map-set-deep($map, ('key3' 'jedi' 'vader'), 'red'); 18 | $map: map-set-deep($map, ('key3' 'jedi' 'luke'), 'green'); 19 | $map: map-set-deep($map, ('key4' 'nine' 'eight'), 'seven'); 20 | @debug $map; 21 | 22 | @import "memo"; 23 | 24 | @include memo-set(singularity, 2 .75 split, 50%); 25 | @include memo-set(singularity, 2 .5 split, 75%); 26 | 27 | @import "memo"; 28 | 29 | $memo-exists: function-exists(memo-get) and function-exists(memo-set); 30 | 31 | @function percentage($target, $context) { 32 | $result: memo-get(percentage, $target $context); 33 | 34 | @if not ($memo-exists and $result != null) { 35 | $result: $target / $context * 100%; 36 | $holder: memo-set(percentage, $target $context, $result); 37 | } 38 | 39 | @return $result; 40 | } 41 | 42 | $half: percentage(20px, 40px); 43 | $half-again: percentage(20px, 40px); 44 | 45 | @debug $Memoization-Table; 46 | @debug memo-get(singularity, 2 .75 split); -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Example 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Sassy Maps In Action

17 | 18 | 19 | 21 | 23 | 24 | -------------------------------------------------------------------------------- /lib/sassy-maps.rb: -------------------------------------------------------------------------------- 1 | require 'sassy-maps/version' 2 | require 'sass' 3 | 4 | sassy_maps_path = File.expand_path('../../sass', __FILE__) 5 | 6 | if (defined? Compass) 7 | Compass::Frameworks.register( 8 | 'sassy-maps', 9 | :stylesheets_directory => sassy_maps_path 10 | ) 11 | else 12 | ENV["SASS_PATH"] = [ENV["SASS_PATH"], sassy_maps_path].compact.join(File::PATH_SEPARATOR) 13 | end 14 | 15 | module SassyMaps 16 | end 17 | -------------------------------------------------------------------------------- /lib/sassy-maps/version.rb: -------------------------------------------------------------------------------- 1 | module SassyMaps 2 | VERSION = "0.4.0" 3 | DATE = "2014-06-03" 4 | end 5 | -------------------------------------------------------------------------------- /lib/tapout/reporters/navigator_reporter.rb: -------------------------------------------------------------------------------- 1 | require 'tapout' 2 | require 'tapout/reporters' 3 | 4 | module Tapout 5 | module Reporters 6 | class NavigatorReporter < RuntimeReporter 7 | 8 | def backtrace_snippets(test) 9 | '' 10 | end 11 | 12 | end 13 | end 14 | end 15 | 16 | Tapout::Reporters.index['navigator'] = Tapout::Reporters::NavigatorReporter 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sassy-maps", 3 | "version": "0.3.2", 4 | "dependencies": {}, 5 | "engines": { 6 | "node": ">=0.8.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sache.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sassy-maps", 3 | "description": "Sassy Maps adds a variety of functions aimed at helping you work with Sass 3.3 maps much easier.", 4 | "tags": ["maps", "dry", "performance", "code-quality", "memo", "memoization"] 5 | } 6 | -------------------------------------------------------------------------------- /sass/_memo.scss: -------------------------------------------------------------------------------- 1 | @import "sassy-maps"; 2 | 3 | $Memoization-Table: () !default; 4 | 5 | ////////////////////////////// 6 | // Memoization Set 7 | ////////////////////////////// 8 | @function memo-set($module, $key, $value) { 9 | $module: "#{$module}"; 10 | $key: "#{$key}"; 11 | 12 | $Memoization-Table: map-set-deep($Memoization-Table, ($module $key), $value) !global; 13 | 14 | @return true; 15 | } 16 | 17 | @mixin memo-set($module, $key, $value) { 18 | $holder: memo-set($module, $key, $value); 19 | } 20 | 21 | ////////////////////////////// 22 | // Memoization Get 23 | ////////////////////////////// 24 | @function memo-get($module, $key) { 25 | $module: "#{$module}"; 26 | $key: "#{$key}"; 27 | $private-sassy-maps-suppress-warnings: true !global; 28 | $result: map-get-deep($Memoization-Table, $module, $key); 29 | $private-sassy-maps-suppress-warnings: false !global; 30 | @return $result; 31 | } 32 | -------------------------------------------------------------------------------- /sass/_sassy-maps.scss: -------------------------------------------------------------------------------- 1 | ////////////////////////////// 2 | // To String 3 | ////////////////////////////// 4 | @import "sassy-maps/map-to-string"; 5 | 6 | ////////////////////////////// 7 | // Getting Helpers 8 | ////////////////////////////// 9 | @import "sassy-maps/map-get"; 10 | 11 | ////////////////////////////// 12 | // Setting Helpers 13 | ////////////////////////////// 14 | @import "sassy-maps/map-set"; -------------------------------------------------------------------------------- /sass/sassy-maps/_map-get.scss: -------------------------------------------------------------------------------- 1 | $private-sassy-maps-suppress-warnings: false !default; 2 | 3 | ////////////////////////////// 4 | // Map Get Deep 5 | // 6 | // Given a map and a list of keys, find the value at the given key 7 | ////////////////////////////// 8 | @function map-get-deep($map, $keys...) { 9 | @if length($keys) == 1 { 10 | $keys: nth($keys, 1); 11 | } 12 | $warn: "#{nth($keys, 1)}"; 13 | $length: length($keys); 14 | $get: map-get($map, nth($keys, 1)); 15 | 16 | @if $length > 1 { 17 | @for $i from 2 through $length { 18 | @if $get != null and type-of($get) == 'map' { 19 | $warn: $warn + "->#{nth($keys, $i)}"; 20 | $get: map-get($get, nth($keys, $i)); 21 | 22 | @if $get == null { 23 | @return map-get-deep-warning($warn, $get, nth($keys, $i)); 24 | } 25 | } 26 | @else { 27 | @return map-get-deep-warning($warn, $get, nth($keys, $i)); 28 | } 29 | } 30 | } 31 | 32 | @return $get; 33 | } 34 | 35 | ////////////////////////////// 36 | // Map Get Deep Warning 37 | // 38 | // Displays a warning if the retrieved value is `null` 39 | ////////////////////////////// 40 | @function map-get-deep-warning($warn, $get, $key) { 41 | @if not $private-sassy-maps-suppress-warnings { 42 | @if $get == null { 43 | @warn "Map has no value for key search `#{$warn}`"; 44 | } 45 | @else if type-of($get) != 'map' { 46 | @warn "Non-map value found for key search `#{$warn}`, cannot search for key `#{$key}`"; 47 | } 48 | } 49 | @return null; 50 | } 51 | -------------------------------------------------------------------------------- /sass/sassy-maps/_map-set.scss: -------------------------------------------------------------------------------- 1 | @function get-keys($keys, $counter) { 2 | $return: (); 3 | @for $i from 1 to $counter { 4 | $return: append($return, nth($keys, $i)); 5 | } 6 | @return $return; 7 | } 8 | 9 | @function map-set($map, $key, $value) { 10 | @return map-merge($map, ($key: $value)); 11 | } 12 | 13 | @function map-set-deep($map, $keys, $value) { 14 | $private-sassy-maps-suppress-warnings: true !global; 15 | $length: length($keys); 16 | $get-keys: (); 17 | $map-level: (); 18 | 19 | 20 | @if $length > 1 { 21 | $get-keys: get-keys($keys, $length); 22 | $map-level: map-get-deep($map, $get-keys); 23 | } 24 | $merge: (nth($keys, $length): $value); 25 | @if $map-level { 26 | $merge: map-merge($map-level, $merge); 27 | } 28 | @for $i from ($length * -1 + 1) through -1 { 29 | $j: abs($i); 30 | $key: nth($keys, $j); 31 | // 32 | // @debug $get-keys; 33 | @if $j > 1 { 34 | $get-keys: get-keys($keys, $j); 35 | $map-level: map-get-deep($map, $get-keys); 36 | @if $map-level { 37 | $merge: map-merge($map-level, ($key: $merge)); 38 | } 39 | @else { 40 | $merge: ($key: $merge); 41 | } 42 | } 43 | @else { 44 | $merge: ($key: $merge); 45 | } 46 | } 47 | $map: map-merge($map, $merge); 48 | 49 | $private-sassy-maps-suppress-warnings: false !global; 50 | @return $map; 51 | } 52 | -------------------------------------------------------------------------------- /sass/sassy-maps/_map-to-string.scss: -------------------------------------------------------------------------------- 1 | @function map-to-string($map) { 2 | @return inspect($map); 3 | } -------------------------------------------------------------------------------- /sassy-maps.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path('../lib', __FILE__) 2 | require 'sassy-maps/version' 3 | 4 | Gem::Specification.new do |s| 5 | s.name = 'sassy-maps' 6 | s.version = SassyMaps::VERSION 7 | s.platform = Gem::Platform::RUBY 8 | s.authors = ['Sam Richard'] 9 | s.email = ['sam@snug.ug'] 10 | s.license = 'MIT' 11 | s.homepage = 'https://github.com/Snugug/Sassy-Maps' 12 | s.summary = 'Map helper functions for Sass 3.3 Maps' 13 | s.description = 'Map helper functions for Sass 3.3 Maps including get-deep and set/set-deep' 14 | s.rubyforge_project = 'sassy-maps' 15 | s.files = ['README.md'] 16 | s.files += Dir.glob("lib/**/*.*") 17 | s.files += Dir.glob("sass/**/*.*") 18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 19 | s.require_paths = ["lib"] 20 | s.add_dependency('sass', '~> 3.3') 21 | s.add_development_dependency('bundler') 22 | s.add_development_dependency('rake') 23 | s.add_development_dependency('minitest') 24 | s.add_development_dependency('minitap') 25 | s.add_development_dependency('tapout') 26 | s.add_development_dependency('term-ansicolor') 27 | s.add_development_dependency('colorize') 28 | end 29 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | Place your test SCSS files in here. These files will be compiled into an `output` directory and those output files will be compared against our `controls`. If there is a difference, you will get a a diff of your `output` vs your `controls`. 2 | 3 | Each individual piece you're testing, from a function to a mixin to general usage, should be easy to identify what it is from the output CSS. The general convention to follow is something like the following: 4 | 5 | ```scss 6 | ////////////////////////////// 7 | // Functions 8 | ////////////////////////////// 9 | /** 10 | * Function Name Function 11 | **/ 12 | .function-name { 13 | /* Each permutation of input/output that is possible should get a CSS comment */ 14 | /* No Defaulted Input */ 15 | _test: "function-name(input)"; 16 | _result: function-name(input); 17 | /* Defaulted Input */ 18 | _test: "function-name(input, input2)"; 19 | _result: function-name(input, input2); 20 | } 21 | ////////////////////////////// 22 | // Mixins 23 | ////////////////////////////// 24 | /** 25 | * Mixin Name 26 | **/ 27 | .mixin-name { 28 | /* Each permutation of input/output that is possible should get a CSS comment */ 29 | /* No Defaulted Input */ 30 | _test: "mixin-name(input)" 31 | @include mixin-name(input); 32 | /* Defaulted Input */ 33 | _test: "mixin-name(input, input2)" 34 | @include mixin-name(input, input2); 35 | } 36 | ``` 37 | 38 | You want representative examples of input/output. Don't be afraid to add tests as you need them, the more tests the better! It's very likely that, for complex systems, the total number of tests you write (and their related control) will outweigh your actual Compass framework. 39 | 40 | You're also encouraged to have multiple files in multiple folders to break down what you're testing into chunks in order to test small, discrete pieces. 41 | -------------------------------------------------------------------------------- /tests/controls/01-map-get.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Map Get Deep 3 | **/ 4 | .map { 5 | _map: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green))))); 6 | } 7 | 8 | .map-get-deep--single-valid { 9 | _test: "map-get-deep($map, 'foo')"; 10 | _result: "bar"; 11 | } 12 | .map-get-deep--single-invalid { 13 | _test: "map-get-deep($map, 'bar')"; 14 | _result: null; 15 | } 16 | .map-get-deep--first-valid { 17 | _test: "map-get-deep($map, 'baz', 'jedi')"; 18 | _result: ("vader": red, "luke": green); 19 | } 20 | .map-get-deep--first-invalid { 21 | _test: "map-get-deep($map, 'baz', 'waldo')"; 22 | _result: null; 23 | } 24 | .map-get-deep--second-valid { 25 | _test: "map-get-deep($map, 'baz', 'jedi', 'luke')"; 26 | _result: green; 27 | } 28 | .map-get-deep--second-invalid { 29 | _test: "map-get-deep($map, 'baz', 'jedi', 'chewie')"; 30 | _result: null; 31 | } 32 | .map-get-deep--second-invalid { 33 | _test: "map-get-deep($map, 'baz', 'waldo', 'luke')"; 34 | _result: null; 35 | } 36 | -------------------------------------------------------------------------------- /tests/controls/02-map-set.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Map Set 3 | **/ 4 | .map-set--existing { 5 | _map: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green))))); 6 | _test: "map-set($map, 'foo', 400)"; 7 | _result: ("foo": 400, "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green))))); 8 | } 9 | .map-set--new { 10 | _map: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green))))); 11 | _test: "map-set($map, 'planets', ('Earth', 'Mars'))"; 12 | _result: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green)))), "planets": ("Earth", "Mars")); 13 | } 14 | 15 | /** 16 | * Map Set Deep 17 | **/ 18 | .map-set-deep--first-existing { 19 | _map: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green))))); 20 | _test: "map-set-deep($map, 'baz' 'jedi', ('windu': purple))"; 21 | _result: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("windu": purple))))); 22 | } 23 | .map-set-deep--first-new { 24 | _map: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green))))); 25 | _test: "map-set-deep($map, 'baz' 'Carmen', 'Sandiego')"; 26 | _result: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green)), "Carmen": "Sandiego"))); 27 | } 28 | .map-set-deep--second-existing { 29 | _map: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green))))); 30 | _test: "map-set-deep($map, 'baz' 'jedi' 'luke', ('color': green))"; 31 | _result: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": (("color": green))))))); 32 | } 33 | .map-set-deep--second-new { 34 | _map: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green))))); 35 | _test: "map-set-deep($map, 'baz' 'jedi' 'windu', purple)"; 36 | _result: ("foo": "bar", "baz": (("qux": "waldo", "jedi": (("vader": red, "luke": green, "windu": purple))))); 37 | } 38 | -------------------------------------------------------------------------------- /tests/controls/03-memo.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Memo Mixin 3 | **/ 4 | .memo--mixin--new { 5 | _memo-table: (); 6 | _test: "@include memo-set(test, foo, bar)"; 7 | _get: "memo-get(test, foo)"; 8 | _result: bar; 9 | _memo-table: ("test": (("foo": bar))); 10 | } 11 | .memo--mixin--existing { 12 | _memo-table: ("test": (("foo": bar))); 13 | _test: "@include memo-set(test, foo, qux)"; 14 | _get: "memo-get(test, foo)"; 15 | _result: qux; 16 | _memo-table: ("test": (("foo": qux))); 17 | } 18 | 19 | .memo--function--new { 20 | _memo-table: ("test": (("foo": qux))); 21 | _test: "$holder: memo-set(test, baz, waldo)"; 22 | _get: "memo-get(test, baz)"; 23 | _result: waldo; 24 | _memo-table: ("test": (("foo": qux, "baz": waldo))); 25 | } 26 | .memo--function--existing { 27 | _memo-table: ("test": (("foo": qux, "baz": waldo))); 28 | _test: "$holder: memo-set(test, baz, ('jedi': ('vader': red, 'luke': green)))"; 29 | _get: "memo-get(test, baz)"; 30 | _result: ("jedi": (("vader": red, "luke": green))); 31 | _memo-table: ("test": (("foo": qux, "baz": (("jedi": (("vader": red, "luke": green))))))); 32 | } 33 | -------------------------------------------------------------------------------- /tests/navigator.rb: -------------------------------------------------------------------------------- 1 | require 'colorize' 2 | require 'term/ansicolor' 3 | require 'minitap' 4 | require 'tapout' 5 | Minitest.reporter = Minitap::TapY 6 | 7 | module Navigator 8 | 9 | def self.create_tests(test_klass) 10 | test_names.each do |file_name| 11 | tn = file_name.tr '/', '_' 12 | test_klass.instance_eval do 13 | define_method(:"test_#{tn}") { assert_rendered_file(file_name) } 14 | end 15 | end 16 | end 17 | 18 | def self.test_names 19 | @test_files ||= Dir["#{tests_root}/tests/**/*.{scss}"].map do |file| 20 | file.sub("#{tests_root}/tests/", '').sub(/\.scss/, '') 21 | end 22 | end 23 | 24 | def self.tests_root 25 | @tests_root ||= File.expand_path File.join(File.dirname(__FILE__)) 26 | end 27 | 28 | def self.tests_output_dir 29 | @tests_output_dir ||= File.join Navigator.tests_root, 'output' 30 | end 31 | 32 | def self.tests_output_dir_clean! 33 | Dir["#{Navigator.tests_output_dir}/**/*.{diff}"].each { |f| File.delete(f) } 34 | end 35 | 36 | def self.tests_sass_file(file_name) 37 | File.join Navigator.tests_root, 'tests', "#{file_name}.scss" 38 | end 39 | 40 | def self.tests_output(file_name) 41 | file = File.join Navigator.tests_root, 'output', "#{file_name}.css.diff" 42 | # file = File.join Navigator.tests_output_dir, "#{file_name}.css.diff" 43 | FileUtils.mkdir_p(File.dirname(file)) 44 | file 45 | end 46 | 47 | def self.tests_control_file(file_name) 48 | File.join Navigator.tests_root, 'controls', "#{file_name}.css" 49 | end 50 | 51 | 52 | class Renderer 53 | 54 | attr_reader :file_name 55 | 56 | class << self 57 | 58 | def render_sass_file(file_name) 59 | self.new(file_name).render 60 | end 61 | 62 | def render_controls 63 | Navigator.test_names.each do |file_name| 64 | control_css = render_sass_file(file_name) 65 | control_file = Navigator.tests_control_file(file_name) 66 | File.open(control_file,'w') { |f| f.write(control_css) } 67 | puts "Rendered->#{control_file}".colorize(:light_cyan) 68 | end 69 | end 70 | 71 | end 72 | 73 | def initialize(file_name) 74 | @file_name = file_name 75 | end 76 | 77 | def render 78 | options = {:syntax => :scss, :cache => false, :style => :expanded} 79 | template = File.read(sass_file) 80 | Sass::Engine.new(template, options).render 81 | end 82 | 83 | def sass_file 84 | Navigator.tests_sass_file(file_name) 85 | end 86 | 87 | end 88 | 89 | module Assertions 90 | 91 | def self.included(base) 92 | Navigator.create_tests(base) 93 | Navigator.tests_output_dir_clean! 94 | end 95 | 96 | private 97 | 98 | def assert_rendered_file(file_name) 99 | rendered_sass = Navigator::Renderer.render_sass_file(file_name) 100 | control_sass = File.read(Navigator.tests_control_file(file_name)) 101 | flunk_sass(file_name, control_sass, rendered_sass) if control_sass != rendered_sass 102 | end 103 | 104 | def flunk_sass(file_name, control_sass, rendered_sass) 105 | diff_file = Navigator.tests_output(file_name) 106 | diff_data = diff(control_sass, rendered_sass) 107 | File.open(diff_file,'w') { |f| f.write(diff_data) } 108 | msg = "" 109 | msg << "Control->Compiled diff output to".colorize(:light_yellow) 110 | msg << " #{diff_file}\n".colorize(:light_cyan) 111 | msg << `cdiff #{diff_file}`.chomp 112 | flunk(msg) 113 | end 114 | 115 | end 116 | 117 | end 118 | -------------------------------------------------------------------------------- /tests/output/.gitkeey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/at-import/Sassy-Maps/36008c57759562654caf938a93c874a9dbdd9d5e/tests/output/.gitkeey -------------------------------------------------------------------------------- /tests/sassy_maps_test.rb: -------------------------------------------------------------------------------- 1 | require 'bundler' ; Bundler.require :development, :test 2 | require 'sassy-maps' 3 | require 'navigator' 4 | require 'minitest/autorun' 5 | 6 | class SassyMapsTest < Minitest::Test 7 | 8 | include Navigator::Assertions 9 | 10 | 11 | end 12 | -------------------------------------------------------------------------------- /tests/tests/01-map-get.scss: -------------------------------------------------------------------------------- 1 | $private-sassy-maps-suppress-warnings: true; 2 | @import "sassy-maps"; 3 | 4 | $map: ( 5 | "foo": "bar", 6 | "baz": ( 7 | "qux": "waldo", 8 | "jedi": ( 9 | "vader": red, 10 | "luke": green 11 | ) 12 | ) 13 | ); 14 | 15 | /** 16 | * Map Get Deep 17 | **/ 18 | .map { 19 | _map: inspect($map); 20 | } 21 | 22 | .map-get-deep { 23 | 24 | &--single { 25 | &-valid { 26 | _test: "map-get-deep($map, 'foo')"; 27 | _result: inspect(map-get-deep($map, 'foo')); 28 | } 29 | 30 | &-invalid { 31 | _test: "map-get-deep($map, 'bar')"; 32 | _result: inspect(map-get-deep($map, 'bar')); 33 | } 34 | } 35 | 36 | &--first { 37 | &-valid { 38 | _test: "map-get-deep($map, 'baz', 'jedi')"; 39 | _result: inspect(map-get-deep($map, 'baz', 'jedi')); 40 | } 41 | &-invalid { 42 | _test: "map-get-deep($map, 'baz', 'waldo')"; 43 | _result: inspect(map-get-deep($map, 'baz', 'waldo')); 44 | } 45 | } 46 | 47 | &--second { 48 | &-valid { 49 | _test: "map-get-deep($map, 'baz', 'jedi', 'luke')"; 50 | _result: inspect(map-get-deep($map, 'baz', 'jedi', 'luke')); 51 | } 52 | &-invalid { 53 | _test: "map-get-deep($map, 'baz', 'jedi', 'chewie')"; 54 | _result: inspect(map-get-deep($map, 'baz', 'jedi', 'chewie')); 55 | } 56 | &-invalid { 57 | _test: "map-get-deep($map, 'baz', 'waldo', 'luke')"; 58 | _result: inspect(map-get-deep($map, 'baz', 'jedi', 'chewie')); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/tests/02-map-set.scss: -------------------------------------------------------------------------------- 1 | $private-sassy-maps-suppress-warnings: true; 2 | @import "sassy-maps"; 3 | 4 | $map: ( 5 | "foo": "bar", 6 | "baz": ( 7 | "qux": "waldo", 8 | "jedi": ( 9 | "vader": red, 10 | "luke": green 11 | ) 12 | ) 13 | ); 14 | 15 | /** 16 | * Map Set 17 | **/ 18 | .map-set { 19 | 20 | &--existing { 21 | _map: inspect($map); 22 | _test: "map-set($map, 'foo', 400)"; 23 | _result: inspect(map-set($map, 'foo', 400)); 24 | } 25 | 26 | &--new { 27 | _map: inspect($map); 28 | _test: "map-set($map, 'planets', ('Earth', 'Mars'))"; 29 | _result: inspect(map-set($map, 'planets', ('Earth', 'Mars'))); 30 | } 31 | 32 | } 33 | 34 | /** 35 | * Map Set Deep 36 | **/ 37 | .map-set-deep { 38 | 39 | &--first { 40 | &-existing { 41 | _map: inspect($map); 42 | _test: "map-set-deep($map, 'baz' 'jedi', ('windu': purple))"; 43 | _result: inspect(map-set-deep($map, 'baz' 'jedi', ('windu': purple))); 44 | } 45 | &-new { 46 | _map: inspect($map); 47 | _test: "map-set-deep($map, 'baz' 'Carmen', 'Sandiego')"; 48 | _result: inspect(map-set-deep($map, 'baz' 'Carmen', 'Sandiego')); 49 | } 50 | } 51 | 52 | &--second { 53 | &-existing { 54 | _map: inspect($map); 55 | _test: "map-set-deep($map, 'baz' 'jedi' 'luke', ('color': green))"; 56 | _result: inspect(map-set-deep($map, 'baz' 'jedi' 'luke', ('color': green))); 57 | } 58 | &-new { 59 | _map: inspect($map); 60 | _test: "map-set-deep($map, 'baz' 'jedi' 'windu', purple)"; 61 | _result: inspect(map-set-deep($map, 'baz' 'jedi' 'windu', purple)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tests/tests/03-memo.scss: -------------------------------------------------------------------------------- 1 | @import "memo"; 2 | 3 | /** 4 | * Memo Mixin 5 | **/ 6 | .memo--mixin { 7 | &--new { 8 | _memo-table: inspect($Memoization-Table); 9 | _test: "@include memo-set(test, foo, bar)"; 10 | @include memo-set(test, foo, bar); 11 | _get: "memo-get(test, foo)"; 12 | _result: memo-get(test, foo); 13 | _memo-table: inspect($Memoization-Table); 14 | } 15 | 16 | &--existing { 17 | _memo-table: inspect($Memoization-Table); 18 | _test: "@include memo-set(test, foo, qux)"; 19 | @include memo-set(test, foo, qux); 20 | _get: "memo-get(test, foo)"; 21 | _result: memo-get(test, foo); 22 | _memo-table: inspect($Memoization-Table); 23 | } 24 | } 25 | 26 | .memo--function { 27 | &--new { 28 | _memo-table: inspect($Memoization-Table); 29 | _test: "$holder: memo-set(test, baz, waldo)"; 30 | $holder: memo-set(test, baz, waldo); 31 | _get: "memo-get(test, baz)"; 32 | _result: memo-get(test, baz); 33 | _memo-table: inspect($Memoization-Table); 34 | } 35 | 36 | &--existing { 37 | _memo-table: inspect($Memoization-Table); 38 | _test: "$holder: memo-set(test, baz, ('jedi': ('vader': red, 'luke': green)))"; 39 | $holder: memo-set(test, baz, ('jedi': ('vader': red, 'luke': green))); 40 | _get: "memo-get(test, baz)"; 41 | _result: inspect(memo-get(test, baz)); 42 | _memo-table: inspect($Memoization-Table); 43 | } 44 | } 45 | --------------------------------------------------------------------------------