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

dict-compile macro doesn't evaluate symbols #54

Open
lorddoig opened this issue Nov 8, 2014 · 15 comments
Open

dict-compile macro doesn't evaluate symbols #54

lorddoig opened this issue Nov 8, 2014 · 15 comments

Comments

@lorddoig
Copy link

lorddoig commented Nov 8, 2014

I'm sure the title is explanation enough for seasoned macro-writers like yourselves, but for completeness:

(def dict {:en {:greetings {:hello "world"}}})
(def compiled-dict (tower-macros/dict-compile dict))

fails because the library functions that actually compile the dictionary are given the symbol dict instead of the map.

I'm new enough to Clojure that anything but simple macros make my head spin: I've tried and failed to fix this. I know it's easily worked around by just putting the dictionary in another file and passing in a string but I'm filing this anyway as I'm guessing this should be fixed/documented at some point.

@ptaoussanis
Copy link
Member

Hey there, thanks for the suggestion! Could you try [com.taoensso/tower "3.1.0-SNAPSHOT"] (on Clojars now), and let me know if that does the job?

Cheers :-)

@lorddoig
Copy link
Author

lorddoig commented Nov 9, 2014

Hey thanks for the super quick response. Eval! Of course. All those hours watching SICP lectures on youtube were wasted on me apparently...

No success, but for other reasons:

  1. [com.taoensso/tower "3.1.0-SNAPSHOT"] seems to be pulling in the v3.1.0-alpha1 branch instead of dev? This branch is missing your eval patch.
  2. I didn't know this before, but apparently dependency ordering matters to leiningen? How old school. Sente and Tower were competing for Encore and screwing each other up.

Also I see the macro's changed from dict-compile to dict-compile* which caught me out for a minute.

I've only ever had headaches using the leiningen checkouts feature otherwise I'd give that a go. Sorry I can't be more useful.

@ptaoussanis
Copy link
Member

No problem :-)

Eval! Of course. All those hours watching SICP lectures on youtube were wasted on me apparently...

Not at all - Clojure macros don't generally use eval much, actually. It's called only twice in core.clj's ~7.3k loc for example.

This is a bit of an unusual case since we're using a macro to dump the entire compiled dict into the ClojureScript source during ClojureScript compile time. I.e. we don't need the dictionary compilation to actually run under ClojureScript - if that makes sense.

[com.taoensso/tower "3.1.0-SNAPSHOT"] seems to be pulling in the v3.1.0-alpha1 branch instead of dev? This branch is missing your eval patch.

It's possible I didn't upload it correctly - just tried again and it does seem to be up now: https://clojars.org/com.taoensso/tower/versions/3.1.0-SNAPSHOT

I didn't know this before, but apparently dependency ordering matters to leiningen? How old school. Sente and Tower were competing for Encore and screwing each other up.

Ahh, yeah - you can add [com.taoensso/encore "1.15.1"] as a Lein dependency above any other com.taoensso libs and you should be fine.

Also I see the macro's changed from dict-compile to dict-compile* which caught me out for a minute.

Sorry, yeah - there's a number of changes (improvements) pending in the dev branch. It's stable (I'm using it now), but I haven't documented the changes yet.

This is the only breaking change iirc.

Hope that helps, cheers! :-)

@lorddoig
Copy link
Author

lorddoig commented Nov 9, 2014

Awesome reply, thanks man. Alright I've got it, but still no dice.

(some) dependencies:

[org.clojure/clojure "1.6.0"]
[org.clojure/core.async "0.1.346.0-17112a-alpha"]
[org.clojure/clojurescript "0.0-2371"]
[com.taoensso/encore "1.15.1"]
[com.taoensso/sente "1.2.0"]
[com.taoensso/tower "3.1.0-SNAPSHOT"]

i18n.cljx:

(ns myns.i18n
  #+cljs (:require-macros [taoensso.tower :as tower-macros])
  (:require [taoensso.tower :as tower])
  )

(def dict
  {:en
   {:test-msgs
    {:one "One" :two "Two"}
    }
   }
  )

#+clj
(def tconfig
  {:dictionary      "i18n-dict.clj"
   :dev-mode?       true                                    ; FIXME: Make this dynamic
   :fallback-locale :en
   })

#+cljs
(def tconfig
  {:compiled-dictionary (tower-macros/dict-compile* dict)
   :fallback-locale     :en})

(def t (tower/make-t tconfig))

error:

Caused by: clojure.lang.ExceptionInfo: java.lang.RuntimeException: Unable to resolve symbol: dict in this context, compiling:(/private/var/folders/43/b72s05yn3kz96bx_wx66wqbc0000gn/T/form-init4639629360688432507.clj:29:53) at line 29 /.........../i18n.cljs {:tag :cljs/analysis-error, :file "/.........../i18n.cljs", :line 29, :column 25}

