glug 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/glug +5 -0
- data/lib/glug.rb +317 -0
- metadata +60 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 58209224a893c7908aec8ff0b9f9c85211353842
         | 
| 4 | 
            +
              data.tar.gz: 5b75fb0f41c1276914b0bddd27569be07c352002
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: f3368acdea157ec8e53f69f9361d185d44dc35f97f3bf8ecd1e0edad04ddf16fb491429341cb4045beaaaacfa3a2547334a04704a6c0729608fd2c2746d13a7b
         | 
| 7 | 
            +
              data.tar.gz: 1fbf8f7471e5a029400b8bc0239ccec3d2599867f8844203ea0d85b502299a929dc97ed8758e3e4382c96d9ca066b483f8bf230528837bc6bbae3911ff8d7db9
         | 
    
        data/bin/glug
    ADDED
    
    
    
        data/lib/glug.rb
    ADDED
    
    | @@ -0,0 +1,317 @@ | |
| 1 | 
            +
            require 'json'
         | 
| 2 | 
            +
            require 'neatjson'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Glug # :nodoc:
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            	# -----	Subscriptable
         | 
| 7 | 
            +
            	#       allows us to create conditions with syntax
         | 
| 8 | 
            +
            	#         any[(highway=='primary'),(highway=='trunk')]
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            	class Subscriptable
         | 
| 11 | 
            +
            		def initialize(type)
         | 
| 12 | 
            +
            			@type=type
         | 
| 13 | 
            +
            		end
         | 
| 14 | 
            +
            		def [](*arguments)
         | 
| 15 | 
            +
            			Condition.new.from_list(@type, arguments)
         | 
| 16 | 
            +
            		end
         | 
| 17 | 
            +
            	end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            	# -----	Condition
         | 
| 20 | 
            +
            	#       represents a Mapbox GL filter of the form [operator, key, value] (etc.)
         | 
| 21 | 
            +
            	#       can be merged with other conditions via the & and | operators
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            	class Condition
         | 
| 24 | 
            +
            		attr_accessor :values, :operator
         | 
| 25 | 
            +
            		def initialize
         | 
| 26 | 
            +
            			@values=[]
         | 
| 27 | 
            +
            		end
         | 
| 28 | 
            +
            		def from_key(operator, key, list)
         | 
| 29 | 
            +
            			@operator = operator
         | 
| 30 | 
            +
            			@values = [key].concat(list)
         | 
| 31 | 
            +
            			self
         | 
| 32 | 
            +
            		end
         | 
| 33 | 
            +
            		def from_list(operator, list)
         | 
| 34 | 
            +
            			@operator = operator
         | 
| 35 | 
            +
            			@values = list
         | 
| 36 | 
            +
            			self
         | 
| 37 | 
            +
            		end
         | 
| 38 | 
            +
            		def &(cond); merge(:all,cond) end
         | 
| 39 | 
            +
            		def |(cond); merge(:any,cond) end
         | 
| 40 | 
            +
            		def merge(op,cond)
         | 
| 41 | 
            +
            			if cond.nil?
         | 
| 42 | 
            +
            				self
         | 
| 43 | 
            +
            			elsif @operator==op
         | 
| 44 | 
            +
            				Condition.new.from_list(op, @values + [cond])
         | 
| 45 | 
            +
            #				@values << cond; self
         | 
| 46 | 
            +
            			elsif cond.operator==op
         | 
| 47 | 
            +
            				Condition.new.from_list(op, [self] + cond.values)
         | 
| 48 | 
            +
            #				cond.values << self; cond
         | 
| 49 | 
            +
            			else
         | 
| 50 | 
            +
            				Condition.new.from_list(op, [self, cond])
         | 
| 51 | 
            +
            			end
         | 
| 52 | 
            +
            		end
         | 
| 53 | 
            +
            		# Encode into an array for Mapbox GL JSON (recursive)
         | 
| 54 | 
            +
            		def encode
         | 
| 55 | 
            +
            			[@operator.to_s, *@values.map { |v| v.is_a?(Condition) ? v.encode : v } ]
         | 
| 56 | 
            +
            		end
         | 
| 57 | 
            +
            		def to_s; "<Condition #{@operator} #{@values}>" end
         | 
