Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

better reflection

  • Loading branch information...
commit 72920015a051a400f35d5eed41549a5882c868b8 1 parent 8db9a2b
Stuart Halloway authored
4  src/mycroft/breadcrumb.clj
@@ -8,7 +8,7 @@
8 8
    :start                      first item to show (for pagination)
9 9
    :headers                    explicitly select table headers."
10 10
   [options]
11  
-  (str "?" (encode-params options)))
  11
+  (encode-params options))
12 12
 
13 13
 (defn namespace-link
14 14
   [ns-name]
@@ -46,7 +46,7 @@
46 46
              (map (fn [partial-selector]
47 47
                     [:span
48 48
                      " » "
49  
-                     [:a {:href (options->query-string {:selectors partial-selector})}
  49
+                     [:a {:href (str "?" (options->query-string {:selectors partial-selector}))}
50 50
                       (breadcrumb-text (last partial-selector)) ]])))
51 51
         [:span " » " (breadcrumb-text (last selector))]]))])
52 52
 
28  src/mycroft/class.clj
@@ -3,27 +3,10 @@
3 3
   (:require [mycroft.reflect :as reflect]
4 4
             [mycroft.breadcrumb :as breadcrumb]))
5 5
 
6  
-(defmulti customize-options
7  
-  (fn [options selectors] selectors))
8  
-
9  
-(defmethod customize-options :default [options & _] options)
10  
-
11  
-(defmethod customize-options [:fields]
12  
-  [options _]
13  
-  (assoc options :headers [:name :type :modifiers]))
14  
-
15  
-(defmethod customize-options [:methods]
16  
-  [options _]
17  
-  (assoc options :headers [:name :parameter-types :return-type :modifiers]))
18  
-
19  
-(defmethod customize-options [:constructors]
20  
-  [options _]
21  
-  (assoc options :headers [:name :parameter-types :modifiers]))
22  
-
23 6
 (defn render
24 7
   [classname options]
25 8
   (let [cls (Class/forName classname)
26  
-        obj (reflect/reflect cls)
  9
+        obj (reflect/members cls)
27 10
         selectors (:selectors options)
28 11
         selection (select-in obj selectors)]
29 12
     [:div
@@ -32,4 +15,11 @@
32 15
       [:span " « "
33 16
        (str "class " classname)]]
34 17
      [:div
35  
-      (render-type selection (customize-options options selectors))]]))
  18
+      (render-type {:superclasses (supers cls)}
  19
+                   {})]
  20
+     [:div
  21
+      (render-type selection
  22
+                   (if selectors
  23
+                     options
  24
+                     (assoc options :headers
  25
+                            [:name :type :parameter-types :return-type :modifiers :declaring-class])))]]))
15  src/mycroft/data.clj
@@ -118,7 +118,7 @@
118 118
      [:div.buttons
119 119
       (if (meta selection)
120 120
         [:span
121  
-         [:a {:href (breadcrumb/options->query-string (add-selector options ::meta))} "metadata"]]
  121
+         [:a {:href (str "?" (breadcrumb/options->query-string (add-selector options ::meta)))} "metadata"]]
122 122
         [:span.disabled-button "metadata"])
123 123
       (if-let [classname (.?. selection getClass getName)]
124 124
         [:span
@@ -140,7 +140,7 @@
140 140
   [item]
141 141
   (binding [*print-length* 5
142 142
             *print-level* 2]
143  
-    (str item)))
  143
+    (with-out-str (pr item))))
144 144
 
