i was gonna suggest this:
(let [users (->> (split (slurp "acl") #"\n")
(map #(split % #"\|"))
(map (fn [[a u p]] [a (split u #",") p]))
(filter (fn [[a _ _]] (= "avail" a)))
(mapcat (fn [[_ users path]]
(map #(hash-map % [path]) users)))
(apply merge-with into))]
users)
then i read the bit about readable... :P
On Thu, Mar 3, 2011 at 8:51 PM, Stuart Halloway
<[email protected]> wrote:
> Dear Clojure Group,
>
> I am currently reading the online book Pro Git. In chapter 7.4 (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 still a beginner, but I am afraid I won’t be
> able to figure out immediately what this code is doing a few weeks from now.
>
> However, I am sure there must be a better way of translating the Ruby
> version into Clojure. 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
>
> Both the approaches above have the weakness that the steps are commingled,
> making it difficult to test (or reuse) part of the work done by the fn. Here
> is a Clojure version that makes the steps more distinct:
> (require '[clojure.string :as str])
> (require '[clojure.java.io :as io])
> (with-open [r (io/reader "somefile")]
> (->> (line-seq r)
> (map #(str/split % #"\|"))
> (filter #(= "avail" (first %)))
> (mapcat (fn [[_ users path]] (map hash-map (str/split users #",")
> (repeat [path]))))
> (apply merge-with into)))
> From this it is easy to see that the fn:
> 1. splits the lines on |
> 2. filters on "avail"
> 3. builds a list of user|path pairs
> 4. merges the user|path pairs into a map
>
> Stuart Halloway
> Clojure/core
> http://clojure.com
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to [email protected]
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> [email protected]
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en