| 58 | 
            +
            	end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            	# -----	Stylesheet
         | 
| 61 | 
            +
            	#       the main document object
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            	class Stylesheet
         | 
| 64 | 
            +
            		attr_accessor :sources, :kv, :refs
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            		def initialize(&block)
         | 
| 67 | 
            +
            			@sources = {}
         | 
| 68 | 
            +
            			@kv = {}
         | 
| 69 | 
            +
            			@layers = []
         | 
| 70 | 
            +
            			@refs = {}
         | 
| 71 | 
            +
            			instance_eval(&block)
         | 
| 72 | 
            +
            		end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            		# Set a property, e.g. 'bearing 29'
         | 
| 75 | 
            +
            		def method_missing(method_sym, *arguments)
         | 
| 76 | 
            +
            			@kv[method_sym] = arguments[0]
         | 
| 77 | 
            +
            		end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            		# Add a source 
         | 
| 80 | 
            +
            		def source(source_name, opts={})
         | 
| 81 | 
            +
            			@sources[source_name] = opts
         | 
| 82 | 
            +
            		end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            		# Add a layer
         | 
| 85 | 
            +
            		# creates a new Layer object using the block supplied
         | 
| 86 | 
            +
            		def layer(id, opts={}, &block)
         | 
| 87 | 
            +
            			r = Layer.new(self, :id=>id, :kv=>opts)
         | 
| 88 | 
            +
            			@layers << r
         | 
| 89 | 
            +
            			r.instance_eval(&block)
         | 
| 90 | 
            +
            		end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            		# Assemble into Mapbox GL JSON format
         | 
| 93 | 
            +
            		def to_hash
         | 
| 94 | 
            +
            			out = @kv.dup
         | 
| 95 | 
            +
            			out['sources'] = @sources
         | 
| 96 | 
            +
            			out['layers'] = @layers.select { |r| r.write? }.collect { |r| r.to_hash }
         | 
| 97 | 
            +
            			out
         | 
| 98 | 
            +
            		end
         | 
| 99 | 
            +
            		def to_json(*args); JSON.neat_generate(to_hash) end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            		# Setter for Layer to add sublayers
         | 
| 102 | 
            +
            		def _add_layer(layer)
         | 
| 103 | 
            +
            			@layers << layer
         | 
| 104 | 
            +
            		end
         | 
| 105 | 
            +
            	end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            	# -----	OSMKey
         | 
| 108 | 
            +
            	#       enables us to write "population<30000" and have it magically converted into a Condition
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            	class OSMKey
         | 
| 111 | 
            +
            		def initialize(k)
         | 
| 112 | 
            +
            			@k=k
         | 
| 113 | 
            +
            		end
         | 
| 114 | 
            +
            		def is(*args); Condition.new.from_key(:==,@k,args) end
         | 
| 115 | 
            +
            		def ==(*args); Condition.new.from_key(:==,@k,args) end
         | 
| 116 | 
            +
            		def !=(*args); Condition.new.from_key(:!=,@k,args) end
         | 
| 117 | 
            +
            		def  <(*args); Condition.new.from_key(:< ,@k,args) end
         | 
| 118 | 
            +
            		def  >(*args); Condition.new.from_key(:> ,@k,args) end
         | 
| 119 | 
            +
            		def <=(*args); Condition.new.from_key(:<=,@k,args) end
         | 
| 120 | 
            +
            		def >=(*args); Condition.new.from_key(:>=,@k,args) end
         | 
| 121 | 
            +
            		def in(*args); Condition.new.from_key(:in,@k,args) end
         | 
| 122 | 
            +
            		def not_in(*args); Condition.new.from_key(:not_in,@k,args) end
         | 
| 123 | 
            +
            	end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
            	# -----	Layer
         | 
| 126 | 
            +
            	#       a layer in an Mapbox GL style
         | 
| 127 | 
            +
            	#       this is where most of the hard work happens, including 'method_missing' and 'on'
         | 
| 128 | 
            +
            	#         calls to provide the grammar
         | 
| 129 | 
            +
             | 
| 130 | 
            +
            	class Layer
         | 
| 131 | 
            +
             | 
| 132 | 
            +
            		# Mapbox GL properties (as distinct from OSM keys)
         | 
| 133 | 
            +
            		LAYOUT    = [ :visibility, :line_cap, :line_join, :line_miter_limit, :line_round_limit, :symbol_placement, :symbol_spacing, :symbol_avoid_edges, :icon_allow_overlap, :icon_ignore_placement, :icon_optional, :icon_rotation_alignment, :icon_size, :icon_image, :icon_rotate, :icon_padding, :icon_keep_upright, :icon_offset, :text_rotation_alignment, :text_field, :text_font, :text_size, :text_max_width, :text_line_height, :text_letter_spacing, :text_justify, :text_anchor, :text_max_angle, :text_rotate, :text_padding, :text_keep_upright, :text_transform, :text_offset, :text_allow_overlap, :text_ignore_placement, :text_optional ]
         | 
| 134 | 
            +
            		PAINT     = [ :background_color, :background_pattern, :background_opacity, :fill_antialias, :fill_opacity, :fill_color, :fill_outline_color, :fill_translate, :fill_translate_anchor, :fill_pattern, :line_opacity, :line_color, :line_translate, :line_translate_anchor, :line_width, :line_gap_width, :line_blur, :line_dasharray, :line_pattern, :icon_opacity, :icon_color, :icon_halo_color, :icon_halo_width, :icon_halo_blur, :icon_translate, :icon_translate_anchor, :text_opacity, :text_color, :text_halo_color, :text_halo_width, :text_halo_blur, :text_translate, :text_translate_anchor, :raster_opacity, :raster_hue_rotate, :raster_brightness_min, :raster_brightness_max, :raster_saturation, :raster_contrast, :raster_fade_duration, :circle_radius, :circle_color, :circle_blur, :circle_opacity, :circle_translate, :circle_translate_anchor ]
         | 
| 135 | 
            +
            		TOP_LEVEL = [ :metadata, :zoom, :interactive ]
         | 
| 136 | 
            +
            		HIDDEN    = [ :ref, :source, :source_layer, :id, :type, :filter, :layout, :paint ]	# top level, not settable by commands
         | 
| 137 | 
            +
             | 
| 138 | 
            +
            		# Shared properties that can be recalled by using a 'ref' 
         | 
| 139 | 
            +
            		REF_PROPERTIES = ['type', 'source', 'source-layer', 'minzoom', 'maxzoom', 'filter', 'layout']
         | 
| 140 | 
            +
             | 
| 141 | 
            +
            		attr_accessor :kv				# key-value pairs for layout, paint, and top level
         | 
| 142 | 
            +
            		attr_accessor :condition		# filter condition
         | 
| 143 | 
            +
            		attr_accessor :stylesheet		# parent stylesheet object
         | 
| 144 | 
            +
             | 
| 145 | 
            +
            		def initialize(stylesheet, args={})
         | 
| 146 | 
            +
            			@stylesheet = stylesheet
         | 
| 147 | 
            +
            			@condition = args[:condition]
         | 
| 148 | 
            +
            			@kv = args[:kv] || {}
         | 
| 149 | 
            +
            			@kv[:id] = args[:id]
         | 
| 150 | 
            +
            			if args[:zoom] then @kv[:zoom]=args[:zoom] end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            			@type = nil							# auto-detected layer type
         | 
| 153 | 
            +
            			@write = true						# write this layer out, or has it been suppressed?
         | 
| 154 | 
            +
            			@cascade_cond = nil					# are we currently evaluating a cascade directive?
         | 
| 155 | 
            +
            			@cascades = args[:cascades] || []	# cascade list to apply to all subsequent layers
         | 
| 156 | 
            +
            			@uncascaded = nil					# condition to add to non-cascaded layers
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            			@kv[:source] ||= stylesheet.sources.find {|k,v| v[:default] }[0]
         | 
| 159 | 
            +
            			@kv[:source_layer] ||= args[:id]
         | 
| 160 | 
            +
            			@child_num = 0				# incremented sublayer suffix
         | 
| 161 | 
            +
            		end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
            		# Handle all missing 'method' calls
         | 
| 164 | 
            +
            		# If we can match it to a Mapbox GL property, it's an assignment:
         | 
