Coder Social home page Coder Social logo

Comments (23)

mitar avatar mitar commented on June 9, 2024 1

See explanation here. So you can use that constant instead, so BlazeComponent.components[ComponentsNamespace.COMPONENTS_FIELD].

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

You have BlazeComponent.components which contains all the components. See here.

Which variables are you thinking about? The ReactiveField you attach in onCreated?

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

@mitar, it was just an idea, but probably yes. I want to list the atts inside the component. The idea is to generate a page with components and the atts that can be passed/used inside it. As we are a medium size team, we want to have some control about what's on the code. btw, tks for the quick response.

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

@mitar, about the BlazeComponent.components, in order to get a component I need to do this:

BlazeComponent.components[""].LoginSteps

Is there any specific reason for [""] ? It works but it feels strange 😄

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

Feel free to create a pull request with static method on ComponentsNamespace to return all components inside a namespace, and another for all sub-namespaces.

So feel free to organize that code for you to make it work for you.

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

But are attributes dynamically added after construction of the component?

So, there are multiple things we ca be talking here:

  • arguments you pass with {{> component args <arguments>}}
  • data context arguments expected by the component
  • reactive fields you add to the component in its onCreated

Which one are you using in your project? Which one you care about?

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

@mitar, Our idea is this:

Okay, let me check if that component already exists. Nice, it exists. What are the arguments that can be passed into it? All of this without checking the code. We are even training our design team to use this type of documentation. With this it would be possible for them to understand if a component is customisable.

I don't know if I'm dreaming too much, listing the components it's already great for us.

About the PR, we are already working on somethings that will probably become a PR.

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

BTW, do you know of: https://github.com/peerlibrary/meteor-blaze-components-explorer

My attempt at creating a global component list: http://components-explorer.meteorapp.com/

I never had time to really do it, but my idea was to:

  • have a standard way that you include a screenshot of the component
  • list all components in a way published on Atmosphere
  • the ideas of arguments you are mentioning also sound very good

Probably we should first then define the proposed way to document and pass the arguments.

But besides arguments there is also what helpers you can and should use. So what methods exist on the component. Or do you think that this is internal to the component?

I like how https://github.com/coffeedoc/codo can discover all the symbols. So maybe, depending if you have enough resources, we could develop something really beautiful here.

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

But in this case we are talking about internal components right?

I'm thinking that we can do this in 2 different ways. One is something like codo, a external service that creates documentation based on comments and symbols. We use something similar for our Rails API, as you can check here.

The other direction would be an autodiscover function. Based on BlazeComponet.components function I can look for helpers, arguments, reactive fields and generate a clean array of objects as a result. That array can be used to render an interface. Nothing stops to use this method and also use comments to add screenshots, for example.

someting like BlazeComponents.getComponent(foo).methods() would already brake a leg here. We could filter private method (as onRendered) and use the others to create the documents.

For method one we would probably use commander + gulp (our common stack for CLI) to go over the project, find components and generate an output.

Sounds pretty interesting... what do you think?

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

As we are going this far, why not list the event binding? (We are defining events on js)

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

See also: #139

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

As we are going this far, why not list the event binding? (We are defining events on js)

Isn't this internal to the component? User of a component should not have to know anything about events.

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

I can look for helpers, arguments, reactive fields and generate a clean array of objects as a result

I am not sure how you can really do that? They are available after component is created only (because mostly they are created in a constructor or onCreated). I am not sure how we could make this automatic. But maybe you know. :-)

That array can be used to render an interface.

Which interface?

Nothing stops to use this method and also use comments to add screenshots, for example.

Of course, we should try to get as much information about the component automatically, if we can.

We could filter private method (as onRendered) and use the others to create the documents.

Yes, those are methods on the component. That is easier to analyze because you can do something like codo does. But for fields/arguments we do not yet have any way to know it, except if we create some extra metadata or some common pattern we are all using and our tool can then parse that pattern. (Like, creating reactive fields inside onCreated and then tool parses code of onCreated and see which fields have been created there.)

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

Isn't this internal to the component? User of a component should not have to know anything about events.

Yes it is. Was just thinking about clear vision about what's happening inside the component. But it makes sense to keep this private.

I am not sure how you can really do that? They are available after component is created only (because mostly they are created in a constructor or onCreated). I am not sure how we could make this automatic. But maybe you know. :-)

Maybe I'm doing something very ugly, but I can return the methods using this snippet:

blazeComponent = BlazeComponent.getComponent('CreateHistory').prototype
methods = _.keys(blazeComponent)

I'm using David Walsh function to get arguments from functions

function getArgs(func) {
  // First match everything inside the function argument parens.
  var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];

  // Split the arguments string into an array comma delimited.
  return args.split(',').map(function(arg) {
    // Ensure no inline comments are parsed and trim the whitespace.
    return arg.replace(/\/\*.*\*\//, '').trim();
  }).filter(function(arg) {
    // Ensure no undefined values are added.
    return arg;
  });
}

Now I can get a key from my methods var and get the arguments

getArgs(blazeComponent["handleTag"])

I'm pretty sure that extending this functions I can get reactive fields, for example.

Which interface?

The array of objects generated by autodiscover function can be used to generate a public page of documentation. That's what I've meant. In my case, I'm probably going to expose that as a JSON to be used inside our internal admin.

Going to comment on #139, but I can already say that components can then have public methods would also help here I suppose.

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

I'm pretty sure that extending this functions I can get reactive fields, for example.

Try. :-) As I said, reading the prototype is easy, reading things you attach onto the object during creation is harder.

The array of objects generated by autodiscover function can be used to generate a public page of documentation. That's what I've meant. In my case, I'm probably going to expose that as a JSON to be used inside our internal admin.

Interesting. So this would be part of the app itself?

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

I've spend some minutes here trying to think in a way to do this. I will describe here a proposal (not the best code, just trying to think out loud).

Interesting. So this would be part of the app itself?

In my case, yes. I've created a Discover() function that can be called and will return the JSON with documentation. That can be used to create the docs. This makes sense for me but I also understand that maybe not for other packages as they just want some documentation generated by blaze-docs generate or something similar. If my internal script works, maybe we can generate a CLI that can start meteor, return the json that I'm creating and render that indo a folder or something like this.

Discover = function () {
    const privateMethods = ['onCreated', 'onRendered', 'onDestroyed', 'events'];
    let components = BlazeComponent.components[""];
    let json = [];
    let keys = _.keys(components);

    keys.forEach(key => {
        keyObject = {name: key};
        blazeComponent = BlazeComponent.getComponent(key).prototype;
        methods = _.keys(blazeComponent);

        describedMethods = [];
        _.difference(methods, privateMethods).forEach( method => {
            describedMethods.push({
                name: method,
                arguments: getArgs(blazeComponent[method])
            });
        });

        if (_.contains(methods, 'onCreated')) {
            keyObject.blazeDoc = getBlazeDoc(blazeComponent['onCreated']);
            keyObject.onCreatedVars = getVars(blazeComponent['onCreated']);
        }

        keyObject.methods = describedMethods;
        json.push(keyObject);
    });

    return json;
}

function getVars(func) {
    var args = func.toString().match(/this.\w+/g);
    return args;
}

function getBlazeDoc(func) {
    let blazeDoc = func.toString().match(/blazeDoc[\w\W]*blazeDoc/g);

    if (!blazeDoc)
        return {};

    let screenshot = blazeDoc[0].match(/@screenshot: https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/);

    return {screenshot: screenshot[0].replace('@screenshot:', '').trim()};
}

function getArgs(func) {
  // First match everything inside the function argument parens.
  var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];

  // Split the arguments string into an array comma delimited.
  return args.split(',').map(function(arg) {
    // Ensure no inline comments are parsed and trim the whitespace.
    return arg.replace(/\/\*.*\*\//, '').trim();
  }).filter(function(arg) {
    // Ensure no undefined values are added.
    return arg;
  });
}

An example component that is working with this method is this one

class LoginSteps extends BlazeComponent {

  onCreated() {
     /*blazeDoc
      @screenshot: http://pandalargo.com.br/wp-content/uploads/2015/09/panda-lambendo.jpg
    blazeDoc*/
    super.onCreated();
    this.status = new ReactiveVar(false);
    this.error = new ReactiveVar(false);
    this.schools = new ReactiveVar([]);
    this.selectedSchool = new ReactiveVar(false);
    this.email = new ReactiveVar(false);
  }

  events() {
    return super.events().concat({
      'submit #login-step-email': this.verifyEmail,
      'submit #login-step-schools': this.selectSchool,
      'submit #login-step-password': this.tryAuth
    });
  }

  tryAuth(event) {
    event.preventDefault();
    let password = this.$('#password').val();
    let school = _.find(this.schools.get(), s => Number(s.id) === Number(this.selectedSchool.get()) );

    Edools.authenticate(this.email.get(), password, this.selectedSchool.get(), (response) => {

      if (response.auth) {
        User.login(response.content, school);
        // Edools.redirectAdmin(response.content, school);
      } else {
        this.error.set('wrong-password');
      }
    });
  }

  selectSchool(event) {
    event.preventDefault();
    let school = this.$('[name=select-school]:checked').val();
    this.selectedSchool.set(school);
    this.status.set('password');
  }

  verifyEmail(event) { 
    event.preventDefault();
    let email = this.$('#email').val();

    Meteor.call('Edools.requestSchools', email, (err, result) => {
      let schoolsList = result.schools;
      this.schools.set(schoolsList);
      this.email.set(email);

      if (schoolsList.length === 0)
        return this.status.set('none');

      if (schoolsList.length > 1)
        return this.status.set('multiple');

      if (schoolsList.length === 1) {
        this.selectedSchool.set(schoolsList[0].id);
        return this.status.set('password');
      }

    });
  }

}

LoginSteps.register('LoginSteps');

This function is returning

  • All components inside my app
  • All helpers and functions (not the private ones). Maybe I can check if it's an private method looking for _
  • All arguments for each method
  • All variables defined as this.variableName inside onCreated
  • The screenshot defined inside the block blazeDoc

What do you think about this? It's a way to move foward?

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

(Creating a package to make this hack more universal and with better coding)

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

I think getVars is very fragile.

Also, your function works only in development mode, when code is not minimized.

Moreover, you should probably be using inline event handler instead of such event maps. :-)

What I started thinking is that it is probably better to define one method fields, which returns a dict of various fields to attach to this, and then in parent onCreated you attach them to onCreated. It is then easier to get information about those fields. And that method can even be class-level method.

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

I think getVars is very fragile.

Agreed @mitar . I guess the fields method that you describe would be the best here. This can be something very nice. Thinking about your ideas to version 2.0, this dict could have the information about required fields and so on, right?

Also, your function works only in development mode, when code is not minimized.

Yes, that's true. But it was quicker to build this (real company world 😕 ). I'm thinking in someway to attach the creation of the docs into a hook before the build. That would solve my problem. Do you think I should go back and rethink the concept?

Moreover, you should probably be using inline event handler instead of such event maps. :-)

That's something that is messing with my head in the last weeks. My developers were not Classic Meteor developers, so it's not a breaking change to make them use inline events. But for a server side view, the idea to have all events mapped into one place is very nice and easy. I get all the benefits of using inline events, but when you have 10+ people on the same code, event maps help you check the context easier. But again, this is still messing with my head. (btw, I'm the CTO, that's why I keep talking about my team)

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

Thinking about your ideas to version 2.0, this dict could have the information about required fields and so on, right?

Exactly. So this is something which we can do already now, as part of common practice. And then it would contain only metadata. And it would create reactive fields and stuff like this already now.

I think I will look into this a bit and add it as a feature to common component.

Do you think I should go back and rethink the concept?

No, I think this could be something available only during development anyway. You could even add it to the file/package which is included only during development into a Meteor app.

from meteor-blaze-components.

caioreis avatar caioreis commented on June 9, 2024

I think I will look into this a bit and add it as a feature to common component.

It works, but why not into core?

No, I think this could be something available only during development anyway. You could even add it to the file/package which is included only during development into a Meteor app.

Any suggestion on how to handle this? I'm imaging a bundle plugin that generates the json and saves a json file. What do you think?

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

It works, but why not into core?

Because it is not needed to be in core. :-)

Any suggestion on how to handle this? I'm imaging a bundle plugin that generates the json and saves a json file. What do you think?

You can use debugOnly flag on a package: https://github.com/meteor/meteor/blob/devel/tools/tests/apps/package-tests/packages/debug-only/package.js

So you have a package which is debugOnly and it at runtime adds to your admin the list of all components in the app. In production, this package is not loaded, so it does not add itself to admin/router and so on.

from meteor-blaze-components.

mitar avatar mitar commented on June 9, 2024

After some thought, I think this can be in the core, yes. A nice upgrade path based on ideas in the other ticket.

But I am realizing I will not have time to look into this anytime soon.

from meteor-blaze-components.

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.