Regarding purging... I doubt you really need to but perhaps I'm wrong.
Certainly could be premature optimisation unless you really have memory
footprint problems.

On Tue, Feb 17, 2015 at 4:29 AM, Scott Nelson <[email protected]> wrote:

> Thanks for the suggestions.
>
> So it sounds like in the my case the state is just a normalized store of
> artists, albums, and songs.  Some portion of the app state would look
> something like:
>
>     {:artists [{:id 1
>                 :name "The Beatles"}]
>      :albums [{:id 1
>                :artist-id 1
>                :name "Abbey Road"}
>               {:id 2
>                :artist-id 1
>                :name "Rubber Soul"}]
>      :songs [{:id 1
>               :album-id 1
>               :name "Come Together"}
>              {:id 2
>               :album-id 1
>               :name "Something"}
>              {:id 3
>               :album-id 2
>               :name "Drive My Car"}
>              {:id 4
>               :album-id 2
>               :name "Norwegian Wood"}]}
>
> The :current-artist could actually then just reference
> artists/ablums/songs and the various components could derive their state:
>
> {:current-artist {:id 1
>                   :albums [{:id 1
>                             :songs [{:id 1}
>                                     {:id 2}]}
>                             {:album-id 2
>                              :songs [{:id 3}
>                                      {:id 4}]}]}}
>
> Similarly, the player component could derive state from references in the
> :playlist portion of the app state:
>
> {:play-index 2
>  :songs [{:id 1}
>          {:id 2}
>          {:id 3}
>          {:id 4}]}
>
> Am I on the right track here?
>
> As the user navigates around to various artist pages there will be API
> requests being made and new artist/album/song data will be added to the
> "store" I mentioned above.  I think the hard part now would be knowing when
> data is no longer needed and can be purged from the store.  Any advice for
> this?  I'm imagining the need to scan through the store and delete items
> that are not referenced by the :current-artist or :playlist.  As other
> areas of the application begin to reference this shared data the cleanup
> process would become more and more coupled to those areas.
>
> On Monday, February 16, 2015 at 6:20:11 AM UTC-5, Oliver George wrote:
> > Hey Leon
> >
> >
> > It was really a matter of practicalities.
> >
> >
> > There doesn't seem to be hook to inject derived state into OM cleanly
> just now.  I have tried various other approaches and my code was much less
> managable.  This approach is proving really convenient.
> >
> >
> > One really strong win with this approach is that derived state happens
> before OM snapshots state to decide what's changed.  Before I had this I
> had the issue of having to manually observe many paths to ensure that
> components updated.  Now it just happens.  You might need to have
> experienced the pain to fully appreciate that.  It's big.
> >
> >
> > The change to OM would be small but I figure it's better to prove the
> approach in some code rather than ask for extensions.  I think the change
> would be either a new interface so that OM can derive state when fetching
> the atom's value.  Somewhere around here is the deref I needed to inject my
> derived state.
> >
> >
> > As for memoize-last.  Well, memoize seems like a memory leak which
> worried me in a browser... no way to clear out old cached info.  CLJS
> doesn't seem have a LRU memo function just yet (at least I couldn't get
> core.memoize to work).  memoize-last just happens to fit perfectly with
> this particular use case.
> >
> >
> >
> >
> > On Mon, Feb 16, 2015 at 9:16 PM, Leon Grapenthin <[email protected]>
> wrote:
> > Can you elaborate why you implement a new IDeref? Why not just calculate
> and pass the derived state during rendering? Memoization in any fashion
> could still happen.
> >
> >
> >
> > Also, is memoize-last just intended as an optimzation to consume less
> memory, or does it serve another purpose?
> >
> >
> >
> > On Sunday, February 15, 2015 at 11:58:17 PM UTC+1, Oliver George wrote:
> >
> > > My approach to this stuff is to think in terms of state and derived
> state.
> >
> > >
> >
> > >
> >
> > >
> >
> > > I have a few helpers to facilitate implementing that which amounts to
> >
> > > Use an atom for stateUse a function to derive stateUse a simple cache
> to reduce load of derivation in render loopHack deref to make atom pass
> back derived-state
> >
> >
> >
> > > If you're new to OM I'd encourage you not to get carried away with
> picking up dodgy bits of code like the ones I'm sharing here until you're
> confident you're making best use of OM as it comes out of the box.
> >
> > >
> >
> > >
> >
> > > That being said...
> >
> > >
> >
> > >
> >
> > > Here's my derived-atom! function.
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > (defn derived-atom!
> >
> > >
> >
> > >
> >
> > >   "
> >
> > >
> >
> > >
> >
> > >   This allows us to access derived values associated with atom state.
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >   The implementation of atom does not use deref so we aren't
> interfering
> >
> > >
> >
> > >
> >
> > >   with how state transitions, they use .-state to access the pure
> attribute
> >
> > >
> >
> > >
> >
> > >   value.
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >   The implementation of om ref cursors use deref to resolve the
> current value
> >
> > >
> >
> > >
> >
> > >   of state so we are able to apply our logic and have the result
> available
> >
> > >
> >
> > >
> >
> > >   via (value c) which means reference cursors will trigger updates
> based
> >
> > >
> >
> > >
> >
> > >   on changes to derived data too.
> >
> > >
> >
> > >
> >
> > >   "
> >
> > >
> >
> > >
> >
> > >   [iref derived-fn]
> >
> > >
> >
> > >
> >
> > >   (specify! iref
> >
> > >
> >
> > >
> >
> > >             IDeref (-deref [_] (derived-fn (.-state iref))))
> >
> > >
> >
> > >
> >
> > >   iref)
> >
> > >
> >
> > >
> >
> > >
> >
> > > This is how i used it as my om app-state
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > (defonce app-state (derived-atom! (atom {}) (memoize-last
> derived-state)))
> >
> > >
> >
> > >
> >
> > >
> >
> > > memoize-last is a cutdown version of memoize which just remembers the
> last version.  That essentially means that we only calculate derived-state
> once for each OM state
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > (defn memoize-last
> >
> > >
> >
> > >
> >
> > >   "Returns a memoized version of a referentially transparent function.
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >   The memoized version of the function keeps a cache of the *most
> recent call*
> >
> > >
> >
> > >
> >
> > >   from arguments to result.
> >
> > >
> >
> > >
> >
> > >   "
> >
> > >
> >
> > >
> >
> > >   [f]
> >
> > >
> >
> > >
> >
> > >   (let [mem (atom {})
> >
> > >
> >
> > >
> >
> > >         lookup-sentinel (js-obj)]
> >
> > >
> >
> > >
> >
> > >     (fn [& args]
> >
> > >
> >
> > >
> >
> > >       (let [v (get @mem args lookup-sentinel)]
> >
> > >
> >
> > >
> >
> > >         (if (identical? v lookup-sentinel)
> >
> > >
> >
> > >
> >
> > >           (let [ret (apply f args)]
> >
> > >
> >
> > >
> >
> > >             (reset! mem {args ret})
> >
> > >
> >
> > >
> >
> > >             ret)
> >
> > >
> >
> > >
> >
> > >           v)))))
> >
> > >
> >
> > >
> >
> > >
> >
> > > Finally, here's me using derived state logic to do things like
> lookups, validation and other things.  It's all very app specific but
> amounts to doing (assoc-in state [:a :b] :something) a lot.  At this risk
> of confusing things here's my generalised required field validation logic
> which is implemented as derived state.
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > (def empty-values #{nil "" [] {} #{}})
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > (defn validate-required-field [field]
> >
> > >
> >
> > >
> >
> > >   (let [{:keys [required value errors]} field]
> >
> > >
> >
> > >
> >
> > >     (if (and required (contains? empty-values value))
> >
> > >
> >
> > >
> >
> > >       (assoc field :errors (conj errors "This field is required"))
> >
> > >
> >
> > >
> >
> > >       field)))
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > (defn is-required-field?
> >
> > >
> >
> > >
> >
> > >   "Identifies walker nodes which are fields relevant to require logic"
> >
> > >
> >
> > >
> >
> > >   [m]
> >
> > >
> >
> > >
> >
> > >   (and (map? m)
> >
> > >
> >
> > >
> >
> > >        (contains? m :required)
> >
> > >
> >
> > >
> >
> > >        (contains? m :value)))
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > (defn validate-required-fields
> >
> > >
> >
> > >
> >
> > >   "Derive errors associated with missing required fields"
> >
> > >
> >
> > >
> >
> > >   [state]
> >
> > >
> >
> > >
> >
> > >   (postwalk
> >
> > >
> >
> > >
> >
> > >     #(if (is-required-field? %) (validate-required-field %) %)
> >
> > >
> >
> > >
> >
> > >     state))
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > (defn derived-state
> >
> > >
> >
> > >
> >
> > >   "Used to include derived state for use by components."
> >
> > >
> >
> > >
> >
> > >   [state] (-> state
> >
> > >
> >
> > >
> >
> > >               ;derive-vertical-required
> >
> > >
> >
> > >
> >
> > >               ;end-position-logic
> >
> > >
> >
> > >
> >
> > >               ;maint-freq-logic
> >
> > >
> >
> > >
> >
> > >               validate-required-fields))
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> >
> >
> > > On Mon, Feb 16, 2015 at 4:17 AM, Leon Grapenthin <[email protected]>
> wrote:
> >
> > > If they were changing, you could implement your own reference system,
> where you have one stateful lookup map that you make globally available via
> a ref cursor. Instead of the actual changing values, you'd use lookup keys
> in your rendered cursors. Changes to values in the stateful lookup map
> would would reflect everywhere they are looked up.
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > On Sunday, February 15, 2015 at 3:54:36 PM UTC+1, Scott Nelson wrote:
> >
> > >
> >
> > > > I’m working on an Om application that includes a music browser and
> player with a simple artist -> albums -> songs hierarchy.  When a user is
> viewing an artist section I layout all the artist’s albums and songs
> hierarchically and this works great since there is basically a 1-to-1
> correspondence between the component hierarchy and the data hierarchy.
> >
> > >
> >
> > > >
> >
> > >
> >
> > > > Here’s a basic example of what a piece of the application state
> might look like under some sort of :current-artist key:
> >
> > >
> >
> > > >
> >
> > >
> >
> > > >     {:name “The Beatles”
> >
> > >
> >
> > > >      :albums [{:name “Abbey Road”,
> >
> > >
> >
> > > >                :songs [{:name “Come Together”}
> >
> > >
> >
> > > >                        {:name “Something”}]}
> >
> > >
> >
> > > >               {:name "Rubber Soul",
> >
> > >
> >
> > > >                :songs [{:name "Drive My Car"}
> >
> > >
> >
> > > >                        {:name "Norwegian Wood"}]}]}
> >
> > >
> >
> > > >
> >
> > >
> >
> > > > When a user plays one of these songs I build an ordered playlist of
> all the artist’s songs.  There is a player component that plays the
> playlist (even if the user navigates away from the current artist section)
> and displays the currently playing artist, album and song name.  To achieve
> this I ended up duplicating a bunch of artist/album/song data elsewhere in
> the application state.
> >
> > >
> >
> > > >
> >
> > >
> >
> > > > Here’s what the :playlist path of the application state might look
> like after a user clicked the play button next to “Drive My Car”:
> >
> > >
> >
> > > >
> >
> > >
> >
> > > >     {:play-index 2
> >
> > >
> >
> > > >      :songs [{:name “Come Together”
> >
> > >
> >
> > > >               :album {:name “Abbey Road”
> >
> > >
> >
> > > >                       :artist {:name "The Beatles"}}}
> >
> > >
> >
> > > >              {:name “Something”
> >
> > >
> >
> > > >               :album {:name “Abbey Road”
> >
> > >
> >
> > > >                       :artist {:name "The Beatles"}}}
> >
> > >
> >
> > > >              {:name “Drive My Car”
> >
> > >
> >
> > > >               :album {:name “Rubber Soul”
> >
> > >
> >
> > > >                       :artist {:name "The Beatles"}}}
> >
> > >
> >
> > > >              {:name “Norwegian Wood”
> >
> > >
> >
> > > >               :album {:name "Rubber Soul"
> >
> > >
> >
> > > >                       :artist {:name "The Beatles"}}}]}
> >
> > >
> >
> > > >
> >
> > >
> >
> > > > I think this sort of denormalization works fine in my case since the
> artist/album/song data is not changing but I'm wondering if there is a
> better way to accomplish this.  If the data were changing then I would
> imagine it could become a challenge to keep the playlist data in sync.
> >
> > >
> >
> > >
> >
> > >
> >
> > > --
> >
> > >
> >
> > > Note that posts from new members are moderated - please be patient
> with your first post.
> >
> > >
> >
> > > ---
> >
> > >
> >
> > > You received this message because you are subscribed to the Google
> Groups "ClojureScript" group.
> >
> > >
> >
> > > To unsubscribe from this group and stop receiving emails from it, send
> an email to [email protected].
> >
> > >
> >
> > > To post to this group, send email to [email protected].
> >
> >
> >
> > >
> >
> > > Visit this group at http://groups.google.com/group/clojurescript.
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> > > --
> >
> > >
> >
> > >
> >
> > > Oliver George
> >
> > > Director, Condense
> >
> > > 0428 740 978
> >
> >
> >
> > --
> >
> > Note that posts from new members are moderated - please be patient with
> your first post.
> >
> > ---
> >
> > You received this message because you are subscribed to the Google
> Groups "ClojureScript" group.
> >
> > To unsubscribe from this group and stop receiving emails from it, send
> an email to [email protected].
> >
> > To post to this group, send email to [email protected].
> >
> > Visit this group at http://groups.google.com/group/clojurescript.
> >
> >
> >
> >
> >
> > --
> >
> >
> > Oliver George
> > Director, Condense
> > 0428 740 978
>
> --
> Note that posts from new members are moderated - please be patient with
> your first post.
> ---
> You received this message because you are subscribed to the Google Groups
> "ClojureScript" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at http://groups.google.com/group/clojurescript.
>



-- 
Oliver George
Director, Condense
0428 740 978

-- 
Note that posts from new members are moderated - please be patient with your 
first post.
--- 
You received this message because you are subscribed to the Google Groups 
"ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/clojurescript.

Reply via email to