Giter VIP home page Giter VIP logo

Comments (16)

realdadfish avatar realdadfish commented on September 24, 2024 1

Thanks for the quick reply!

Yes, the tagging idea kind of resembles my naive "handle" idea, but better. The tag could mark two subtrees B1 and B2 of root A to be of the same "kind", essentially letting instances live in B's, and only inside B's, not inside A's. They would act as an upper boundary for TOP_MOST scoping then. I like the idea, because it would not give up the concept of automatic "up-leveling" of instances that is already existing, but would rather define a limit up to which scope an instance can up-level.

And yes, the visitor interface would be more than sufficient. One could easily create any kind of visualization on top of that, be it a logging output, a graphviz graph or anything else. For a continuous output I think the only missing piece would be a pingback mechanism that would tell the visitor to re-scan the graph, in case new scopes are created or existing scopes are disposed. I am thinking here of the evaluation of Lazy injections that potentially postpone the creation of subgraphs inside a scope until they're actually used. I don't know how feasible this is though, because we probably don't want events for each and every single instantiated object.

from magnet.

realdadfish avatar realdadfish commented on September 24, 2024 1

It's planned in, hope I get to it next week. We're just a few days before a release...

from magnet.

realdadfish avatar realdadfish commented on September 24, 2024

And yes, I acknowledge there is a "Help wanted" section exactly about my third option, but I'd start low and would at first try to add the needed interfaces / machinery in Magnet itself to get this information during runtime before talking about fancy visualizations :)

from magnet.

sergejsha avatar sergejsha commented on September 24, 2024

I would split this into two separate topics to follow up:

  • (a) Print out current scope tree at runtime, starting from given root scope
  • (b) Limit Scoping.TOP_MOST scoping in a way, that instances created without any dependencies get allocated in a given scope (at most) instead of being allocated right in the root scope.

For (a) I could think of a simple visitor interface for visiting all scopes and their instances starting from the root scope. Something simple like this:

interface ScopeVisitor {
    fun onEnter(scope: Scope, parent: Scope?)
    fun onInstance(instance: Instance)
    fun onExit(scope: Scope)
}

interface Instance {
    val scoping: Scoping
    val classifier: String
    val type: Class<Any>
    val value: Any
}

interface RootScope {
    fun accept(visitor: ScopeVisitor)
}

Notes:

  • Scope visiting will implement breadth-first search to "scan" scopes level-by-level.
  • Because the same instance can be registered multiple times with different types in the same scope, it will be visited multiple times with different types too. This means there can be multiple instances of Instance type with the same value.

For (b) I was thinking implementing tagged scopes. Imaging a tag to be a string or a custom annotation which can be added to a scope and then used as scoping attribute while declaring instances. An instance declared with this "tagged TOP_MOST" scoping will be allocated in that scope or in one of its children, but never in its parent, including the root. Regarding the error handling, we can either fail at injection if tagged scope cannot be found or use root scope as fallback. It can be even configurable, up to us.

What do you think?

p.s. As a temporal workaround you can use any of the options you described. I cannot offer anything better than that for now.

from magnet.

sergejsha avatar sergejsha commented on September 24, 2024

Right. What is more important for you for now, (a) or (b)?

from magnet.

realdadfish avatar realdadfish commented on September 24, 2024

I'd say having a debugging mechanism at hand would be more important for me, because it would give me insights for a variety of potential issues.

from magnet.

sergejsha avatar sergejsha commented on September 24, 2024

Use #91 to follow up on topic (a) Print out current scope tree at runtime, starting from given root scope.

This issue will be used to follow up on topic (b) Limit Scoping.TOP_MOST scoping in a way, that instances created without any dependencies get allocated in a given scope (at most) instead of being allocated right in the root scope.

from magnet.

sergejsha avatar sergejsha commented on September 24, 2024

Proposal

Changes:

  • Scope gets new String property called limit.
  • Instance gets new String property called limitedTo.

