diff --git a/README.md b/README.md index 40f5430..404da71 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Author](http://img.shields.io/badge/author-@@anolilab-blue.svg?style=flat-square)](https://twitter.com/@anolilab) [![npm](https://img.shields.io/npm/v/sass-config-manager.svg?style=flat-square)](https://www.npmjs.com/package/sass-config-manager) -[![David](https://img.shields.io/david/growcss/sass-config-manager.svg?style=flat-square)](https://david-dm.org/growcss/sass-config-manager#info=dependencies&view=table) +[![devDependency Status](https://david-dm.org/growcss/sass-config-manager/dev-status.svg?style=flat-square)](https://david-dm.org/growcss/sass-config-manager#info=devDependencies) [![npm](https://img.shields.io/npm/v/npm.svg?style=flat-square)](https://www.npmjs.com/package/sass-config-manager) [![GitHub release](https://img.shields.io/github/release/qubyte/rubidium.svg?style=flat-square)](https://github.com/growcss/sass-config-manager/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) diff --git a/bower.json b/bower.json index b4a8808..3f5ff5f 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "growcss-sass-config-manager", - "version": "1.0.0", + "version": "2.0.0", "homepage": "http://growcss.com/", "authors": [ "Daniel Bannert " diff --git a/package.json b/package.json index af8ebfd..bf1205f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "growcss-sass-config-manager", - "version": "1.0.0", + "version": "2.0.0", "description": "A dot-syntax configuration (Map) library for Sass (mixin / function).", "author": { "name": "Daniel Bannert", diff --git a/src/scss/config-manager.scss b/src/scss/config-manager.scss index 44afa0c..d236a10 100644 --- a/src/scss/config-manager.scss +++ b/src/scss/config-manager.scss @@ -1,5 +1,8 @@ // Here are all the functions, helpers, mixins and variables. @import 'utils/variables'; -@import 'utils/helpers'; -@import 'utils/functions'; + +@import 'helpers/helpers'; + +@import 'functions/functions'; + @import 'utils/mixins'; diff --git a/src/scss/functions/_config-get.scss b/src/scss/functions/_config-get.scss new file mode 100644 index 0000000..09b1df7 --- /dev/null +++ b/src/scss/functions/_config-get.scss @@ -0,0 +1,25 @@ +// Gets a value to/from a configuration path +// +// @function config-get +// +// @param $key {string} The configuration path +// @param $default {bool} Whether the configuration is default +// +// @return {*} The value of the configuration path +@function config-get($key, $default: false) { + @if $default { + @if config-map-has($config-default, $key) { + @return config-map-get($config-default, $key); + } + } @else { + @if config-map-has($config-attr, $key) { + @return config-map-get($config-attr, $key); + } @else if config-map-has($config-default, $key) { + @return config-map-get($config-default, $key); + } + } + + @warn 'The key "#{$key}" doesn\'t exist.'; + + @return null; +} diff --git a/src/scss/functions/_config-has.scss b/src/scss/functions/_config-has.scss new file mode 100644 index 0000000..5bec3a7 --- /dev/null +++ b/src/scss/functions/_config-has.scss @@ -0,0 +1,15 @@ +// Returns whether a configuration path exists +// +// @function config-has +// +// @param $key {string} The configuration path +// +// @return {bool} True if the configuration path has a value, +// otherwise false +@function config-has($key) { + @if config-map-has($config-attr, $key) or config-map-has($config-default, $key) { + @return true; + } + + @return false; +} diff --git a/src/scss/functions/_config-set.scss b/src/scss/functions/_config-set.scss new file mode 100644 index 0000000..f16d385 --- /dev/null +++ b/src/scss/functions/_config-set.scss @@ -0,0 +1,22 @@ +// Sets a value to/from a configuration path +// +// @function config +// +// @param $key {string} The configuration key +// @param $value {*} The value to set +// @param $default {bool} Whether the configuration is default +// +// @return {*} The value of the configuration path +@function config-set($key, $value, $default: false) { + @if $default { + @if config-map-has($config-default, $key) { + $value: config-map-get($config-default, $key); + } @else { + $config-default: config-map-set($config-default, $key, $value) !global; + } + } @else { + $config-attr: config-map-set($config-attr, $key, $value) !global; + } + + @return $value; +} diff --git a/src/scss/functions/_functions.scss b/src/scss/functions/_functions.scss new file mode 100644 index 0000000..98000ef --- /dev/null +++ b/src/scss/functions/_functions.scss @@ -0,0 +1,3 @@ +@import 'config-set'; +@import 'config-get'; +@import 'config-has'; diff --git a/src/scss/helpers/_helpers.scss b/src/scss/helpers/_helpers.scss new file mode 100644 index 0000000..c520199 --- /dev/null +++ b/src/scss/helpers/_helpers.scss @@ -0,0 +1,3 @@ +@import 'list'; +@import 'str'; +@import 'map'; diff --git a/src/scss/helpers/_list.scss b/src/scss/helpers/_list.scss new file mode 100644 index 0000000..d75ea3f --- /dev/null +++ b/src/scss/helpers/_list.scss @@ -0,0 +1,39 @@ +// Return list-map from `$list` and ensure input list-map is list-of-lists +// +// @function list-map-check +// +// @access public +// +// @param $list {list} +// +// @return {list-map} +@function list-map-check($list) { + @if length($list) == 2 and length(nth($list, 1)) == 1 { + @return append((), $list, 'comma'); + } + + @return $list; +} + +// Extracts a slice of a list +// +// @function list-slice +// +// @access private +// +// @param $list {list} The list to extract +// @param $start {number} The start index to extract +// @param $end {number} The end index to extract +// +// @return {list} The extracted list +@function list-slice($list, $start: 1, $end: length($list)) { + $output: (); + + @if $start >= 1 and $end >= $start { + @for $i from $start through $end { + $output: append($output, nth($list, $i)); + } + } + + @return $output; +} diff --git a/src/scss/helpers/_map.scss b/src/scss/helpers/_map.scss new file mode 100644 index 0000000..5f02439 --- /dev/null +++ b/src/scss/helpers/_map.scss @@ -0,0 +1,130 @@ +// Sets a value to a Map by the map path +// +// @function config-map-set +// +// @access private +// +// @param $map {map} The Map +// @param $path {string} The map path +// @param $value {*|null} The value to set +// +// @return {map...} A new Map +@function config-map-set($map, $path, $value) { + $map: list-map-check($map); + $keys: str-split($path, $config-delimiter); + $length: length($keys); + + $result: (nth($keys, $length): $value); + + @if $length > 1 { + @for $i from 1 through $length - 1 { + $path: ''; + $key: nth($keys, 1); + + $j: $length - $i; + $key: nth($keys, $j); + + $path: str-join(list-slice($keys, 1, $j), $config-delimiter); + + @if config-map-has($map, $path) { + $value: config-map-get($map, $path); + + @if type-of($value) == 'map' { + $result: config-map-merge(($key: $value), ($key: $result)); + } @else { + $result: ($key: $result); + } + } @else { + $result: ($key: $result); + } + } + } + + $map: config-map-merge($map, $result); + + @return $map; +} + +// Gets a value from a Map by the map path +// +// @function config-map-get +// +// @access private +// +// @param $map {map...} The Map +// @param $path {string} The map path +// +// @return {*} The value of the map path +@function config-map-get($map, $path) { + $keys: str-split($path, $config-delimiter); + $value: list-map-check($map); + + @each $key in $keys { + @if type-of($value) != 'map' or not map-has-key($value, $key) { + @warn 'The path "#{$path}" doesn\'t exist.'; + + @return null; + } + + $value: map-get($value, $key); + } + + @return $value; +} + +// Returns whether the key of a map path exists in a Map +// +// @function config-map-has +// +// @access private +// +// @param $map {map...} The Map +// @param $key {string} The map path +// +// @return {bool} True if the map path has a value, +// otherwise false +@function config-map-has($map, $key) { + $keys: str-split($key, $config-delimiter); + $value: list-map-check($map); + + @each $key in $keys { + @if type-of($value) != 'map' or not map-has-key($value, $key) { + @return false; + } + + $value: map-get($value, $key); + } + + @return true; +} + +// Recursively merges one or more maps +// +// @function config-map-merge +// +// @access private +// +// @param $maps {map...} The map(s) to merge +// +// @return {map} The merged map +@function config-map-merge($maps...) { + $result: nth($maps, 1); + + @for $i from 1 through length($maps) - 1 { + @each $key, $value in nth($maps, $i + 1) { + @if type-of($result) != 'map' { + $result: ($key: $value); + } + + @if type-of($value) == 'map' { + $value: config-map-merge(map-get($result, $key), $value); + } + + @if $key != null { + $result: map-merge($result, ($key: $value)); + } + } + } + + @return $result; +} diff --git a/src/scss/helpers/_str.scss b/src/scss/helpers/_str.scss new file mode 100644 index 0000000..a24b43d --- /dev/null +++ b/src/scss/helpers/_str.scss @@ -0,0 +1,66 @@ +// Joins list elements with a string +// +// @function str-join +// +// @access private +// +// @param $list {list} The list to join +// @param $glue {string} The glue string to join list elements +// +// @return {string} The joined string +@function str-join($list, $glue: '') { + $result: ''; + + @if length($list) == 0 { + @return $result; + } + + @if length($list) > 1 { + @for $i from 1 through length($list) - 1 { + $result: $result + nth($list, $i) + $glue; + } + } + + $result: $result + nth($list, length($list)); + + @return $result; +} + +// Splits a string by a delimiter +// +// @function str-split +// +// @access private +// +// @param $string {string} The string to split +// @param $delimiter {string} The boundary string to split the string +// +// @return {list} The splitted list +@function str-split($string, $delimiter: '') { + $result: (); + $length: str-length($string); + + @if str-length($delimiter) == 0 { + @for $i from 1 through $length { + $result: append($result, str-slice($string, $i, $i)); + } + + @return $result; + } + + $break: false; + + @while not $break { + $index: str-index($string, $delimiter); + + @if not $index or $index == 0 { + $break: true; + } @else { + $part: if($index != 1, str-slice($string, 1, $index - 1), ''); + $result: append($result, $part); + $string: str-slice($string, $index + str-length($delimiter)); + } + } + + @return append($result, $string); +} diff --git a/src/scss/utils/_functions.scss b/src/scss/utils/_functions.scss deleted file mode 100644 index b185cd8..0000000 --- a/src/scss/utils/_functions.scss +++ /dev/null @@ -1,96 +0,0 @@ -// Sass Functions -// - - - - - - - - - - - - - - - - - - - - - - - - - - -// Sets a value to/from a configuration path -// -// @function config -// -// @param $key {string} The configuration key -// @param $value {*} The value to set -// @param $default {bool} Whether the configuration is default -// -// @return {*} The value of the configuration path -@function config-set($key, $value, $default: false) { - @if $default { - @if config-map-has($config-default, $key) { - $value: config-map-get($config-default, $key); - } @else { - $config-default: config-map-set($config-default, $key, $value) !global; - } - } @else { - $config-attr: config-map-set($config-attr, $key, $value) !global; - } - - @return $value; -} - -// Gets a value to/from a configuration path -// -// @function config-get -// -// @param $key {string} The configuration path -// @param $default {bool} Whether the configuration is default -// -// @return {*} The value of the configuration path -@function config-get($key, $default: false) { - @if $default { - @if config-map-has($config-default, $key) { - @return config-map-get($config-default, $key); - } - } @else { - @if config-map-has($config-attr, $key) { - @return config-map-get($config-attr, $key); - } @else if config-map-has($config-default, $key) { - @return config-map-get($config-default, $key); - } - } - - @warn 'The key "#{$key}" doesn\'t exist.'; - - @return null; -} - -// Sets or gets a value to/from a configuration path -// -// @function config-bind -// -// @param $key {string} The configuration path -// @param $value {array} The value to set -// @param $default {bool} Whether the configuration is default -// -// @return {*} The value of the configuration path -@function config-bind($key, $values, $default: false) { - @each $value in $values { - @return config-set($key + $config-delimiter + $value, $value, $default); - } - - @return config-get($key, $default); -} - -// Returns whether a configuration path exists -// -// @function config-has -// -// @param $key {string} The configuration path -// -// @return {bool} True if the configuration path has a value, -// otherwise false -@function config-has($key) { - @if config-map-has($config-attr, $key) or config-map-has($config-default, $key) { - @return true; - } - - @return false; -} - -// Removes keys from maps -// -// @function config-remove -// -// @param $key {string} The configuration path -// -// @return {bool} True if the configuration path has a value, -// otherwise false -@function config-remove($key) { - -} diff --git a/src/scss/utils/_helpers.scss b/src/scss/utils/_helpers.scss deleted file mode 100644 index 1fd3da8..0000000 --- a/src/scss/utils/_helpers.scss +++ /dev/null @@ -1,220 +0,0 @@ -// Class & placeholders helpers -// - - - - - - - - - - - - - - - - - - - - - - - - - - -// Splits a string by a delimiter -// -// @function str-split -// -// @private -// -// @param $string {string} The string to split -// @param $delimiter {string} The boundary string to split the string -// -// @return {list} The splitted list -@function str-split($string, $delimiter: '') { - $result: (); - $length: str-length($string); - - @if str-length($delimiter) == 0 { - @for $i from 1 through $length { - $result: append($result, str-slice($string, $i, $i)); - } - - @return $result; - } - - $break: false; - - @while not $break { - $index: str-index($string, $delimiter); - - @if not $index or $index == 0 { - $break: true; - } @else { - $part: if($index != 1, str-slice($string, 1, $index - 1), ''); - $result: append($result, $part); - $string: str-slice($string, $index + str-length($delimiter)); - } - } - - @return append($result, $string); -} - -// Joins list elements with a string -// -// @function str-join -// -// @private -// -// @param $list {list} The list to join -// @param $glue {string} The glue string to join list elements -// -// @return {string} The joined string -@function str-join($list, $glue: '') { - $result: ''; - - @if length($list) == 0 { - @return $result; - } - - @if length($list) > 1 { - @for $i from 1 through length($list) - 1 { - $result: $result + nth($list, $i) + $glue; - } - } - - $result: $result + nth($list, length($list)); - - @return $result; -} - -// Extracts a slice of a list -// -// @function list-slice -// -// @private -// -// @param $list {list} The list to extract -// @param $start {number} The start index to extract -// @param $end {number} The end index to extract -// -// @return {list} The extracted list -@function list-slice($list, $start: 1, $end: length($list)) { - $output: (); - - @for $i from $start through $end { - $output: append($output, nth($list, $i)); - } - - @return $output; -} - -// Returns whether the key of a map path exists in a Map -// -// @function config-map-has -// -// @private -// -// @param $map {map} The Map -// @param $key {string} The map path -// -// @return {bool} True if the map path has a value, -// otherwise false -@function config-map-has($map, $key) { - $keys: str-split($key, $config-delimiter); - $value: $map; - - @each $key in $keys { - @if type-of($value) != 'map' or not map-has-key($value, $key) { - @return false; - } - - $value: map-get($value, $key); - } - - @return true; -} - -// Gets a value from a Map by the map path -// -// @function config-map-get -// -// @private -// -// @param $map {map} The Map -// @param $path {string} The map path -// -// @return {*} The value of the map path -@function config-map-get($map, $path) { - $keys: str-split($path, $config-delimiter); - $value: $map; - - @each $key in $keys { - @if type-of($value) != 'map' or not map-has-key($value, $key) { - @warn 'The path "#{$path}" doesn\'t exist.'; - - @return null; - } - - $value: map-get($value, $key); - } - - @return $value; -} - -// Sets a value to a Map by the map path -// -// @function config-map-set -// -// @private -// -// @param $map {map} The Map -// @param $path {string} The map path -// @param $value {*|null} The value to set -// -// @return {map} A new Map -@function config-map-set($map, $path, $value) { - $keys: str-split($path, $config-delimiter); - $length: length($keys); - - $result: (nth($keys, $length): $value); - - @for $i from 1 through $length - 1 { - $path: ''; - $key: nth($keys, 1); - - @if $length > 1 { - $j: $length - $i; - $key: nth($keys, $j); - - $path: str-join(list-slice($keys, 1, $j), $config-delimiter); - } - - @if config-map-has($map, $path) { - $value: config-map-get($map, $path); - - @if type-of($value) == 'map' { - $result: config-map-merge(($key: $value), ($key: $result)); - } @else { - $result: ($key: $result); - } - } @else { - $result: ($key: $result); - } - } - - $map: config-map-merge($map, $result); - - @return $map; -} - -// Recursively merges one or more maps -// -// @function config-map-merge -// -// @private -// -// @param $maps {map...} The map(s) to merge -// -// @return {map} The merged map -@function config-map-merge($maps...) { - $result: nth($maps, 1); - - @for $i from 1 through length($maps) - 1 { - @each $key, $value in nth($maps, $i + 1) { - @if type-of($result) != 'map' { - $result: ($key: $value); - } - - @if type-of($value) == 'map' { - $value: config-map-merge(map-get($result, $key), $value); - } - - @if $key != null { - $result: map-merge($result, ($key: $value)); - } - } - } - - @return $result; -} diff --git a/src/scss/utils/_mixins.scss b/src/scss/utils/_mixins.scss index 433aa6d..42f64c3 100644 --- a/src/scss/utils/_mixins.scss +++ b/src/scss/utils/_mixins.scss @@ -11,14 +11,3 @@ @mixin config-set($key, $value, $default: false) { $config: config-set($key, $value, $default); } - -// Sets a values to a configuration path -// -// @mixin config-bind -// -// @param $key {string} The configuration key -// @param $value {list} The value to set -// @param $default {bool} Whether the configuration is default -@mixin config-bind($key, $values, $default: false) { - $config: config-bind($key, $values, $default); -} diff --git a/src/scss/utils/_variables.scss b/src/scss/utils/_variables.scss index 7df7914..62bf03a 100644 --- a/src/scss/utils/_variables.scss +++ b/src/scss/utils/_variables.scss @@ -2,13 +2,16 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - // The delimiter of configuration path +// // @private $config-delimiter: '.' !default; // The configuration storage +// // @private $config-attr: () !default; // The default configuration storage +// // @private $config-default: () !default; diff --git a/tests/specs/include.scss b/tests/specs/include.scss index 4558625..bad5a3d 100644 --- a/tests/specs/include.scss +++ b/tests/specs/include.scss @@ -1,4 +1,5 @@ -@import 'src/scss/utils/functions'; -@import 'src/scss/utils/helpers'; -@import 'src/scss/utils/mixins'; + @import 'src/scss/utils/variables'; +@import 'src/scss/helpers/helpers'; +@import 'src/scss/functions/functions'; +@import 'src/scss/utils/mixins'; diff --git a/tests/specs/scss/functions.scss b/tests/specs/scss/functions.scss index 5c0c076..89513ab 100644 --- a/tests/specs/scss/functions.scss +++ b/tests/specs/scss/functions.scss @@ -2,28 +2,72 @@ @include test-module('Functions') { // Testing set functions - @include test('config-set [function]') { + @include test('config-set [function] Set a single key.') { + @include config-set('bar', 'foo'); + @include config-set('color', #fff); + + $expect: ("bar": "foo", "color": #fff); + + @include assert-equal($config-attr, $expect, 'Check if map is correct.'); + } + + // Testing set functions + @include test('config-set [function] Set a single !default key.') { + @include config-set('single-default', 'foo', true); + + $expect: ("single-default": "foo"); + @include assert-equal($config-default, $expect, 'Check if map is correct.'); + } + + // Testing set functions + @include test('config-set [function] Set a dotted key.') { @include config-set('test.bar', 'foo'); @include config-set('test.color', #fff); - $expect: ("test": ("bar": 'foo', "color": #fff)); - @include assert-equal($config-attr, $expect, 'Check if map is correct.'); + $expect: ("bar": 'foo', "color": #fff); + $config: config-get('test'); - $config-attr: (); + @include assert-equal($config, $expect, 'Check if map is correct.'); } // Testing get functions - @include test('config-get [function]') { + @include test('config-get [function] Get a value from key.') { @include config-set('test.color', #000); $get: config-get('test.color'); $expect: #000; @include assert-equal($get, $expect, 'Returns test.color: #000'); + } + // Testing get functions + @include test('config-get [function] Get a map from key.') { $get: config-get('test'); $expect: ("bar": "foo", "color": #000); - @include assert-equal($get, $expect, 'Returns test map'); + @include assert-equal($get, $expect, 'Returns test map.'); + + $config-attr: (); + } + + // Testing get functions + @include test('config-get [function] Get null as value and a warning') { + $get: config-get('warn'); + $expect: null; + + @include assert-equal($get, $expect, 'Returns null.'); + } + + // Testing get functions + @include test('config-has [function] Get true if a key exist and false if not.') { + $get: config-has('test'); + $expect: true; + + @include assert-equal($get, $expect, 'Returns true.'); + + $get: config-has('test-not-found'); + $expect: false; + + @include assert-equal($get, $expect, 'Returns true.'); } }