'for' can be handy when unpacking compound lines:

(ns foobar
  (:require [clojure.java.io :as io]))

(defn parse-acl [acl-file]
  (with-open [r (io/reader acl-file)]
    (apply
     merge-with into
     (for [[status users path] (map #(.split % "\\|") (line-seq r))
           :when (= "avail" status)
           user (.split users ",")]
       {user [path]}))))

Justin

On Mar 3, 9:15 pm, Stefan Rohlfing <stefan.rohlf...@gmail.com> wrote:
> Dear Clojure Group,
>
> I am currently reading the online book Pro Git <http://progit.org/book/>. In
> chapter 7.4 <http://progit.org/book/ch7-4.html> (section “Enforcing a
> User-Based ACL System”) there is a task of reading in an access control list
> (ACL) file, such as the following
>
> # avail/unavail | users | path
> avail|nickh,pjhyett,defunkt,tpw
> avail|usinclair,cdickens,ebronte|doc
> avail|schacon|lib
> avail|schacon|tests
>
> and printing out a map of the form { "user1" [path 1, path 2], "user2"
> [path2, path3] ...}.
>
> The author of the book provides a solution in Ruby, which I find relatively
> easy to follow, despite not having written any Ruby code before:
>
> def get_acl_access_data(acl_file)
>  # read in ACL data
>  acl_file = File.read(acl_file).split("\n").reject { |line| line == '' }
>  access = {}
>  acl_file.each do |line|
>    avail, users, path = line.split('|')
>    next unless avail == 'avail'
>    users.split(',').each do |user|
>      access[user] ||= []
>      access[user] << path
>    end
>  end
>  access
> end
>
> I then tried the same in Clojure, but found my solution to be much less
> readable compared to the Ruby code:
>
> (use '[clojure.string :only (split)])
>
> (defn get-acl-access-data [file]
>  (let [acl (split (slurp file) #"\n")]
>    (apply merge-with #(into %1 %2)
>              (map (fn [[avail users path]]
>                          (let [users (split users #",")]
>                             (reduce (fn [acc user]
>                                             (when (= avail "avail")
>                                               (assoc acc user [path])))
>                                            {} users)))
>                        (map #(split % #"\|") acl)))))
>
> ;; Output:
> ;; {"schacon" ["lib" "tests"],
> ;;  "usinclair" ["doc"],
> ;;  "cdickens" ["doc"],
> ;;  "ebronte" ["doc"],
> ;;  "tpw" [nil],
> ;;  "defunkt" [nil],
> ;;  "pjhyett" [nil],
> ;; "nickh" [nil]}
>
> Maybe it is just because I am an ambitious beginner at best, but I am afraid
> I won’t be able to figure out immediately what this code is doing in a few
> weeks.
>
> However, there probably is a better way of translating the Ruby version into
> Clojure. With "better" I mean easier to follow. My main goal is on clarity,
> as I often struggle organizing my code in a way I would consider readable.
>
> I therefore would be glad for any ideas of improvement. Any suggestions are
> highly welcome!
>
> Best regards,
>
> Stefan

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to