| 165 | 
            +
            		# otherwise it's an OSM key
         | 
| 166 | 
            +
            		def method_missing(method_sym, *arguments)
         | 
| 167 | 
            +
            			if LAYOUT.include?(method_sym) || PAINT.include?(method_sym) || TOP_LEVEL.include?(method_sym)
         | 
| 168 | 
            +
            				v = arguments.length==1 ? arguments[0] : arguments
         | 
| 169 | 
            +
            				if v.is_a?(Proc) then v=v.call(@kv[method_sym]) end
         | 
| 170 | 
            +
            				if @cascade_cond.nil?
         | 
| 171 | 
            +
            					@kv[method_sym] = v
         | 
| 172 | 
            +
            				else
         | 
| 173 | 
            +
            					_add_cascade_condition(method_sym, v)
         | 
| 174 | 
            +
            				end
         | 
| 175 | 
            +
            			else
         | 
| 176 | 
            +
            				return OSMKey.new(method_sym.to_s)
         | 
| 177 | 
            +
            			end
         | 
| 178 | 
            +
            		end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
            		# Add a sublayer with an additional filter
         | 
| 181 | 
            +
            		def on(*args, &block)
         | 
| 182 | 
            +
            			@child_num+=1
         | 
| 183 | 
            +
            			r = Layer.new(@stylesheet,
         | 
| 184 | 
            +
            					:id => "#{@kv[:id]}__#{@child_num}".to_sym,
         | 
| 185 | 
            +
            					:kv => @kv.dup, :cascades => @cascades.dup)
         | 
| 186 | 
            +
             | 
| 187 | 
            +
            			# Set zoom level
         | 
| 188 | 
            +
            			if args[0].is_a?(Range) || args[0].is_a?(Fixnum)
         | 
| 189 | 
            +
            				r.kv[:zoom] = args.shift
         | 
| 190 | 
            +
            			end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
            			# Set condition
         | 
| 193 | 
            +
            			sub_cond = nil
         | 
| 194 | 
            +
            			if args.empty?
         | 
| 195 | 
            +
            				sub_cond = @condition						# just inherit parent layer's condition
         | 
| 196 | 
            +
            			else
         | 
| 197 | 
            +
            				sub_cond = (args.length==1) ? args[0] : Condition.new.from_list(:any,args)
         | 
| 198 | 
            +
            				sub_cond = sub_cond & @condition
         | 
| 199 | 
            +
            			end
         | 
| 200 | 
            +
            			r._set_filter(sub_cond & @uncascaded)
         | 
| 201 | 
            +
            			r.instance_eval(&block)
         | 
| 202 | 
            +
            			@stylesheet._add_layer(r)
         | 
| 203 | 
            +
             | 
| 204 | 
            +
            			# Create cascaded layers
         | 
| 205 | 
            +
            			child_chr='a'
         | 
| 206 | 
            +
            			@cascades.each do |c|
         | 
| 207 | 
            +
            				c_cond, c_kv = c
         | 
| 208 | 
            +
            				l = Layer.new(@stylesheet, :id=>"#{r.kv[:id]}__#{child_chr}", :kv=>r.kv.dup)
         | 
| 209 | 
            +
            				l._set_filter(sub_cond & c_cond)
         | 
| 210 | 
            +
            				l.kv.merge!(c_kv)
         | 
| 211 | 
            +
            				@stylesheet._add_layer(l)
         | 
| 212 | 
            +
            				child_chr.next!
         | 
| 213 | 
            +
            			end
         | 
| 214 | 
            +
            		end
         | 
| 215 | 
            +
            		
         | 
| 216 | 
            +
            		# Add a cascading condition
         | 
| 217 | 
            +
            		def cascade(*args, &block)
         | 
| 218 | 
            +
            			cond = (args.length==1) ? args[0] : Condition.new.from_list(:any,args)
         | 
| 219 | 
            +
            			@cascade_cond = cond
         | 
| 220 | 
            +
            			self.instance_eval(&block)
         | 
| 221 | 
            +
            			@cascade_cond = nil
         | 
| 222 | 
            +
            		end
         | 
| 223 | 
            +
            		def _add_cascade_condition(k, v)
         | 
