-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] Best way to create a documentation about components #138
Comments
You have Which variables are you thinking about? The |
@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. |
@mitar, about the
Is there any specific reason for |
See explanation here. So you can use that constant instead, so |
Feel free to create a pull request with static method on So feel free to organize that code for you to make it work for you. |
But are attributes dynamically added after construction of the component? So, there are multiple things we ca be talking here:
Which one are you using in your project? Which one you care about? |
@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. |
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:
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. |
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 The other direction would be an autodiscover function. Based on someting like For method one we would probably use Sounds pretty interesting... what do you think? |
As we are going this far, why not list the event binding? (We are defining events on js) |
See also: #139 |
Isn't this internal to the component? User of a component should not have to know anything about events. |
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
Which interface?
Of course, we should try to get as much information about the component automatically, if we can.
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 |
Yes it is. Was just thinking about clear vision about what's happening inside the component. But it makes sense to keep this private.
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 getArgs(blazeComponent["handleTag"]) I'm pretty sure that extending this functions I can get reactive fields, for example.
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 |
Try. :-) As I said, reading the prototype is easy, reading things you attach onto the object during creation is harder.
Interesting. So this would be part of the app itself? |
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).
In my case, yes. I've created a 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
What do you think about this? It's a way to move foward? |
(Creating a package to make this hack more universal and with better coding) |
I think 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 |
Agreed @mitar . I guess the
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?
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) |
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.
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. |
It works, but why not into 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? |
Because it is not needed to be in core. :-)
You can use 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. |
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. |
Hey,
My team and I decided to create a documentation for the components created inside our app. I'm searching for some way to easily do
BlazeComponent.listComponents()
, and it would be awesome to have access to variables inside the component and list them.Any ideas on how to do this?
The text was updated successfully, but these errors were encountered: