Posted to tcl by Napier at Wed Oct 15 00:33:09 GMT 2014view pretty

namespace eval Firebase {
	# The following packages are required for operation:
	package require json::write
	package require json
	package require http
	package require tls

	variable URL "automationapps.firebaseio.com"
	variable Secret "8ba9dbadzve2pnPdrIMLpCgcc9cYf7t10EhNavRS"
	variable Timestamp "{\".sv\": \"timestamp\"}"
	variable DefaultMethod "PATCH"
	variable DefaultBlocking 0
	::http::register https 443 tls::socket
	
	proc Register {url secret} {
		# Provide the URL & Auth Token / Firebase Secret to be used on all Send Calls
		# this will be the permament default for all calls after registering.
		set ::Firebase::URL $url
		set ::Firebase::Secret $secret
	}

	proc Send {args} {
		## Description / Overview:
		# Sends Data to Firebase and uses the following Arguments
		## Example Call:
		# ::Firebase::Send -method PATCH -directory users -dict "{Key 1} {Value 1} {Key 2} {Value 2}"
		#
		## -method : This can be PUT, PATCH, POST and defaults to PATCH if not defined
		### Example: -method "PUT"
		#
		## -directory : This defines where the data will go relative to the Firebase URL
		### Note: Do not provide trailing or leading /'s 
		### Example: -directory "dir1/dir2/dir3"
		#
		## -data : This must be a valid TCL Dictionary with your Keys & Values that should be placed
		## at the location provided in the "-directory" argument.
		### Example: -dict "{Key 1} {Value 1} {Key 2} {Value 2}"
		#
		## -secret : You can temporarily replace the default Firebase Secret (Auth Token) by defining the -secret argument.
		### Example: -secret $firebaseSecret
		#
		## -url : You can temporarily replace the default Firebase URL by defining the -url argument.
		### Example: -url $firebaseURL
		#
		## -arguments : You can define arguments as a Tcl Dictionary to be included
		### NOTE: Not currently available 
		#
		## -blocking : Defines whether the call should be made with blocking or unblocking.  Should be a 0 (Unlocking) or 1 (Blocking)
		### Note: This will default to 0 (Unblocking).
		### Note: You must use blocking if a response is required in the return value.  
		### Example: -blocking 1
		#
		if {[dict exists $args -secret]} {set secret [dict get $args -secret]} else {set secret ${::Firebase::Secret}}
		if {[dict exists $args -url]} {set url [string map {"https://" ""} [dict get $args -url]]} else {set url ${::Firebase::URL}}
		if {[dict exists $args -method]} {set method [dict get $args -method]} else {set method ${::Firebase::DefaultMethod}}
		if {[dict exists $args -directory]} {set directory [string map {" " "+"} [dict get $args -directory]]} else {set directory ""}
		if {[dict exists $args -dict]} {set tempDict [dict get $args -dict]} else {return -code error "Must Define a Tcl Dictionary in -dict"}
		if {[dict exists $args -arguments]} {set arguments [dict get $args -arguments]} else {set arguments ""}
		if {[dict exists $args -blocking]} {
			set blocking [dict get $args -blocking]
			if {![string is bool $blocking]} {return -code error "Argument -blocking must be a boolean value"}
		} else {set blocking ${::Firebase::DefaultBlocking}}

		dict for {k v} $tempDict {
			if {[string is integer $v] || [string index $v 0] == {\"} && [string index $v end] == {\"}} { continue
			} else {dict set tempDict $k [::json::write string $v]}
		}
		set data [::json::write object {*}$tempDict]
		if {!$blocking} {
			::http::geturl https://${url}/${directory}.json?auth=${secret}${arguments} -query $data -method $method -command ::Firebase::UnblockingCallback
		} else {
			set token [::http::geturl https://${url}/${directory}.json?auth=${secret}${arguments} -query $data -method $method]
			set response [::http::data $token]
			::http::cleanup $token
			return $response
		}
		return
	}

	proc UnblockingCallback {token} {
		puts "Cleaning up $token"
		::http::cleanup $token
	}

	proc Get {args} {
		## Description / Overview:
		# Gets Data from Firebase based on the specified Arguments.  Will respond with a Tcl Dictionary.
		# If -key or -keys is not provided, the entire directory and all sub directories will be returned as a Tcl Dictionary.
		#
		## -directory : This defines where the data will be retrieved relative to the Firebase URL
		### Note: Do not provide trailing or leading /'s 
		### Example: -directory "dir1/dir2/dir3"
		#
		## -key: The "Key" to get from the Firebase Directory provided.  Will return the keys value in a Tcl Dictionary.
		### Example: -key "Key 1"
		### Returns: {Key 1} {Value 1}
		#
		## -keys : If provided, will attempt to get each Key provided from the directory.  Should be a Tcl List.
		### Note: This will return a Tcl Dictionary
		### Example: -keys "{Key 1} {Key 2} {Key 3}"
		### Returns: {Key 1} {Value 1} {Key 2} {Value 2} {Key 3} {Value 3}
		## -secret : You can temporarily replace the default Firebase Secret (Auth Token) by defining the -secret argument.
		### Example: -secret $firebaseSecret
		#
		## -url : You can temporarily replace the default Firebase URL by defining the -url argument.
		### Example: -url $firebaseURL
		set tempDict [dict create]; set keys ""
		if {[dict exists $args -directory]} {set directory [string map {" " "+"} [dict get $args -directory]]} else {set directory ""}
		if {[dict exists $args -secret]} {set secret [dict get $args -secret]} else {set secret $::Firebase::Secret}
		if {[dict exists $args -url]} {set url [string map {"https://" ""} [dict get $args -url]]} else {set url $::Firebase::URL}
		if {[dict exists $args -key]} {
			lappend keys [dict get $args -key]
		} elseif {[dict exists $args -keys]} {
			lappend keys {*}[dict get $args -keys]
		} else {
			set token [::http::geturl https://${url}/${directory}/.json?auth=${secret}]
			set response [::http::data $token]
			::http::cleanup $token
			set tempDict [::json::json2dict $response]
			return $tempDict
		}
		foreach key $keys {
			puts "Key is $key"
			set FormattedKey [::Firebase::ue $key]
			set token [::http::geturl https://${url}/${directory}/${FormattedKey}.json?auth=${secret}]
			set response [::http::data $token]
			::http::cleanup $token
			set response [::json::json2dict $response]
			lappend tempDict $key $response
		}
		return $tempDict
	}

	proc Delete {args} {
		## Description / Overview:
		# Deletes data from Firebase based on the specified Arguments
		# Note: Only 1 of the "-key" "-keys" or "-dict" should be called at any time or an error will be returned.
		# Note: If -key, -keys, or -dict are not provided the directory will be deleted.
		#
		## Example Call:
		# ::Firebase::Delete -directory dir1/dir2/dir3 -key Test
		#
		## -directory : This defines where the data will be deleted relative to the Firebase URL
		### Note: Do not provide trailing or leading /'s 
		### Example: -directory "dir1/dir2/dir3"
		#
		## -key : The "Key" to delete from the Firebase Directory provided.
		### Example: -key "Key 1"
		#
		## -keys : If provided, will attempt to delete each Key provided.  Should be a Tcl List.
		### Example: -keys "{Key 1} {Key 2} {Key 3}"
		#
		## -dict : If provided will attempt to delete each Key of the provided Tcl Dict.
		### Example: -dict "{Key 1} {Value 1} {Key 2} {Value 2} {Key 3} {Value 3}"
		#
		## -secret : You can temporarily replace the default Firebase Secret (Auth Token) by defining the -secret argument.
		### Example: -secret $firebaseSecret
		#
		## -url : You can temporarily replace the default Firebase URL by defining the -url argument.
		### Example: -url $firebaseURL
		# 
		## -blocking : Defines whether the call should be made with blocking or unblocking.  Should be a 0 (Unlocking) or 1 (Blocking)
		### Note: This will default to 0 (Unblocking).
		### Note: You must use blocking if a response is required in the return value.  
		### Example: -blocking 1

		if {[dict exists $args -directory]} {set directory [string map {" " "+"} [dict get $args -directory]]} else {set directory ""}
		if {[dict exists $args -secret]} {set secret [dict get $args -secret]} else {set secret $::Firebase::Secret}
		if {[dict exists $args -url]} {set url [string map {"https://" ""} [dict get $args -url]]} else {set url $::Firebase::URL}
		if {[dict exists $args -blocking]} {
			set blocking [dict get $args -blocking]
			if {![string is bool $blocking]} {return -code error "Argument -blocking must be a boolean value"}
		} else {set blocking ${::Firebase::DefaultBlocking}}
		set keys ""
		if {[dict exists $args -key]} {
			lappend keys [dict get $args -key]
		} elseif {[dict exists $args -keys]} {
			lappend keys {*}[dict get $args -keys]
		} elseif {[dict exists $args -dict]} {
			lappend keys [dict keys [dict get $args -dict]]
		} else {
			if {!$blocking} {
				::http::geturl https://${url}/${directory}/.json?auth=${secret} -method DELETE -command ::Firebase::UnblockingCallback
			} else {
				set token [::http::geturl https://${url}/${directory}/.json?auth=${secret} -method DELETE]
				::http::cleanup $token
			}
			return
		}
		foreach key $keys {
			set key [::Firebase::ue $key]
			if {!$blocking} {
				::http::geturl https://${url}/${directory}/${key}.json?auth=${secret} -method DELETE -command ::Firebase::UnblockingCallback
				continue
			} else {
				set token [::http::geturl https://${url}/${directory}/${key}.json?auth=${secret} -method DELETE]
				::http::cleanup $token
				continue
			}
		}
		return
	}

	proc ue_init {} {
        lappend d + { }
        for {set i 0} {$i < 256} {incr i} {
            set c [format %c $i]; set x %[format %02x $i]
            if {![string match {[a-zA-Z0-9]} $c]} {lappend e $c $x; lappend d $x $c}
        }
        set ::ue_map $e; set ::ud_map $d
    }
    ::Firebase::ue_init
    proc ue {s} {string map $::ue_map $s}; proc ud {s} {string map $::ud_map $s}
}