Git Product home page Git Product logo

breadboard's People

Contributors

andrewalker avatar autarch avatar bobtfish avatar book avatar dakkar avatar die-tech avatar doy avatar haarg avatar jasonmay avatar jhannah avatar jrockway avatar kaoru avatar lecstor avatar manwar avatar neilb avatar rafl avatar stevan avatar szabgab avatar ubu avatar xenoterracide avatar yanick avatar zostay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

breadboard's Issues

#53 breaks backwards-compat

We have a number of modules that extend the Bread::Board DSL by doing:

    Moose::Exporter->setup_import_methods(
        ...
        also  => 'Bread::Board',
    );

If there's not a compelling reason for #53, can you please revert it?

Thanks!

Dist::Zilla dependency issues - Dist::Zilla::Plugin::Test::ReportPrereqs

I managed to successfully run dzil authordeps --missing | cpanm on my Perl 5.18.4 perlbrew and somehow got into a weird state.

$ dzil authordeps --missing
[... no output ...]

But then...

$ dzil listdeps             
Required plugin Dist::Zilla::Plugin::Test::ReportPrereqs isn't installed.

Run 'dzil authordeps' to see a list of all required plugins.
You can pipe the list to your CPAN client to install or update them:

    dzil authordeps --missing | cpanm

Any ideas what's up with that?

On top of that, if I do install Dist::Zilla::Plugin::Test::ReportPrereqs manually things don't get any better.

$ cpanm Dist::Zilla::Plugin::Test::ReportPrereqs
[...]
1 distribution installed

But then...

$ dzil listdeps                                 
fatal: ambiguous argument 'releases...master': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]' at /home/alex/perl5/perlbrew/perls/perl-5.18.4/lib/site_perl/5.18.4/Dist/Zilla/Plugin/ChangeStats/Git.pm line 61.

Any ideas? If I can't get the dependencies installed and dzil running then working on #47 is going to be tricky.

Thanks,

Alex

The "Null" lifecycle feels like a hack

If your service has a lifecycle applied, you can remove it by setting $service->lifecycle('Null'). This feels wrong (also, the way we remove a role by reblessing into the first superclass is probably fragile, but that's for another day).
Should I write a (essentially empty) Bread::Board::Lifecycle::Prototype (to use the name from the manual) and document it? That way we can remove the special-cased name.

SetterInjection has hard-coded constructor name

ConstructorInjection uses the MOP (plus override) to find the constructor to call, but SetterInjection always calls new. Should I factor out the constructor_name attribute and use it in both injections?

Make it possible to track whether a service has been instantiated?

I was working on some code recently where I had a container for my test code that could be used to create a temporary database for fixtures. The database should be removed at the end of each test run, or else I end up with a million databases named "Foo_12345".

However, it's possible some tests would never resolve the fixtures service, so the database would never be created. I tried to get the database object to clean up after itself on DEMOLISH, but the unpredictability of Perl's global destruction order make this really difficult (or plain impossible).

I have a test runner that wraps my TCM classes that is perfectly positioned to do this, though. It can run the tests and then check if the container resolved the fixtures. If so, it disconnects the database handle (also made by the container), then drops the test database.

I made this all work by just wrapping the Bread::Board::Container in my own object that has its own resolve method that just tracks what strings it sees. It'd be a lot nicer if it knew that '/foo' and 'foo' were equivalent services though, which is why I'd like to see this in BB itself. Something like a $container->resolved_service or ->service_was_resolved or something like that.

The parameterized Log::Dispatch example does not work as written

The last bit simply cannot work, because of the way that a container is named when generated from parameters:

my $ld = $c->resolve( service => 'Logging/Logger' );

This will cause an error - "Could not find container or service for Logging in Logging|Outputs at lib/Bread/Board/Traversable.pm line 104."

That's cause the name of the container is Logging|Outputs, not Logging.

Obviously, fixing this is easy, but I find the container naming a little gross. Is it possible to fix that so it doesn't include the parameter names? I can't see a good reason to include those in the name, but I don't know enough about Bread::Board to say for sure.

Add an equivalent to dep(value => ...) to Bread::Board

I'm not a fan of Bread::Board::Declare, but I've often found myself wanting something like dep(value => ...) when using Bread::Board. It would be nice to be able to quickly specify anonymous literals to service dependencies.

Would it be possible to add a literal() or value() function to Bread::Board to create anonymous Bread::Board::Literal services?

Add abstract to modules

Those modules are missing an ABSTRACT line like:

package Foo;
# ABSTRACT: does the thing

  • lib/Bread/Board/BlockInjection.pm
  • lib/Bread/Board/ConstructorInjection.pm
  • lib/Bread/Board/Container.pm
  • lib/Bread/Board/Container/FromParameterized.pm
  • lib/Bread/Board/Dependency.pm
  • lib/Bread/Board/Dumper.pm
  • lib/Bread/Board/LifeCycle.pm
  • lib/Bread/Board/LifeCycle/Singleton.pm
  • lib/Bread/Board/LifeCycle/Singleton/WithParameters.pm
  • lib/Bread/Board/Literal.pm
  • lib/Bread/Board/Service.pm
  • lib/Bread/Board/Service/Alias.pm
  • lib/Bread/Board/Service/Deferred.pm
  • lib/Bread/Board/Service/Deferred/Thunk.pm
  • lib/Bread/Board/Service/Inferred.pm
  • lib/Bread/Board/Service/WithClass.pm
  • lib/Bread/Board/Service/WithDependencies.pm
  • lib/Bread/Board/Service/WithParameters.pm
  • lib/Bread/Board/SetterInjection.pm
  • lib/Bread/Board/Traversable.pm
  • lib/Bread/Board/Types.pm

CPAN Pull Request Challenge - June 2015

Hi there,

This month I got BreadBoard as my CPAN Pull Request Challenge. I know you already know about it because I already did a CPAN PR Challenge for one of your distributions in January :-)