Behavoir:

  • Scope.limit(limitName: String) can be assigned just once.
  • Instance.limitedTo(limitName: String) can only be used with Scoping.TOPMOST.
  • Scope resolution uses TOPMOST algorithm but its topmost scope is the either limiting scope with given limitName value or the root if limiting scope couldn't be found.

Example:
In the configuration shown below, when requesting an instance from scope [4]

  • if instance has no dependencies, it will be allocated in scope [2]
  • if instance has a dependency residing in scopes [3] or [4], the instance will be allocated in [3] or [4] respecting its dependency.
  • Not matching limit in scope [3] is ignored.

Screenshot 2019-07-01 at 18 45 23

@realdadfish What do you think?

from magnet.

realdadfish avatar realdadfish commented on September 24, 2024

Looks good to me!

from magnet.

sergejsha avatar sergejsha commented on September 24, 2024

@realdadfish I decided to release everything in 3.3 because it look quite ready and stable now. Scope limiting can be tried out with 3.3-rc3 which is on the way.

Usage:

// scope
const val CATALOG = "catalog"
scope.limit(CATALOG)

// instances
@Instance(type = CatalogPage::class, limit = CATALOG)
internal class LibraryCatalogPage : CatalogPage {}

@Instance(type = CatalogPage::class, limit = CATALOG)
internal class DeviceCatalogPage : CatalogPage {}

Stetho-scope will print something like that

[3] <catalog> magnet.internal.MagnetScope@d943481
   BOUND View androidx.constraintlayout.motion.widget.MotionLayout{cdfd926 V.E..... ........ 0,0-1080,1794 #7f080057 app:id/catalogContainer}
   BOUND Fragment CatalogFragment{8dd3967 (5b35ba0e-8db5-4106-97cb-3ac9c9395c11) id=0x7f0800c0}
   BOUND FragmentManager FragmentManager{3a590314 in CatalogFragment{8dd3967}}
   BOUND LifecycleOwner CatalogFragment{8dd3967 (5b35ba0e-8db5-4106-97cb-3ac9c9395c11) id=0x7f0800c0}
   TOPMOST CatalogView de.halfbit.a3.catalog.DefaultCatalogView@2c4e97bd
   TOPMOST CatalogViewBinder de.halfbit.a3.catalog.DefaultCatalogViewBinder@28e061b2
   <catalog> CatalogPage de.halfbit.a3.catalog.library.LibraryCatalogPage@6fa0503
   <catalog> CatalogPage de.halfbit.a3.catalog.device.DeviceCatalogPage@94d3c80

Enjoy!

from magnet.

realdadfish avatar realdadfish commented on September 24, 2024

Perfect, thank you!

from magnet.

sergejsha avatar sergejsha commented on September 24, 2024

The only missing part is to track "unscoped" instances in a weak-scope in debug. That would give the full picture then.

Please let me know whether it worked for you, that I can make a release. Thanks!

from magnet.

realdadfish avatar realdadfish commented on September 24, 2024

I'm currently in the process of implementing this to get my activity / fragment scopes clean.

Scope resolution uses TOPMOST algorithm but its topmost scope is the either limiting scope with given limitName value or the root if limiting scope couldn't be found.

Would it be possible to fail the instantiation if no topmost scope with a given limit name is found? Wouldn't otherwise "leak" components that are fetched from a "higher scope" into that scope, instead of being fixed to the top-most limiting scope they define?

val rootScope = Magnet.createRootScope()
val childScope = rootScope.createSubscope { limit("foo") }

@Instance(type = Foo::class, limit = "foo")
class Foo

rootScope.getSingle(Foo::class.java)  // <-- this should fail IMHO

Also, I kind of liked limitedTo instead of limit on the @Instance annotation. What made you change it?

from magnet.

sergejsha avatar sergejsha commented on September 24, 2024

3.3-rc4 with "required" limiting is on the way.

Also, I kind of liked limitedTo instead of limit on the @instance annotation. What made you change it?

limitedTo sounds stronger than just limit. Now, when limit is required, limitedTo makes sense. Changed.

from magnet.

realdadfish avatar realdadfish commented on September 24, 2024

from magnet.

sergejsha avatar sergejsha commented on September 24, 2024

from magnet.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.