Browse Source

Use a typeclass to factor "run" operation

jherve 1 year ago
parent
commit
f966773a56

+ 10 - 9
src/Content.purs

@@ -7,10 +7,11 @@ import Effect (Effect)
 import Effect.Class.Console (logShow)
 import Effect.Console (log)
 import LinkedIn (run, runToDetached)
-import LinkedIn.Page.JobOffer as PageJ
-import LinkedIn.Page.Projects as PageP
-import LinkedIn.Page.Skills as PageS
-import LinkedIn.Page.WorkExperiences as PageWE
+import LinkedIn.Page.JobOffer (JobOfferPage)
+import LinkedIn.Page.Projects (ProjectsPage)
+import LinkedIn.Page.Skills (SkillsPage)
+import LinkedIn.Page.WorkExperiences (WorkExperiencesPage)
+import Type.Proxy (Proxy(..))
 
 main :: Effect Unit
 main = do
@@ -18,9 +19,9 @@ main = do
 
   dom <- getBrowserDom
 
-  run PageWE.query PageWE.extract dom >>= logShow
-  run PageP.query PageP.extract dom >>= logShow
-  run PageS.query PageS.extract dom >>= logShow
-  run PageJ.query PageJ.extract dom >>= logShow
+  run (Proxy :: Proxy WorkExperiencesPage) dom >>= logShow
+  run (Proxy :: Proxy SkillsPage) dom >>= logShow
+  run (Proxy :: Proxy ProjectsPage) dom >>= logShow
+  run (Proxy :: Proxy JobOfferPage) dom >>= logShow
 
-  runToDetached PageJ.query dom >>= logShow
+  runToDetached (Proxy :: Proxy JobOfferPage) dom >>= logShow

+ 14 - 10
src/LinkedIn.purs

@@ -6,28 +6,32 @@ import Data.Either (Either(..))
 import Data.Traversable (class Traversable, traverse)
 import Effect (Effect)
 import LinkedIn.DetachedNode (DetachedNode, toDetached)
+import LinkedIn.Extractible (class Extractible)
+import LinkedIn.Extractible as LE
 import LinkedIn.QueryRunner (QueryError, QueryRunner', runQuery)
 import LinkedIn.UI.Elements.Parser (fromDetachedToUI)
 import LinkedIn.UI.Elements.Types (UIElement)
+import Type.Proxy (Proxy)
 import Web.DOM (Document, Node)
 
-run :: forall t a.
+run :: forall a t.
   Traversable t
-  ⇒ QueryRunner' Document (t Node)
-  -> (t UIElement → Either String a)
+  => Extractible t a
+  => Proxy t
   -> Document
   -> Effect (Either String a)
-run query parse dom = do
-  detached <- runToDetached query dom
-  pure $ extract parse $ toUI detached
+run prox dom = do
+  detached <- runToDetached prox dom
+  pure $ extract LE.extract $ toUI detached
 
-runToDetached :: forall t.
+runToDetached :: forall t a.
   Traversable t
-  ⇒ QueryRunner' Document (t Node)
+  => Extractible t a
+  => Proxy t
   -> Document
   -> Effect (Either QueryError (t DetachedNode))
-runToDetached query dom = do
-  qRes <- doQuery query dom
+runToDetached _ dom = do
+  qRes <- doQuery LE.query dom
   detach qRes
 
 doQuery ∷ ∀ b. QueryRunner' Document b → Document → Effect (Either QueryError b)

+ 10 - 0
src/LinkedIn/Extractible.purs

@@ -0,0 +1,10 @@
+module LinkedIn.Extractible where
+
+import Data.Either (Either)
+import LinkedIn.QueryRunner (QueryRunner')
+import LinkedIn.UI.Elements.Types (UIElement)
+import Web.DOM (Document, Node)
+
+class Extractible t out | t -> out where
+  query :: QueryRunner' Document (t Node)
+  extract ∷ t UIElement → Either String out

+ 7 - 10
src/LinkedIn/Page/JobOffer.purs

@@ -2,17 +2,15 @@ module LinkedIn.Page.JobOffer where
 
 import Prelude
 
-import Data.Either (Either)
 import Data.Foldable (class Foldable, foldMap, foldlDefault, foldrDefault)
 import Data.Generic.Rep (class Generic)
 import Data.Show.Generic (genericShow)
 import Data.Traversable (class Traversable, sequence, traverseDefault)
+import LinkedIn.Extractible (class Extractible)
 import LinkedIn.Jobs.JobOffer (JobOffer)
 import LinkedIn.Jobs.JobOffer as JJO
+import LinkedIn.QueryRunner (subQueryOne)
 import LinkedIn.UI.Components.JobsUnifiedTopCard (JobsUnifiedTopCardElement, queryJobsUnifiedTopCardElement)
-import LinkedIn.QueryRunner (QueryRunner', subQueryOne)
-import LinkedIn.UI.Elements.Types (UIElement)
-import Web.DOM (Document, Node)
 
 data JobOfferPage a = JobOfferPage (JobsUnifiedTopCardElement a)
 
@@ -35,10 +33,9 @@ instance Traversable JobOfferPage where
 
   traverse = \x -> traverseDefault x
 
-query :: QueryRunner' Document (JobOfferPage Node)
-query n = do
-  card <- subQueryOne queryJobsUnifiedTopCardElement "div.jobs-unified-top-card" n
-  pure $ JobOfferPage card
+instance Extractible JobOfferPage JobOffer where
+  query n = do
+    card <- subQueryOne queryJobsUnifiedTopCardElement "div.jobs-unified-top-card" n
+    pure $ JobOfferPage card
 
-extract ∷ JobOfferPage UIElement → Either String JobOffer
-extract (JobOfferPage tabs) = JJO.fromUI tabs
+  extract (JobOfferPage tabs) = JJO.fromUI tabs

+ 9 - 1
src/LinkedIn/Page/Projects.purs

@@ -8,10 +8,11 @@ import Data.Generic.Rep (class Generic)
 import Data.List.Types (NonEmptyList)
 import Data.Show.Generic (genericShow)
 import Data.Traversable (class Traversable, sequence, traverse, traverseDefault)
-import LinkedIn.UI.Components.ArtDecoCard (ArtDecoCardElement, queryArtDecoCard)
+import LinkedIn.Extractible (class Extractible)
 import LinkedIn.Profile.Project (Project)
 import LinkedIn.Profile.Project as PP
 import LinkedIn.QueryRunner (QueryRunner', subQueryMany)
+import LinkedIn.UI.Components.ArtDecoCard (ArtDecoCardElement, queryArtDecoCard)
 import LinkedIn.UI.Elements.Types (UIElement)
 import Web.DOM (Document, Node)
 
@@ -43,3 +44,10 @@ query n = do
 
 extract ∷ ProjectsPage UIElement → Either String (NonEmptyList Project)
 extract (ProjectsPage cards) = traverse PP.fromUI cards
+
+instance Extractible ProjectsPage (NonEmptyList Project) where
+  query n = do
+    cards <- subQueryMany queryArtDecoCard "section.artdeco-card > div ~ div > div > div > ul > li" n
+    pure $ ProjectsPage cards
+
+  extract (ProjectsPage cards) = traverse PP.fromUI cards

+ 8 - 11
src/LinkedIn/Page/Skills.purs

@@ -2,18 +2,16 @@ module LinkedIn.Page.Skills where
 
 import Prelude
 
-import Data.Either (Either)
 import Data.Foldable (class Foldable, foldMap, foldlDefault, foldrDefault)
 import Data.Generic.Rep (class Generic)
 import Data.List.Types (NonEmptyList)
 import Data.Show.Generic (genericShow)
 import Data.Traversable (class Traversable, sequence, traverse, traverseDefault)
-import LinkedIn.UI.Components.ArtDecoTab (ArtDecoTabElement, queryArtDecoTab)
+import LinkedIn.Extractible (class Extractible)
 import LinkedIn.Profile.Skill (Skill)
 import LinkedIn.Profile.Skill as PS
-import LinkedIn.QueryRunner (QueryRunner', subQueryMany)
-import LinkedIn.UI.Elements.Types (UIElement)
-import Web.DOM (Document, Node)
+import LinkedIn.QueryRunner (subQueryMany)
+import LinkedIn.UI.Components.ArtDecoTab (ArtDecoTabElement, queryArtDecoTab)
 
 data SkillsPage a = SkillsPage (NonEmptyList (ArtDecoTabElement a))
 
@@ -36,10 +34,9 @@ instance Traversable SkillsPage where
 
   traverse = \x -> traverseDefault x
 
-query :: QueryRunner' Document (SkillsPage Node)
-query n = do
-  tabs <- subQueryMany queryArtDecoTab "div.artdeco-tabs > div > div > div > div > ul > li" n
-  pure $ SkillsPage tabs
+instance Extractible SkillsPage (NonEmptyList Skill) where
+  query n = do
+    tabs <- subQueryMany queryArtDecoTab "div.artdeco-tabs > div > div > div > div > ul > li" n
+    pure $ SkillsPage tabs
 
-extract ∷ SkillsPage UIElement → Either String (NonEmptyList Skill)
-extract (SkillsPage tabs) = traverse PS.fromUI tabs
+  extract (SkillsPage tabs) = traverse PS.fromUI tabs

+ 8 - 11
src/LinkedIn/Page/WorkExperiences.purs

@@ -2,18 +2,16 @@ module LinkedIn.Page.WorkExperiences where
 
 import Prelude
 
-import Data.Either (Either)
 import Data.Foldable (class Foldable, foldMap, foldlDefault, foldrDefault)
 import Data.Generic.Rep (class Generic)
 import Data.List.Types (NonEmptyList)
 import Data.Show.Generic (genericShow)
 import Data.Traversable (class Traversable, sequence, traverse, traverseDefault)
-import LinkedIn.UI.Components.ArtDecoCard (ArtDecoCardElement, queryArtDecoCard)
+import LinkedIn.Extractible (class Extractible)
 import LinkedIn.Profile.WorkExperience (WorkExperience)
 import LinkedIn.Profile.WorkExperience as PWE
-import LinkedIn.QueryRunner (QueryRunner', subQueryMany)
-import LinkedIn.UI.Elements.Types (UIElement)
-import Web.DOM (Document, Node)
+import LinkedIn.QueryRunner (subQueryMany)
+import LinkedIn.UI.Components.ArtDecoCard (ArtDecoCardElement, queryArtDecoCard)
 
 data WorkExperiencesPage a = WorkExperiencesPage (NonEmptyList (ArtDecoCardElement a))
 
@@ -36,10 +34,9 @@ instance Traversable WorkExperiencesPage where
 
   traverse = \x -> traverseDefault x
 
-query :: QueryRunner' Document (WorkExperiencesPage Node)
-query n = do
-  cards <- subQueryMany queryArtDecoCard "section.artdeco-card > div ~ div > div > div > ul > li" n
-  pure $ WorkExperiencesPage cards
+instance Extractible WorkExperiencesPage (NonEmptyList WorkExperience) where
+  query n = do
+    cards <- subQueryMany queryArtDecoCard "section.artdeco-card > div ~ div > div > div > ul > li" n
+    pure $ WorkExperiencesPage cards
 
-extract ∷ WorkExperiencesPage UIElement → Either String (NonEmptyList WorkExperience)
-extract (WorkExperiencesPage cards) = traverse PWE.fromUI cards
+  extract (WorkExperiencesPage cards) = traverse PWE.fromUI cards

+ 4 - 4
test/ArtDecoCard.purs

@@ -11,15 +11,15 @@ import Data.Maybe (Maybe(..), fromJust)
 import Data.NonEmpty (NonEmpty(..))
 import Data.Traversable (traverse)
 import Effect (Effect)
-import LinkedIn.UI.Components.ArtDeco (ArtDecoCenter(..), ArtDecoCenterContent(..), ArtDecoCenterHeader(..), ArtDecoPvsEntity(..), ArtDecoPvsEntitySubComponent(..))
-import LinkedIn.UI.Components.ArtDecoCard (ArtDecoCardElement(..))
 import LinkedIn.DetachedNode (DetachedNode(..), toDetached)
+import LinkedIn.Extractible (query)
 import LinkedIn.Page.WorkExperiences (WorkExperiencesPage(..))
-import LinkedIn.Page.WorkExperiences as PageWE
 import LinkedIn.Profile.WorkExperience (WorkExperience(..))
 import LinkedIn.Profile.WorkExperience as PWE
 import LinkedIn.QueryRunner (runQuery)
 import LinkedIn.UI.Basic.Types (Duration(..), TimeSpan(..))
+import LinkedIn.UI.Components.ArtDeco (ArtDecoCenter(..), ArtDecoCenterContent(..), ArtDecoCenterHeader(..), ArtDecoPvsEntity(..), ArtDecoPvsEntitySubComponent(..))
+import LinkedIn.UI.Components.ArtDecoCard (ArtDecoCardElement(..))
 import LinkedIn.UI.Elements.Parser (fromDetachedToUI)
 import Node.JsDom (jsDomFromFile)
 import Partial.Unsafe (unsafePartial)
@@ -29,7 +29,7 @@ import Test.Utils (toMonthYear')
 testArtDecoCards :: Effect Unit
 testArtDecoCards = do
   dom <- jsDomFromFile "test/examples/andrew_ng_experiences.html"
-  wep <- runQuery $ PageWE.query dom
+  wep <- runQuery $ query dom
 
   assert $ isRight wep
 

+ 3 - 2
test/JobsUnifiedTopCard.purs

@@ -10,13 +10,14 @@ import Data.NonEmpty (NonEmpty(..))
 import Data.Traversable (traverse)
 import Effect (Effect)
 import LinkedIn.DetachedNode (DetachedNode(..), toDetached)
+import LinkedIn.Extractible (query)
 import LinkedIn.Jobs.JobOffer (JobOffer(..))
 import LinkedIn.Jobs.JobOffer as JJO
-import LinkedIn.UI.Components.JobsUnifiedTopCard (JobsUnifiedTopCardElement(..), TopCardAction(..), TopCardInsight(..), TopCardInsightContent(..), TopCardPrimaryDescription(..), TopCardSecondaryInsight(..))
 import LinkedIn.Page.JobOffer (JobOfferPage(..))
 import LinkedIn.Page.JobOffer as PageJO
 import LinkedIn.QueryRunner (runQuery)
 import LinkedIn.UI.Basic.Types (JobFlexibility(..))
+import LinkedIn.UI.Components.JobsUnifiedTopCard (JobsUnifiedTopCardElement(..), TopCardAction(..), TopCardInsight(..), TopCardInsightContent(..), TopCardPrimaryDescription(..), TopCardSecondaryInsight(..))
 import LinkedIn.UI.Elements.Parser (fromDetachedToUI)
 import Node.JsDom (jsDomFromFile)
 import Partial.Unsafe (unsafePartial)
@@ -26,7 +27,7 @@ main :: Effect Unit
 main = do
   dom <- jsDomFromFile "test/examples/job_offer.html"
 
-  wep <- runQuery $ PageJO.query dom
+  wep <- runQuery $ query dom
 
   assert $ isRight wep