It looks like there's a couple of issues and pull requests open on Github. Any in particular that you'd like me to take a look at?

Alex

Cannot have optional parameters

I have this setup:

package Foo;
use Moo;

has p1 => ( is => 'rw' );
has p2 => ( is => 'rw' );
has p3 => ( is => 'rw' );
has p4 => ( is => 'rw' );

package main;

my $c = container 'container' => as {
    service 'foo' => (
        class => 'Foo',
        parameters => {
            p1 => { required => 1 },
            p2 => { required => 0 },
            p3 => { },
            p4 => { },
        }
    );
};

my $foo = $c->resolve(service => 'foo', parameters => { p4 => 'test' });

I get this error:

Mandatory parameters 'p1', 'p2', 'p3' missing in call to Bread::Board::Service::WithParameters::check_parameters.

Why is p2 required? It has required => 0.

Calling container() inside another container() call can lead to odd results

What I'm seeing is a parent ending up containing both the parameterized container and the container it generates (the FromParameterized one).

I tried to boil this down to the simplest example I could:

package Foo;

use v5.22;

use Bread::Board;
use Moose;

use feature 'signatures';
no warnings 'experimental::signatures';

has broken => (
    is      => 'ro',
    lazy    => 1,
    builder => '_build_broken',
);

has works => (
    is      => 'ro',
    lazy    => 1,
    builder => '_build_works',
);

has _db_name => (
    is      => 'ro',
    lazy    => 1,
    builder => '_build_db_name',
);

has _database_container => (
    is      => 'ro',
    lazy    => 1,
    builder => '_build_database_container',
);

sub _build_db_name {'Foo'}

sub _build_broken ($self) {
    my $db_name = 'Foo';
    my $c = container db_name => as {
        service name => $db_name;
    };

    return container master => as {
        container(
            $self->_database_container->create(
                db_name => $c,
            )
        );
    };
}

sub _build_works ($self) {
    my $db_name = $self->_db_name;
    my $c = container db_name => as {
        service name => $db_name;
    };

    my $db = $self->_database_container->create(
        db_name => $c,
    );

    return container master => as {
        container($db);
    };
}

sub _build_database_container ($self) {
    return container database => ['db_name'] => as {
        service name => {
            block => sub ($s) {
                $s->parent->get_sub_container('db_name')
                    ->resolve( service => 'name' );
            }
        };
    };
}

__PACKAGE__->meta->make_immutable;

package main;
use Bread::Board::Dumper;

say 'Broken';
say '-------';
say Bread::Board::Dumper->new->dump( Foo->new->broken );

say 'Works';
say '-------';
say Bread::Board::Dumper->new->dump( Foo->new->works );

Note that I tried removing the Moose bits and doing this with non-method subs, and I couldn't get the issue to recur.

I think the problem has to do with this particular bit of code:

    return container master => as {
        container(
            $self->_database_container->create(
                db_name => $c,
            )
        );
    };

I think what's happening is that the first container master bit sets up a contact in Bread::Board causing it to set $CC. Then the subsequent call to container() in _build_database_container ends up adding this newly created parameterized container to the root, even though that's not what I wanted.

I have no idea how to fix this. If it can't be fixed, it's probably worth documenting.

That said, I tried to make a much simpler example using plain subs and couldn't replicate this. I really don't know why!

Dependencies accidentally cached when block-injection block dies

Found a bug where:

  1. If a "block injection" service has a dependency,
  2. And the block throws an exception,
  3. The next resolution of the service fails to recalculate the dependency.

We can work around this easily enough by wrapping the contents of the block with eval, but I actually liked the fact that the exception was being propagated to whoever called $container->resolve. If there's a way to keep that behavior, while still cleaning up the dependencies, that would be great.

Questions about depsolving, locks, parameters, singletons

Why can't we defer a parameterized service, if we have the parameters?
In ::WithDependencies::resolve_dependencies, could we not stash the $dependency->service_params alongside the $service object in the ::Deferred proxy? There's probably a good reason, but I don't see it…

Also, why is the is_locked attribute not marked NoClone?

Singleton::WithParameters does not lock

BB::Lifecycle::Singleton uses a lock while geting, but BB::Lifecycle::Singleton::WithParameters does not. Why? Should it? Should the lock be on the whole service, or on the specific get call (i.e. should we have a single boolean lock, or a hashref from parameters keys to booleans)?

Document parameterized container names

Is it intended that parameterized containers rename themselves to include all the parameter names? For example, given this snippet:

my $pc = container X => [ qw( Y Z ) ] => as { ... }

I would expect the following test to pass:

use Test::More;
my $c = $pc->create( Y => $yc, Z => $zc );
is($c->name, 'X', 'container is named X');

However, the passing test is actually:

is($c->name, 'X|Y|Z', 'container is named X with parameters');

This means to recover the expected name, I have to call the name setter to adjust the container. This isn't a problem, I just wanted to know if this is the intended behavior and can provide a documentation patch to Bread::Board::Manual::Concepts::Advanced.

RFC: Add a service injector for service "currying"

I have a project where it would be nice to have a set of services that act as a sort of template for another set of services. For example, I could define a generic database connector service that might be used to help construct a whole bunch of real database connectors. I was contemplating the benefits of doing this through Bread::Board like this:

container ResourceTypes => as {
  container DB => as {
    service connection => (
      class => 'MyApp::DB::Handle',
      parameters => {
        host => { isa => 'Str' },
        port => { isa => 'Int' },
        schema => { isa => 'Str' },
        user => { isa => 'Str' },
        password => { isa => 'Str' },
    },
  };
};

Later, I define a set of actual configurations which can be loaded something like this:

container Resources => [ 'ResourceType' ] => as {
  service devdb_host => 'devdb';
  service devdb_port => 3306;
  service devdb_schema => 'myapp';
  service devdb_user => 'sandbox';
  service devdb_password => 'secret';

  service devdb => (
    service_type => 'Service',
    service_path => 'ResourceType/DB/connection',
    dependencies => {
      host => depends_on('devdb_host'),
      port => depends_on('devdb_port'),
      schema => depends_on('devdb_schema'),
      user => depends_on('devdb_user'),
      password => depends_on('devdb_password'),
    },
  );
};

The ServiceInjection class would simply do a fetch() on the named service_path and pass through the parameters setup by the dependencies and parameters, allowing one service to be built up from another.

This works as a sort of currying, I guess, allowing one service to be built upon another.

Thoughts? If that sounds like a reasonable idea, I can send a pull request. Otherwise, I may just find a different way because I don't really need to create this just for me.

MX_PARAMS_VALIDATE_ALLOW_EXTRA => 1,

it's kind of annoying to require strict validation to an object that is capable of validating itself. e.g. instantiating a model which has a lot of parameters, but needs a few dependencies injected. could we add the following to

  package Bread::Board::Service::WithParameters;

around line 62

    MX_PARAMS_VALIDATE_ALLOW_EXTRA => 1,

I see 2 options for doing this, option A, just add it, anyone who's code conforms to strict will still work. Option B, add a new attribute allow_extra which will set this if it's set. I prefer option A, because I think it makes Bread::Board easier to use in general, but I could understand that it could be seen as a behavior change.

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.