145 145
 (defn render-cell
146 146
   ([content] (render-cell content nil))
@@ -155,7 +155,7 @@
155 155
   [row options]
156 156
   {:pre (= 2 (count row))}
157 157
   `[:tr
158  
-    ~(render-cell (first row) {:href (breadcrumb/options->query-string (add-selector options (first row)))})
  158
+    ~(render-cell (first row) {:href (str "?" (breadcrumb/options->query-string (add-selector options (first row))))})
159 159
     ~@(map render-cell (rest row))])
160 160
 
161 161
 (defn render-row-matching-headers
@@ -163,8 +163,8 @@
163 163
   {:pre [(= 2 (count row))
164 164
          (associative? obj)]}
165 165
   `[:tr
166  
-    ~(render-cell key {:href (breadcrumb/options->query-string (add-selector options key))})
167  
-    ~@(let [explicit-columns (map obj headers)]
  166
+    ~(render-cell key {:href (str "?" (breadcrumb/options->query-string (add-selector options key)))})
  167
+    ~@(let [explicit-columns (map #(% obj) headers)]
168 168
         (map render-cell explicit-columns))
169 169
     ~(let [rest-of-object (apply dissoc obj headers)]
170 170
         (render-cell rest-of-object))])
@@ -185,14 +185,14 @@
185 185
             (> count items-per-page))
186 186
     [:div.buttons {:id "pagination"}
187 187
      (if (> start 0)
188  
-       [:a {:href (breadcrumb/options->query-string (update-in options [:start] - items-per-page))}
  188
+       [:a {:href (str "?" (breadcrumb/options->query-string (update-in options [:start] - items-per-page)))}
189 189
         "prev"]
190 190
        [:span.disabled-button "prev"])
191 191
      (when count
192 192
        [:span
193 193
         (str "Items " start "-" (min count (+ start items-per-page)) " of " count)])
194 194
      (if has-more?
195  
-       [:a {:href (breadcrumb/options->query-string (update-in options [:start] + 0 items-per-page))}
  195
+       [:a {:href (str "?" (breadcrumb/options->query-string (update-in options [:start] + 0 items-per-page)))}
196 196
         "next"]
197 197
        [:span.disabled-button "next"])]))
198 198
 
@@ -210,6 +210,7 @@
210 210
 
211 211
 (defn render-table
212 212
   [content {:keys [headers] :as options}]
  213
+  (println "options are " options)
213 214
   (if (seq content)
214 215
     (if headers
215 216
       (render-table-with-headers content options)
2  src/mycroft/history.clj
@@ -6,7 +6,7 @@
6 6
 (defn add
7 7
   "Add an object to history, returning its URL."
8 8
   [obj]
9  
-  (str "/vars/mycroft.history/history"
  9
+  (str "/vars/mycroft.history/history?"
10 10
        (breadcrumb/options->query-string {:selectors [:mycroft.data/deref :mycroft.data/deref (dec (count (swap! history conj obj)))]})))
11 11
 
12 12
 
18  src/mycroft/main.clj
@@ -17,15 +17,15 @@
17 17
    inspect anything, including vars, classes, and arbitrary
18 18
    expressions. A history of past things you have inspected is
19 19
    at /vars/mycroft.history/history."
20  
-  [o]
21  
-  (if inspector
22  
-    (if (symbol? o)
23  
-      (if-let [resolved (ns-resolve *ns* o)]
24  
-        `(.inspect inspector ~resolved)
25  
-        `(.inspect inspector '~o))
26  
-      `(.inspect inspector ~o))
27  
-    
28  
-    "Launch the inspector with (mycroft.main/run port) first!"))
  20
+  [o & options]
  21
+  (let [options (apply hash-map options)]
  22
+    (if inspector
  23
+      (if (symbol? o)
  24
+        (if-let [resolved (ns-resolve *ns* o)]
  25
+          `(.inspect inspector ~resolved ~options)
  26
+          `(.inspect inspector '~o ~options))
  27
+        `(.inspect inspector ~o ~options))
  28
+      "Launch the inspector with (mycroft.main/run port) first!")))
29 29
 
30 30
 
31 31
 
220  src/mycroft/reflect.clj
... ...
@@ -1,5 +1,7 @@
1 1
 (ns mycroft.reflect
2  
-  (:import [java.lang.reflect Field Constructor Method Modifier]))
  2
+  (:import [java.lang.reflect Modifier])
  3
+  (:require [clojure.string :as str])
  4
+  (:use [clojure.pprint :only (pprint)]))
3 5
 
4 6
 (defn modifiers->set
5 7
   [mod]
@@ -17,55 +19,209 @@
17 19
                 (when (Modifier/isTransient mod) :transient)
18 20
                 (when (Modifier/isVolatile mod) :volatile)])))
19 21
 
  22
+(defn modifier-predicate
  23
+  [sym]
  24
+  `(defn ~(symbol (str sym "?"))
  25
+     ~(str "Does the :modifiers of o include :" sym " ?")
  26
+     [~'o]
  27
+     ((:modifiers ~'o) ~(keyword sym))))
  28
+
  29
+(defmacro modifier-predicates
  30
+  [& modifiers]
  31
+  `(do
  32
+     ~@(map
  33
+        modifier-predicate
  34
+        modifiers)))
  35
+
  36
+(modifier-predicates abstract final interface native private
  37
+                     protected public static strict synchronized
  38
+                     transient volatile)
  39
+
  40
+(defprotocol ReplFormat
  41
+  (format-member [o] "Helper method used by describe when printing reflective descriptions."))
  42
+
  43
+(extend-protocol ReplFormat
  44
+  Object
  45
+  (format-member [o] o)
  46
+
  47
+  Class
  48
+  (format-member [c]
  49
+               (if (.isArray c)
  50
+                 (str (format-member (.getComponentType c)) "[]")
  51
+                 (.getName c))))
  52
+
  53
+(defn param-str
  54
+  [m]
  55
+  (str "(" (str/join ", " (map format-member (:parameter-types m))) ")"))
  56
+
  57
+(defrecord Constructor
  58
+  [name declaring-class parameter-types exceptions modifiers]
  59
+  ReplFormat
  60
+  (format-member [c]
  61
+               (str "<init> " (param-str c))))
  62
+
  63
+(defn constructor?
  64
+  "Is x an instance of mycroft.reflect/Constructor?"
  65
+  [o]
  66
+  (instance? Constructor o))
  67
+
20 68
 (defn constructor->map
21  
-  [^Constructor constructor]
22  
-  {:name (.getName constructor)
23  
-   :declaring-class (.getDeclaringClass constructor)
24  
-   :parameter-types (vec (.getParameterTypes constructor))
25  
-   :exception-types (vec (.getExceptionTypes constructor))
26  
-   :modifiers (modifiers->set (.getModifiers constructor))})
27  
-
28  
-(defn constructors-set
  69
+  [^java.lang.reflect.Constructor constructor]
  70
+  (Constructor.
  71
+    (.getName constructor)
  72
+    (.getDeclaringClass constructor)
  73
+    (vec (.getParameterTypes constructor))
  74
+    (vec (.getExceptionTypes constructor))
  75
+    (modifiers->set (.getModifiers constructor))))
  76
+
  77
+(defn declared-constructors
  78
+  "Return a set of the declared constructors of class as a Clojure map."
29 79
   [^Class cls]
30 80
   (set (map
31 81
         constructor->map
32 82
         (.getDeclaredConstructors cls))))
33 83
 
  84
+(defrecord Method
  85
+  [name return-type declaring-class parameter-types exception-types modifiers]
  86
+  ReplFormat
  87
+  (format-member [c]
  88
+               (str (format-member (:return-type c)) " " (:name c) (param-str c))))
  89
+
  90
+(defn method?
  91
+  "Is x an instance of mycroft.reflect/Method?"
  92
+  [x]
  93
+  (instance? Method x))
  94
+
34 95
 (defn method->map
35  
-  [^Method method]
36  
-  {:name (.getName method)
37  
-   :return-type (.getReturnType method)
38  
-   :declaring-class (.getDeclaringClass method)
39  
-   :parameter-types (vec (.getParameterTypes method))
40  
-   :exception-types (vec (.getExceptionTypes method))
41  
-   :modifiers (modifiers->set (.getModifiers method))})
42  
-
43  
-(defn methods-set
  96
+  [^java.lang.reflect.Method method]
  97
+  (Method.
  98
+    (.getName method)
  99
+    (.getReturnType method)
  100
+    (.getDeclaringClass method)
  101
+    (vec (.getParameterTypes method))
  102
+    (vec (.getExceptionTypes method))
  103
+    (modifiers->set (.getModifiers method))))
  104
+
  105
+(defn declared-methods
  106
+  "Return a set of the declared constructors of class as a Clojure map."
44 107
   [^Class cls]
45 108
   (set (map
46 109
         method->map
47 110
         (.getDeclaredMethods cls))))
48 111
 
  112
+(defrecord Field
  113
+  [name type declaring-class modifiers]
  114
+  ReplFormat
  115
+  (format-member [c]
  116
+               (str (format-member (:type c)) " " (:name c))))
  117
+
  118
+(defn field?
  119
+  "Is x an instance of mycroft.reflect/Field?"
  120
+  [x]
  121
+  (instance? Field x))
  122
+
49 123
 (defn field->map
50  
-  [^Field field]
51  
-  {:name (.getName field)
52  
-   :type (.getType field)
53  
-   :declaring-class (.getDeclaringClass field)
54  
-   :modifiers (modifiers->set (.getModifiers field))})
  124
+  [^java.lang.reflect.Field field]
  125
+  (Field.
  126
+    (.getName field)
  127
+    (.getType field)
  128
+    (.getDeclaringClass field)
  129
+    (modifiers->set (.getModifiers field))))
55 130
 
56  
-(defn fields-set
  131
+(defn declared-fields
  132
+  "Return a set of the declared fields of class as a Clojure map."
57 133
   [^Class cls]
58 134
   (set (map
59 135
         field->map
60 136
         (.getDeclaredFields cls))))
61 137
 
62  
-
63 138
 (defn reflect
64  
-  [cls]
65  
-  (when cls
66  
-    (if (class? cls)
67  
-      {:fields (fields-set cls)
68  
-       :methods (methods-set cls)
69  
-       :constructors (constructors-set cls)}
70  
-      (reflect (class cls)))))
  139
+  "Low-level method called by members and describe.
  140
+
  141
+   Reflect over o, returning a map containing information about its
  142
+   supers, fields, methods, and constructors. Includes all members
  143
+   all the way back up the inheritance hierarchy, so you can filter
  144
+   to get the parts you want."
  145
+  [o]
  146
+  (when o
  147
+    (if (class? o)
  148
+      (let [supers (supers o)
  149
+            classes (conj supers o)]
  150
+        {:supers supers
  151
+         :fields (into #{} (mapcat declared-fields classes))
  152
+         :methods (into #{} (mapcat declared-methods classes))
  153
+         :constructors (into #{} (mapcat declared-constructors classes))})
  154
+      (reflect (class o)))))
  155
+
  156
+(defn exclude
  157
+  "Create a predicate that matches only items whose :declaring-class
  158
+   is *not* in the set specified by classes, or any superclasses of
  159
+   classes."
  160
+  [& classes]
  161
+  (let [excluded-class? (into (set classes) (mapcat supers classes))]
  162
+    #(not (excluded-class? (:declaring-class %)))))
  163
+
  164
+(defn only
  165
+  "Create a predicate that matches only items whose :declaring-class
  166
+   is in the set specified by classes."
  167
+  [& classes]
  168
+  (let [included-class? (set classes)]
  169
+    #(included-class? (:declaring-class %))))
  170
+
  171
+(defn returns
  172
+  "Create a predicate that matches items whose :return-type is type."
  173
+  [type]
  174
+  #(= type (:return-type %)))
  175
+
  176
+(defn members
  177
+  "Returns all members (fields, methods, constructors) of o,
  178
+   sorted by name.
  179
+
  180
+   Filters are predicates that are used to limit the results
  181
+   returned. Common filters include:
  182
+
  183
+   (only Foo Bar)       include only results from classes
  184
+                        Foo and Bar
  185
+   (exclude Foo Bar)    exclude results from classes Foo and
  186
+                        Bar, *and* all their supers.
  187
+   (returns String)     include only methods that return
  188
+                        String
  189
+   private?             include only private methods
  190
+   public?              include only public methods?
  191
+
  192
+   There are predicates for all the java Modifiers:
  193
+             abstract final interface native private
  194
+             protected public static strict synchronized
  195
+             transient volatile
  196
+   Examples:
  197
+
  198
+   (members 123)
  199
+   (members String (returns String))
  200
+   (members foo private? (only Foo))"
  201
+  [o & filters]
  202
+  (let [reflection (reflect o)]
  203
+    (->> (mapcat reflection [:fields :methods :constructors])
  204
+         (clojure.core/filter (if filters
  205
+                                #(every? (fn [filter-fn] (filter-fn %)) filters)
  206
+                                identity))
  207
+         (sort-by :name))))
71 208
 
  209
+(defmacro describe
  210
+  "Print a description of o, via reflection. See members for
  211
+   a description of common filters."
  212
+  [o & filters]
  213
+  `(let [o# ~o
  214
+         c# (if (class? o#) o# (class o#))
  215
+         sep# "============================================="]
  216
+     (println sep#)
  217
+     (println (str c#))
  218
+     (println "Filters: " '~filters)
  219
+     (println sep#)
  220
+     (let [ms# (members o# ~@filters)]
  221
+       (if (seq ms#)
  222
+         (doseq [m# ms#]
  223
+           (println (str/join " "
  224
+                              [(str/join " " (map name (:modifiers m#)))
  225
+                               (format-member m#)])))
  226
+         (println "No matches.")))
  227
+     (println sep#)))
38  src/mycroft/server.clj
@@ -10,6 +10,7 @@
10 10
   (:require
11 11
    [compojure.route :as route]
12 12
    [mycroft.jmx :as jmx]
  13
+   [mycroft.breadcrumb :as breadcrumb]
13 14
    [mycroft.resources :as resources]
14 15
    [mycroft.data :as data]
15 16
    [mycroft.class :as class]
@@ -49,26 +50,14 @@
49 50
                       "\n\tSession " (:session request)))
50 51
         response))))
51 52
 
52  
-(defn parse-start
53  
-  [x]
54  
-  (try
55  
-   (max (Integer/parseInt x) 0)
56  
-   (catch NumberFormatException e nil)))
57  
-
58 53
 (defn normalize-options
59 54
   "Convert options from string form (as coming in from web)
60 55
    to data structures as needed."
61 56
   [options]
62  
-  (let [options (keywordize-keys options)
63  
-        options (if (:selectors options)
64  
-                  (update-in options [:selectors] read-string)
65  
-                  options)
66  
-        options (if (:start options)
67  
-                  (update-in options [:start] parse-start)
68  
-                  options)
69  
-        options (merge {:start 0} options)]
70  
-    options))
71  
-
  57
+  (-> (keywordize-keys options)
  58
+      (update-in [:selectors] (fnil read-string "nil"))
  59
+      (update-in [:headers] (fnil read-string "nil"))
  60
+      (update-in [:start] (fnil read-string "0"))))
72 61
 
73 62
 (defroutes namespace-routes
74 63
   (GET "/vars" []
@@ -115,16 +104,19 @@
115 104
 
116 105
 (defprotocol Inspector
117 106
   (launch [_])
118  
-  (inspect [_ obj]))
  107
+  (inspect [_ obj options]))
119 108
 
120 109
 (defrecord Instance [port]
121 110
   Inspector
122 111
   (launch [_]
123 112
           (run-jetty (var app) {:port port
124 113
                                 :join? false}))
125  
-  (inspect [_ obj]
126  
-           (if (class? obj)
127  
-             (browse-url (str "http://localhost:" port
128  
-                              "/classes/" (.getName obj)))
129  
-             (browse-url (str "http://localhost:" port
130  
-                              (history/add obj))))))
  114
+  (inspect [_ obj options]
  115
+           (let [query (breadcrumb/options->query-string options)]
  116
+             (if (class? obj)
  117
+               (browse-url (str "http://localhost:" port
  118
+                                "/classes/" (.getName obj) "?"
  119
+                                query))
  120
+               (browse-url (str "http://localhost:" port
  121
+                                (history/add obj) "&"
  122
+                                query))))))

0 notes on commit 7292001

Please sign in to comment.
Something went wrong with that request. Please try again.