| 224 | 
            +
            			if @cascades.length>0 && @cascades[-1][0].to_s==@cascade_cond.to_s
         | 
| 225 | 
            +
            				@cascades[-1][1][k]=v
         | 
| 226 | 
            +
            			else
         | 
| 227 | 
            +
            				@cascades << [@cascade_cond, { k=>v }]
         | 
| 228 | 
            +
            			end
         | 
| 229 | 
            +
            		end
         | 
| 230 | 
            +
            		def uncascaded(*args)
         | 
| 231 | 
            +
            			cond = case args.length
         | 
| 232 | 
            +
            				when 0; nil
         | 
| 233 | 
            +
            				when 1; args[0]
         | 
| 234 | 
            +
            				else; Condition.new.from_list(:any,args)
         | 
| 235 | 
            +
            			end
         | 
| 236 | 
            +
            			@uncascaded = cond
         | 
| 237 | 
            +
            		end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
            		# Setters for @condition (making sure we copy when inheriting)
         | 
| 240 | 
            +
            		def filter(*args)
         | 
| 241 | 
            +
            			_set_filter(args.length==1 ? args[0] : Condition.new.from_list(:any,args))
         | 
| 242 | 
            +
            		end
         | 
| 243 | 
            +
            		def _set_filter(condition)
         | 
| 244 | 
            +
            			@condition = condition.dup
         | 
| 245 | 
            +
            		end
         | 
| 246 | 
            +
             | 
| 247 | 
            +
            		# Set layer name
         | 
| 248 | 
            +
            		def id(name)
         | 
| 249 | 
            +
            			@kv[:id] = name
         | 
| 250 | 
            +
            		end
         | 
| 251 | 
            +
            		
         | 
| 252 | 
            +
            		# Suppress output of this layer
         | 
| 253 | 
            +
            		def suppress; @write = false end
         | 
| 254 | 
            +
            		def write?; @write end
         | 
| 255 | 
            +
             | 
| 256 | 
            +
            		# Square-bracket filters (any[...], all[...], none[...])
         | 
| 257 | 
            +
            		def any ; return Subscriptable.new(:any ) end
         | 
| 258 | 
            +
            		def all ; return Subscriptable.new(:all ) end
         | 
| 259 | 
            +
            		def none; return Subscriptable.new(:none) end
         | 
| 260 | 
            +
             | 
| 261 | 
            +
            		# Deduce 'type' attribute from style attributes
         | 
| 262 | 
            +
            		def set_type_from(s)
         | 
| 263 | 
            +
            			return unless s.include?('-')
         | 
| 264 | 
            +
            			t = s.split('-')[0].to_sym
         | 
| 265 | 
            +
            			if t==:icon || t==:text then t=:symbol end
         | 
| 266 | 
            +
            			if @type && @type!=t then raise "Attribute #{s} conflicts with deduced type #{@type} in layer #{@kv[:id]}" end
         | 
| 267 | 
            +
            			@type=t
         | 
| 268 | 
            +
            		end
         | 
| 269 | 
            +
             | 
| 270 | 
            +
            		# Create a Mapbox GL-format hash from a layer definition
         | 
| 271 | 
            +
            		def to_hash
         | 
| 272 | 
            +
            			hash = { :layout=> {}, :paint => {} }
         | 
| 273 | 
            +
             | 
| 274 | 
            +
            			# Assign key/values to correct place
         | 
| 275 | 
            +
            			@kv.each do |k,v|
         | 
| 276 | 
            +
            				s = k.to_s.gsub('_','-')
         | 
| 277 | 
            +
            				if s.include?('-color') && v.is_a?(Fixnum) then v = "#%06x" % v end
         | 
| 278 | 
            +
             | 
| 279 | 
            +
            				if LAYOUT.include?(k)
         | 
| 280 | 
            +
            					hash[:layout][s]=v
         | 
| 281 | 
            +
            					set_type_from s
         | 
| 282 | 
            +
            				elsif PAINT.include?(k)
         | 
| 283 | 
            +
            					hash[:paint][s]=v
         | 
| 284 | 
            +
            					set_type_from s
         | 