I'm not sure how to read this error - does that mean it doesn't know what dict is before it passes it to dict-compile* (suggesting it's my fault), or is it bubbling up from inside dict-compile*? Regarding the former, I can't see how that can be the case: I've inspected the generated i18n.cljs and it clearly defines dict before passing it in to dict-compile*. Just to be safe, and perhaps displaying my lack of understanding of macros, I changed the name of dict to be sure no variable capture/other weirdness was going on but it made no difference.

For the record I've run this (overly safe) command multiple times to the same effect

lein -U do clean, deps, compile

The compile step in my setup is:

  1. Transform CLJX (no error)
  2. Compile project.main (no error)
  3. Compile CLJS (above error)

I hope this helps, if it's something I'm doing wrong I'd be grateful for the heads up.

@ptaoussanis
Copy link
Member

Hi there,

Not really sure what the problem is - just ran a test and it does appear to be working on my end (no CLJS compilation errors). Would be happy to debug it with you, but I'm pretty swamped / on an urgent project atm.

I'd start by checking that the dict var is showing up in your .clj file (since that's where it needs to be for the macro-time dictionary compilation).

As an alternative, I'd recommend just sticking your dictionary in a separate .clj file in your <project.clj-root>/resources path and using

(def tconfig
  {:compiled-dictionary (tower-macros/dict-compile* "my-dictionary.clj")
   :fallback-locale     :en})
;; where `my-dictionary.clj` is at <project.clj-root>/my-dictionary.clj

This has a number of advantages and is the approach I take + tend to recommend. Any particular reason you're hesitant to go that route?

@smbecker
Copy link

smbecker commented Feb 7, 2015

I am getting the same exception as @lorddoig. I found that if I just structure the dict in the same fashion that dict-compile* produces then I can bypass using the macro all together.

So

(def dict
  {:en
   {:test-msgs
    {:one "One" :two "Two"}
    }
   }
  )

becomes

(def dict {
  {:test-msgs/one {:en "One" }}
  {:test-msgs/two {:en "Two" }}})

@ptaoussanis
Copy link
Member

@smbecker Hi Shaun, I'd caution against that approach. Dictionary compilation involves more than just map restructuring (Markdown support & html escaping for example). In any case the compiled dictionary format is an implementation detail, so future versions may break compatibility with your pre-compiled dict. I'd suggest using a resource file if you can.

Hope that helps, cheers!

@smbecker
Copy link

smbecker commented Feb 8, 2015

The basic problem that I am trying to solve is to write a macro that creates an accessor function for each of my localized strings. Since the clojurescript support doesn't support formatting, I have to manually format before passing the args to tower. Also, I am doing language detection on the browser to determine active locale. I don't want the rest of my code to have to worry about either of these details. That is why I was attempting to wrap all of that behind an a macro generated accessor. In order to generate the accessor's, I need access to the dict as well. I'm VERY much a clojure noob so I may be approaching it wrong in the first place (be patient with me). One thing that just occurred to me to try so I can accomplish what I want but still adhere to your caution is to still rely on dict-compile but to save the result of that to a variable the change my macro to be more of post processor rather than pre. Granted, I would still need to take some reliance on how dict-compile structures the output but I don't know any other way unless you have any suggestions.

@okal
Copy link

okal commented Mar 20, 2015

Still seeing this problem. Passing anything but anything but a map literal to dict-compile* results in

Caused by: clojure.lang.ExceptionInfo: java.lang.RuntimeException: Unable to resolve symbol: [name] in this context

@ptaoussanis
Copy link
Member

Still seeing this problem. Passing anything but anything but a map literal to dict-compile* results in

Hi Okal, what version of Tower are you running? I have [com.taoensso/tower "3.1.0-beta2"] in production with a (tower/dict-compile* "i18n-dict.clj") call working as expected.

@okal
Copy link

okal commented Mar 20, 2015

Hm. I'm probably doing it wrong. I assumed it would be able to accept single language translation files of the following form:

;; en.clj
{:page-title "Home"}

I'm attempting to call dict-compile* on individual language file paths.

@okal
Copy link

okal commented Mar 20, 2015

Added it properly, now it works. Thanks 😄

@ptaoussanis
Copy link
Member

Happy to hear you've got it working. What inconsistency do you have in mind though? You should be able to inline a whole dictionary if you want, I just wouldn't normally recommend going that route unless you have a good reason.

@okal
Copy link

okal commented Mar 20, 2015

In CLJ, I can have arbitrary depth nesting in the lang maps, but that doesn't seem to be the case with CLJS. So, I can do (translate :sw :top-level/mid-level/key), but I can only go as far as the second level in CLJS. That arbitrary depth nesting isn't something I found documented, just a convenience I stumbled upon. I edited it out from the comment for that reason. It's irrelevant to this thread.

To illustrate:

{:main-navigation {:dropdown {:profile-link-text "Profile}}}

:main-navigation/dropdown/profile-link text is accessible from CLJ but not CLJS.

@ptaoussanis
Copy link
Member

Cljs absolutely also supports arbitrary-depth nesting, but you must use valid keyword syntax: so :main-navigation.dropdown/profile-link. Keywords can have at most one "/" to separate the namespace and name parts (you can use as many .'s as you like).

Not sure if that's the specific issue you're running into, but it wouldn't surprise me if that keyword form were getting rejected in Cljs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants