Upgrade to Pro — share decks privately, control downloads, hide ads and more …

同僚の登壇資料作成をScalaで手伝った話 / ScalaMatsuri 2019 LT

同僚の登壇資料作成をScalaで手伝った話 / ScalaMatsuri 2019 LT

bakenezumi

June 28, 2019
Tweet

Other Decks in Technology

Transcript

  1. addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.1") $ sbt sbt:glaze> dependencyBrowseGraphHtml use

    sbt-dependency-graph it can create dot script and HTML with dependencyBrowseGraphHtml command plugins/plugins.sbt sbt-dependency-graphを利用 https://github.com/jrudolph/sbt-dependency-graph dependencyBrowseGraphHtmlコマンドでdotスクリプトとHTMLが作れる
  2. 欲しかったもの Wanted List of entity classes, List of value object

    classes - エンティティクラスの一覧 - 値オブジェクトクラスの一覧
  3. REPL で以下を実行 import collection.JavaConverters._ val isConcrete = (clz: Class[_]) =>

    !clz.isInterface && !java.lang.reflect.Modifier.isAbstract(clz.getModifiers) val getTopLevelClassesRecursive = (pkg: String) => com.google.common.reflect.ClassPath.from(Thread.currentThread().getContextClassLoader()).getTopLevelClassesRe cursive(pkg).stream().map[Class[_]](_.load()).collect(java.util.stream.Collectors.toSet()).asScala.filter(isC oncrete) val myDomainClasses = Set("glaze.kernel.domain", "glaze.progad.domain").flatMap(getTopLevelClassesRecursive) val getMemberNames = (clz: Class[_]) => (clz.getDeclaredMethods.map(_.getName) ++ clz.getDeclaredFields.map(_.getName)).toSet val hasId = (clz: Class[_]) => getMemberNames(clz).exists(_ == "id") val partitionIntoEntityAndValueObject = (classes: Set[Class[_]]) => classes.partition(hasId) val (entities, valueObjects) = partitionIntoEntityAndValueObject(myDomainClasses) val format = (clz: Set[Class[_]]) => clz.map(_.getSimpleName).toSeq.sorted.mkString(", ") println(format(entities)) println(format(valueObjects)) run with REPL
  4. 実行結果 scala> println(format(entities)) Ad, Advertiser, AdvertiserListOwner, App, AppList, AppListMeta, AudienceIdentifierList,

    AudienceIdentifierListMeta, Audit, Banner, Bid, BidRequest, BidResponse, BudgetAccount, Campaign, CompleteCandidate, Content, Conversion, FixedPricingBidAgentConfig, Imp, InsertionOrder, LineItem, PartialCandidate, Producer, Publisher, Segment, Site, StandardBidAgentConfig, StandardBidRequest, SystemListOwner scala> println(format(valueObjects)) AcceptedActivity, Activity, ActivitySet, Ad, AdPreviewEndpoint, AdSuite, AdTypeDescriptor, Adjust, AdsDigest, AggregatePeriod, AggregateSpending, AggregatedFrequency, AggregationGroup, AndThenAdFilter, AndThenCandidateFilter, AndroidAppId, AppDescriptor, AppListEntry, AppTargeting, AppsFlyer, AssetPath, AtomicFeature, AttachmentPoint, AttributionId, Audience, AudienceDescriptor, AudienceIdentifierListEntry, AudienceProto, AudienceStats, AudienceTargeting, AudioPlacement, BidAgentConfigured, BidContext, BidDeliveryTracking, BidPayload, BidProto, BidResponse, BitSetDescriptorSet, Bitrate, BlankPayload, BlankProto, BoundAssets, BuiltinMeasurement, CallToAction, CandidateEvaluation, CandidateHeader, Candidates, CapTarget, ClickDeliveryTracking, ClickMeasurementTracking, ClickPayload, ClickProto, ClickThroughRate, ClickTracker, CompleteRichCreative, ContentCategoryDescriptor, ConversionEvent, ConversionRate, CoproductUrl, CostPerAcquisition, CostPerClick, CostPerView, CostRate, Cpm, Credit, CustomerActionMeasurementTracking, CustomerActionPayload, CustomerActionProto, DelayedFeedback, DeliveryBoundEndpoints, DeliveryContext, DeliveryEvent, DeliveryEventEncoder, DeliveryEventProto, DeliveryUnboundEndpoints, DeniedActivity, Device, DeviceProto, Dimension, DisplayPlacement, DomainDescriptor, DurationRange, EnvironmentDescriptor, EnvironmentTargeting, EventForAggregate, ExchangeDescriptor, ExtensionTrackingType, FactorizationMachine, Execution result
  5. 解説① // JavaのSetからScalaのSetに変換するために必要 import collection.JavaConverters._ // 具象クラスか判定する val isConcrete =

    (clz: Class[_]) => !clz.isInterface && !java.lang.reflect.Modifier.isAbstract(clz.getModifiers) // 指定パッケージ配下の具象クラスをClassPathから取得する。aws-sdkがguavaを使っていたのでそれを利用 val getTopLevelClassesRecursive = (pkg: String) => com.google.common.reflect.ClassPath.from(Thread.currentThread() .getContextClassLoader()).getTopLevelClassesRecursive(pkg).stream() .map[Class[_]](_.load()).collect(java.util.stream.Collectors.toSet()) .asScala.filter(isConcrete) // ドメインレイヤのパッケージに含まれる具象クラス val myDomainClasses = Set("glaze.kernel.domain", "glaze.progad.domain").flatMap(getTopLevelClassesRecursive)
  6. 解説② // クラスのメンバー名を取得 val getMemberNames = (clz: Class[_]) => (clz.getDeclaredMethods.map(_.getName)

    ++ clz.getDeclaredFields.map(_.getName)).toSet // "id" をメンバーにもつか。エンティティの判定に使う val hasId = (clz: Class[_]) => getMemberNames(clz).exists(_ == "id") // クラス群をエンティティとそれ以外(バリューオブジェクト)に分割 val partitionIntoEntityAndValueObject = (classes: Set[Class[_]]) => classes.partition(hasId) val (entities, valueObjects) = partitionIntoEntityAndValueObject(myDomainClasses) val format = (clz: Set[Class[_]]) => clz.map(_.getSimpleName).toSeq.sorted.mkString(", ") println(format(entities)) println(format(valueObjects))
  7. おまけ ((pkgs: Set[String]) => (format: Set[Class[_]] => String) => (effect:

    String => Unit) => (((_: Set[Class[_]]).partition(clz => (clz.getDeclaredMethods.map(_.getName) ++ clz.getDeclaredFields.map(_.getName)).toSet.exists(_ == "id")).productIterator.map(_.asInstanceOf[Set[Class[_]]]).map(format))(pkgs.flatMap(pkg => collection.JavaConverters.asScalaSetConverter(com.google.common.reflect.ClassPath.from(Thread.curr entThread().getContextClassLoader()).getTopLevelClassesRecursive(pkg).stream().map[Class[_]](_.loa d()).collect(java.util.stream.Collectors.toSet())).asScala.filter(clz => !clz.isInterface && !java.lang.reflect.Modifier.isAbstract(clz.getModifiers))))).foreach(effect))(Set("glaze.kernel.do main", "glaze.progad.domain"))(_.map(_.getSimpleName).toSeq.sorted.mkString(", "))(println) 一文で by one sentence