Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0.0 Release #19

Merged
merged 1 commit into from
Oct 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [1.0.0] - 2015-10-04
### Changed
- Significant changes to API for `identikon`. Please see updated documentation. Related to [#16](https://github.com/DarrenN/identikon/issues/16)
- Two new functions provided: `save-identikon`, `identikon->string`. Related to [#15](https://github.com/DarrenN/identikon/issues/15)
- `identikon` can now take a file as input via the `#:filename` flag.
- Added tests, dealt with weird errors in test submod w/ quickcheck
- Updated documentation

### Fixed
- [#18](https://github.com/DarrenN/identikon/issues/18) Contract-violation when saving identikon to file
2 changes: 1 addition & 1 deletion info.rkt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#lang info
(define collection "identikon")
(define version "0.2")
(define version "1.0.0")
(define scribblings '(("scribblings/identikon.scrbl" ())))
(define deps '("base"
"sugar"
Expand Down
193 changes: 116 additions & 77 deletions main.rkt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#lang racket/base

; Identikon - parses username into a sha1-based identifier list and
; interfaces with rule-sets to create identicon image
;; Identikon - parses username into a sha1-based identifier list and
;; interfaces with rule-sets to create identicon image

(require racket/date
racket/list
Expand All @@ -11,26 +11,33 @@
openssl/sha1
2htdp/image
sugar
identikon/utils)
identikon/utils
identikon/transforms)

(provide (contract-out [identikon (->* (exact-positive-integer?
exact-positive-integer?
any/c)
(string?
(or/c boolean? string?))
image?)]))

; ———————————
; implementation

; Identifier we overwrite dynamically with module functions
((or/c string? symbol?)
#:filename boolean?)
image?)]
[save-identikon (->* (string?
(or/c symbol? string?)
image?)
(#:quality number?)
boolean?)]
[identikon->string (->* (image?
(or/c symbol? string?))
(#:quality number?)
string?)]))

;; Identifier we overwrite dynamically with module functions
(define draw-rules null)

(define-namespace-anchor a)

(define-runtime-path RULES-DIR "rules")

; Dynamically load in a rules file
;; Dynamically load in a rules file
(define (load-plug-in file)
(let ([ns (make-base-empty-namespace)]
[filename (build-path RULES-DIR file)])
Expand All @@ -40,76 +47,95 @@
(parameterize ([current-namespace ns])
(dynamic-require filename 'draw-rules))))

; Create a filename and check if the file already exists, if so
; append a timestamp
;; Create a filename and check if the file already exists, if so
;; append a timestamp
(define (make-filename name size extension)
(let* ([ext (string-join (list "." extension) "")]
[sizename (string-join (list name (number->string size)) "_")]
(let* ([ext (format ".~a" (->string extension))]
[sizename (format "~a_~a" (->string name) (number->string size))]
[filename (string-join (list sizename ext) "")])
(if (file-exists? filename)
(string-join (list sizename "_" (number->string (date->seconds (current-date))) ext) "")
(string-join
(list sizename "_"
(number->string (date->seconds (current-date))) ext) "")
filename)))

; Save the file based on type - png or svg
(define (save-identicon filename type rendered)
(cond
[(string=? "svg" type) (save-svg-image rendered filename)]
[(string=? "png" type) (save-image rendered filename)]
[else (error 'save-identicon "failed because could not not save file type of ~a" type)]))

; Turn a SHA1 hash into a list of 20 base 10 numbers
(define (process-user user)
(let [(str (if (string? user)
user
(->string user)))]
(map (λ (x) (string->number x 16))
(string-pairs (sha1 (open-input-bytes (string->bytes/utf-8 str)))))))

; Identikon - build an identicon of a specific size based on username and
; using a rule-set. Will automatically drop the identicon in the repl unless
; you tell it to save
;
; ex: ;(identikon 300 300 "dfsdf")
;
(define (identikon width height username [rules "default"] [type #f])
(let* ([processed-user (process-user username)]
[rule-file (string-join (list rules "rkt") ".")])

; Load rules file if provided
;; Save the file based on type - png, jpeg or svg
(define (save-identikon filename type image #:quality [quality 75])
(let* ([ext (->string type)]
[path (make-filename filename (image-width image) ext)])
(cond
[(string=? "svg" ext) (save-svg image path)]
[(string=? "png" ext) (save-bitmap image path)]
[(string=? "jpeg" ext) (save-bitmap image path 'jpeg #:quality quality)]
[else (error 'save-identicon
"failed because could not not save file type of ~a" type)])))

;; Output the image as a string representation
;; (svg as xml, png/jpeg as base64 bytes)
(define (identikon->string type image #:quality [quality 75])
(let* ([ext (->string type)])
(cond
[(string=? "svg" ext) (image->svg-string image)]
[(or (string=? "png" ext) (string=? "jpeg" ext))
(image->bitmap-string image type quality)]
[else (error 'identikon->string "~a is not a valid image type" ext)])))

;; Convert a symbol or string into a rules filename
(define (create-rules-filename rules)
(let ([root (if (string? rules)
rules
(->string rules))])
(format "~a.rkt" rules)))

#|

Identikon - build an identicon of a specific size based on username and
using a rule-set. Will automatically drop the identicon in the repl unless
you tell it to save

ex: (identikon 300 300 "dfsdf")
(identikon 300 300 'dfsdf 'qbert)

|#
(define (identikon width height input
[rules "default"] #:filename [filename #f])
(let* ([processed-input (if filename
(file->numberlist input)
(string->numberlist input))]
[rule-file (create-rules-filename rules)])

;; Load rules file if provided
(set! draw-rules (load-plug-in rule-file))

; Create identicon
(define rendered (draw-rules width height processed-user))
;; Create identicon
(define rendered (draw-rules width height processed-input))

; Either save the identicon or output to REPL
(if type
(save-identicon (make-filename username width type) type rendered)
rendered)))
;; Return identikon (image)
rendered))

(module+ test
(require quickcheck
(require rackunit
sugar
2htdp/image)

; Ensure we get a list of 20 values
(define process-user-lengths-agree
(property ([val (choose-mixed (list (choose-integer 1 (random 10000)) (choose-string choose-printable-ascii-char (random 100))))])
(= 20 (length (process-user val)))))
(test-case
"create-rules-filename will append .rkt to anything that can be stringed"
(check-regexp-match ".rkt" (create-rules-filename "rza"))
(check-regexp-match ".rkt" (create-rules-filename 'wutang))
(check-regexp-match ".rkt" (create-rules-filename 187)))

(quickcheck process-user-lengths-agree)
(test-case
"identikon returns an image"
(check-pred image? (identikon 100 100 'rza)))

; Ensure identikon returns images
; !! Warning: this is computationally expensive as it generates 100
; !! identicons
(define identikon-images-agree
(property ([dim arbitrary-natural]
[str arbitrary-printable-ascii-string])
(image? (identikon dim dim str))))

(quickcheck identikon-images-agree))
(test-case
"identikon->string returns a string"
(check-pred string? (identikon->string 'jpeg (identikon 100 100 'rza)))
(check-pred string? (identikon->string 'png (identikon 100 100 'rza)))
(check-pred string? (identikon->string 'svg (identikon 100 100 'rza)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Command line handling for Identikon
;; Command line handling for Identikon
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(module+ main
Expand All @@ -118,16 +144,21 @@

(define size-flags (make-parameter null))
(define rules-set (make-parameter '("default")))
(define name (make-parameter null))
(define input-str (make-parameter null))
(define file-name (make-parameter null))
(define ext (make-parameter "png"))

(define make-identikon
(command-line
#:program "identikon"
#:once-each
[("-n" "--name") nm
"Username to convert to identikon"
(name nm)]
[("-i" "--input-str") in
"String input-str to convert to identikon"
(input-str in)]

[("-f" "--file") fl
"File or input stream used to generate identikon"
(file-name fl)]

[("-t" "--type") ty
"File type: png or svg"
Expand All @@ -139,16 +170,24 @@

#:multi
[("-s" "--size") sz
"Add a square size to generate"
"Add a square size(s) to generate. You can create multiple sizes."
(size-flags (cons sz (size-flags)))]))

(cond
[(and (empty? (size-flags)) (empty? (name))) (printf "No information provided ~n")]
[(and (empty? (size-flags))
(empty? (input-str))) (printf "No information provided ~n")]
[(empty? (size-flags)) (printf "No sizes were provided, -s ~n")]
[(empty? (name)) (printf "No name provided to process, -n ~n")]
[(empty? (input-str)) (printf "No input provided to process, -i ~n")]
[(not (empty? (file-name))) (for ([s (size-flags)])
(save-identikon (file-name) (ext) (identikon (string->number s)
(string->number s)
(file-name)
(first (rules-set))
#:filename #t))
(printf "Saved ~apx identicon for ~a ~n" s (file-name)))]
[else (for ([s (size-flags)])
(identikon (string->number s)
(string->number s)
(name)
(first (rules-set)) (ext))
(printf "Saved ~apx identicon for ~a ~n" s (name)))]))
(save-identikon (input-str) (ext) (identikon (string->number s)
(string->number s)
(input-str)
(first (rules-set))))
(printf "Saved ~apx identicon for ~a ~n" s (input-str)))]))
8 changes: 4 additions & 4 deletions rules/default.rkt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#lang racket/base

(require racket/list
2htdp/image
identikon/utils)

; Default rule-set for identikon.
; All rule-sets must provide a single function, draw-rules
; which is called by identikon. This function should always
Expand All @@ -10,10 +14,6 @@
; ———————————
; implementation

(require racket/list
2htdp/image
identikon/utils)

; Constants
(define BORDER-MAX 20)

Expand Down
Loading