| 285 | 
            +
            				elsif TOP_LEVEL.include?(k) || HIDDEN.include?(k)
         | 
| 286 | 
            +
            					hash[s]=v
         | 
| 287 | 
            +
            				else raise "#{s} isn't a recognised layer attribute"
         | 
| 288 | 
            +
            				end
         | 
| 289 | 
            +
            			end
         | 
| 290 | 
            +
             | 
| 291 | 
            +
            			hash['type'] = @type
         | 
| 292 | 
            +
            			if @condition then hash['filter'] = @condition.encode end
         | 
| 293 | 
            +
             | 
| 294 | 
            +
            			# Convert zoom level
         | 
| 295 | 
            +
            			if (v=hash['zoom'])
         | 
| 296 | 
            +
            				hash['minzoom'] = v.is_a?(Range) ? v.first : v
         | 
| 297 | 
            +
            				hash['maxzoom'] = v.is_a?(Range) ? v.last  : v
         | 
| 298 | 
            +
            				hash.delete('zoom')
         | 
| 299 | 
            +
            			end
         | 
| 300 | 
            +
             | 
| 301 | 
            +
            			# See if we can reuse an earlier layer's properties
         | 
| 302 | 
            +
            			mk = ref_key(hash)
         | 
| 303 | 
            +
            			if stylesheet.refs[mk]
         | 
| 304 | 
            +
            				REF_PROPERTIES.each { |k| hash.delete(k) }
         | 
| 305 | 
            +
            				hash['ref'] = stylesheet.refs[mk]
         | 
| 306 | 
            +
            			else
         | 
| 307 | 
            +
            				stylesheet.refs[mk] = hash['id']
         | 
| 308 | 
            +
            			end
         | 
| 309 | 
            +
            			hash
         | 
| 310 | 
            +
            		end
         | 
| 311 | 
            +
             | 
| 312 | 
            +
            		# Key to identify matching layer properties (slow but...)
         | 
| 313 | 
            +
            		def ref_key(hash)
         | 
| 314 | 
            +
            			(REF_PROPERTIES.collect { |k| hash[k] } ).to_json
         | 
| 315 | 
            +
            		end
         | 
| 316 | 
            +
            	end
         | 
| 317 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: glug
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.2
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Richard Fairhurst
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2015-10-06 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: neatjson
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - '>='
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - '>='
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 27 | 
            +
            description: Text-based markup for Mapbox GL styles
         | 
| 28 | 
            +
            email: richard@systemeD.net
         | 
| 29 | 
            +
            executables:
         | 
| 30 | 
            +
            - glug
         | 
| 31 | 
            +
            extensions: []
         | 
| 32 | 
            +
            extra_rdoc_files: []
         | 
| 33 | 
            +
            files:
         | 
| 34 | 
            +
            - lib/glug.rb
         | 
| 35 | 
            +
            - bin/glug
         | 
| 36 | 
            +
            homepage: http://github.com/systemed/glug
         | 
| 37 | 
            +
            licenses:
         | 
| 38 | 
            +
            - FTWPL
         | 
| 39 | 
            +
            metadata: {}
         | 
| 40 | 
            +
            post_install_message: 
         | 
| 41 | 
            +
            rdoc_options: []
         | 
| 42 | 
            +
            require_paths:
         | 
| 43 | 
            +
            - lib
         | 
| 44 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 45 | 
            +
              requirements:
         | 
| 46 | 
            +
              - - '>='
         | 
| 47 | 
            +
                - !ruby/object:Gem::Version
         | 
| 48 | 
            +
                  version: '0'
         | 
| 49 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 50 | 
            +
              requirements:
         | 
| 51 | 
            +
              - - '>='
         | 
| 52 | 
            +
                - !ruby/object:Gem::Version
         | 
| 53 | 
            +
                  version: '0'
         | 
| 54 | 
            +
            requirements: []
         | 
| 55 | 
            +
            rubyforge_project: 
         | 
| 56 | 
            +
            rubygems_version: 2.0.14
         | 
| 57 | 
            +
            signing_key: 
         | 
| 58 | 
            +
            specification_version: 4
         | 
| 59 | 
            +
            summary: Glug
         | 
| 60 | 
            +
            test_files: []
         |