jagi / meteor-astronomy Goto Github PK
View Code? Open in Web Editor NEWModel layer for Meteor
Home Page: https://atmospherejs.com/jagi/astronomy
License: MIT License
Model layer for Meteor
Home Page: https://atmospherejs.com/jagi/astronomy
License: MIT License
I want to create a child class, which only adds some methods.
But astronomy forces adding at least one field even to child classes:
Error: At least one field has to be defined
I think it should be possible to have child models without extra fields.
When working with forms, we definitely want to set the label, placeholder, the input type, if it is a textarea, number of rows, min and max, etc to use then in template helpers.
Now, we can't define this on scheme level. Besides, if we use validators, we also define some of this info there: maxLen, minLen for example. The type definition is duplicated in fields and simpleValidators (string for example).
We need to think about a good api for that functionality.
@jagi , any thoughts on how this can be implemented? Where do you suggest to do that?
Hi,
This may just be a documentation issue, but some of the lifecycle event functions in https://github.com/jagi/meteor-astronomy/wiki/Behaviors are not camel case. I think we should be consistent everywhere.
e.g.
addbehavior
initclass
initinstance
Great package. Seems to be working wonderfully in my project up to right now. I'm not quite sure of what I'm doing wrong but if anyone could help me out that'd be great.
This is going to seem like a very long issue but it's really just a lot of snippets of code. I basically have a simple Add Question page to that calls the /question/save Meteor Method on the server after validating and then inserts the document in the collection. For some reason, some of my fields (2 selects, 1 textarea and 1 input) come back as null. Not always the same one too (which makes things worst). I know the method is being called and a document is created (good sign) and I know the jQuery for querying the data from the form is sound. I'm just not sure what is going wrong.
<!-- Section of addQuestionAdmin Template -->
{{#with question}}
<div class="field">
<label>Texto da Questão</label>
<textarea rows="4" placeholder="Escreva a questão aqui..." type="text" value="{{text}}" id="text"></textarea>
</div>
<div class="inline fields">
<div class="four wide field">
<label>Resposta</label>
<select class="ui dropdown" id="answer" value="{{answer}}">
<option value="">Resposta</option>
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="D">D</option>
</select>
</div>
<div class="six wide field">
<label>Prova</label>
<input placeholder="Prova" type="text" id="prova" value="{{prova}}">
</div>
<div class="six wide field">
<label>Área</label>
<select class="ui dropdown" type="text" id="materia" value="{{materia}}">
<option value="">Área da questão</option>
<option value="0">Ética</option>
<option value="1">Filosofia</option>
<option value="2">Constitucional</option>
<option value="3">Direitos Humanos</option>
<option value="4">Direito Internacional</option>
</select>
</div>
</div>
<br>
<div class="right field">
<div class="ui button green" type="button" name="save">Adicionar Questão</div>
{{/with}}
I then have a simple events page on the template
Template.addQuestionAdmin.events({
'focus .field': function(e, tmpl) {
var question = this;
var text = $('#text').val();
var answer = $('#answer').val();
var prova = $('#prova').val();
var materia = $("#materia").val();
question.set('text', text);
question.set('answer', answer);
question.set('materia', materia);
question.set('prova', prova);
question.validate(text, answer, materia, prova);
},
'click [name=save]': function(e, tmpl) {
var question = this;
if (question.validate()) {
Meteor.call('/question/save', question, function(err) {
if (!err) {
Router.go('/admin');
} else {
question.catchValidationException(err);
}
});
}
},
});
I also have the /question/save method on the server.
Meteor.methods({
'/question/save': function(question) {
if (question.validate()) {
question.set('author', Meteor.userId());
question.save();
return question;
}
question.throwValidationException();
}
})
Here's my collection
// Create global Mongo collection.
Questions = new Mongo.Collection('questions');
// Create global class (model).
Question = Astro.Class({
name: 'Question', // Name model.
collection: Questions, // Associate collection with the model.
transform: true, // Auto transform objects fetched from collection.
fields: {
text: 'string',
answer: 'string', // Define "title" field of String type.
prova: 'string',
materia: 'string',
author: 'string',
},
behaviors: ['timestamp'] // Add "timestamp" behavior that adds "createdAt" and "updatedAt" fields.
});
Here's my router:
Router.route('/add-question', {
name: 'addQuestionAdmin',
data: function() {
return {
question: new Question()
}
}
});
It should be simple. I don't understand why the data isn't coming through. Through using Mongol all I can see on the db that some values get the data, some get null. They interchange, sometimes getting the first two fields and then the next try only gets the last 2 selects. It's fucking with my brain. What am I doing wrong!?
Any help would be appreciated. Thanks in advance!
Consider the following code:
Meteor.methods({
setPostTitle: function(postId, title) {
Posts.update(postId, { title: title });
}
});
It this the Astronomy equivalent?:
Meteor.methods({
setPostTitle: function(postId, title) {
var post = Posts.findOne(postId);
post.set('title', title);
post.save();
}
});
Any way to avoid the extra database read? (findOne)
Hi, great package and thanks for all the time put in!
Is it possible to set the _id
when inserting new documents? I'm storing data from a 3rd party API which already includes a unique ID. I need to query based on this 3rd party ID, and since _id
is indexed automatically I'd prefer to just use the 3rd party ID, rather than having to create another index.
relations stopped to work in my project returning undefined.
is this an issue known to you?
The icon in the logo of meteor astronomy really like like the icon of the logo of meteorionic.
I wouldnt say its a big problem but I just to take note
I am quite new to meteor and I am playing with setting up models for a new project and I'm using the timestamp as well as some customer behaviours. At the moment I'm specifically using behaviours to add groups of fields to models that will be re-used in a few different models, for example address fields.
Astro.createBehavior({
name: 'addresses',
events: {
addbehavior: function(behaviorData) {
var Class = this;
// Add fields to the Class.
Class.addFields({
'addresses': {
type: 'array',
default: []
},
'addresses.$': {
type: 'object',
default: {}
},
'addresses.$.name': 'string',
'addresses.$.street': 'string',
'addresses.$.town': 'string',
'addresses.$.postcode': 'string',
'addresses.$.country': 'string',
'addresses.$.isDefault': 'boolean',
'addresses.$.isBilling': 'boolean',
'addresses.$.isShipping': 'boolean'
});
},
initclass: function(schemaDefinition) {},
initinstance: function(attrs) {}
}
});
I have a customer class which implements the addresses and timestamp behaviours.
Customers = new Mongo.Collection('Customers');
Customer = Astro.Class({
name: 'Customer',
collection: Customers,
transform: true,
fields: {
company: {
type: 'string'
}
},
behaviours: ['addresses', 'timestamp'],
relations: {
Quotes: {
type: 'many',
class: 'Quote',
local: '_id',
foreign: 'customerId'
},
Contacts: {
type: 'many',
class: 'Contact',
local: '_id',
foreign: 'customerId'
}
}
})
I am simply adding a quick customer on the server side at startup.
Meteor.startup(function() {
Customers.remove({});
cust = new Customer();
cust.company = "Acme Cafe Ltd";
cust.set('addresses.0', {
name: "Cafe",
street: "The Esplanade"
});
cust.save();
});
However the document thats created in Mongo has no createdAt or address fields, I do have the additional modules installed in the packages file.
What am I doing wrong? I don't see any errors and the 'just' object validates.
Many thanks for a great package, I hope I can get this working well.
Vanilla meteor allows you to pass a hash to an .update() call using $set. This feature is missing from Astronomy. (or is it?) It's definitely missing from the docs. Is there a way to do this in Astronomoy, similar to mass-setting the parameters in a new Object(hash) call? Thanks!
eg.
var obj = {"name": "blah", "title": "name"}
// ast is an astronomy object
ast.update(obj)
ast.save()
I have three questions about events.
What is the order of execution for the events: beforesave
, beforeinsert
, beforeupdate
, beforeremove
, aftersave
, afterinsert
, afterupdate
, afterremove
? Is this correct: beforesave
=> aftersave
=> beforeinsert/update
=> afterinsert/update
and independently beforeremove
=> afterremove
Is the afterinsert
hook from matb33:collection-hooks
the same thing compared to the afterinsert
event from astronomy
?
Is getModified()
available to the events so that I can check what actually has been modified within the before/afterupdate
hook and act accordingly? I'm referring to the this.previous
from https://github.com/matb33/meteor-collection-hooks#afterupdateuserid-doc-fieldnames-modifier-options
The wiki does not specify this situation I have.
How could one do a relation to self based on an array of ids?
User follows other User and maybe vice-versa?
class User would have an array "followersIds" with all of the ids of its followers.
User: _id, followersIds: [id2,id3,id4]; var user = new User(); user.followers
Thank you for your time
Hi @jagi
when trying to save an array of objects, it works the first time, second time not.
You can use the same repo here: https://github.com/banglashi/astronomy-serverside-transform
Than run following on the console:
var newChat = new Chat()
undefined
newChat.title = "test";
"test"
newChat.members.push({title:'test'})
1
newChat.save()
"pz5FLFERiXgFfjkzJ"
newChat.members.push({title:'test2'})
2
newChat.save()
false
Any ideas? Is this perhaps still in development? Thanks and regards.
menu
is instance of Astro.Class
.
Server side :
if ( menu.validate() ) {
menu.save();
} else {
console.log( menu._errors._getMigrationData() );
return menu;
}
Above console.log
show that there is invalid field in menu
.
_errors : ReactiveDict
is not send through DDP , so client side can't see errors sent by server side.
There are some custom validators which should run on server ( unique ). So my goal is to validate on client side and then validate on server side. Eventually errors should be send back to client side.
A ToC is supposed to describe what's in the document below. Here there are 2 issues:
As a result, I have just spent 15 minutes starring at the doc to find the concept of "validator" (although I used other keywords such as "validate" and "check"). I found nothing and went though the source code, wondering "how is it possible there is nothing to validate a value?!". Then I finally found that there is an external package for this (a package whose name isn't even mentioned in the doc).
I suggest the following:
Any plans to add form model binding? Thanks, this looks like a great package, looking forward to playing with it.
Is there a reason you made them lower case?
https://github.com/jagi/meteor-astronomy/#field-events
Edit: maybe even use dots?
'after.set'
This is the next feature I'm going to implement. This will allow sending Astronomy models as a param of Meteor method.
Many people in the Meteor community are familiar with @aldeed's SimpleSchema and @matb33's CollectionHooks. @Sewdn's collection-behaviors is another prior art that also seems to have been used as source of inspiration.
After reading the documentation, Astronomy seems to reinvent all these three packages and use neither. A lot of work has gone into both SimpleSchema, CollectionHooks and Collection Behaviors on one hand, and Astronomy on the other. There are probably good reasons for not reusing the existing packages, and why Astronomy decided to build its own schema, hooks and behaviors. Users would love to know these.
Thanks!
Hello,
i found out that the code example for the behavior timestamp is not working, we need to change 'Timestamp' to 'timestamp'.
Currently, the only way to work with array-type fields is to explicitly specify them by index:
Let c be:
{
members: [
'member-1',
'member-2',
'member-3'
]
}
c.get('members.0') // Returns 'member-1'
c.set('members.4', 'member-4')
It would be nice to have a way to push a new element onto the array like so:
c.push('members', 'member-4')
c.pop('members') // Returns 'member-4'
c.pop('members', 0) // Returns 'member-1'
I tried to do that
Address = Astro.Class({
name: 'Address',
collection: Addresses,
fields: {
// ...
}
});
Person = Astro.Class({
name: 'Person',
collection: Persons,
fields: {
// ...
address: new Address() // here
}
});
But it doesn't work, how do I achieve this ?
Since I see a natural evolution of astronomy to be a forms framework, I'd like the fields to have a selective:
property where persisted
is default but if marked as transient
it becomes a part of the model and not saved in the db.
A practical use of transient fields would be form fields where we would like to validate, but only save a calculation or part of. For example, a form may consist of an address part where the user needs to enter areacode
, areaname
, district
, province
, city
, state
but evidently, only areacode
is sufficient to deduce that whole set of fields. So I would not want to persist the whole thing, only the areacode.
This goes for the view as well, such that the user would be able to see the complete address information that is only derived from a few persisted fields.
Another example would be two fields, age<integer>
and isAdult<boolean>
where age
needs to be persisted but isAdult
is actully age>=18
.
I know some of this can be done using methods, but I believe methods should be reserved mostly for mutators.
Actually, that kind of semantics goes for relationships as well, indicating that a field is actually a reference to another database object that is mapped by it.
This is actually not new, they are concepts from java's persistence api implementation. This is a very brief, but effective read: http://www.objectdb.com/java/jpa/entity/fields
PS: It also discusses @version
which is used for optimistic locking of database records, and may be a good candidate to become a behaviour in astronomy.
Hi @jagi
It looks like new version 0.10.0 removed AND and OR validators...
What is the reason for doing that ?
Having those validators gives a lot of flexibility.
I'm using them and cannot find them...
Hi, I have question to you about validators. I'm working now on implementing them as a module for Meteor Astronomy. Here is the repository. It's not finished yet and I need your support on how it should work to be the most intuitive and easy in use. Please take a look at read me in the repository.
Let me describe here how it works. Right now, we can define array of validators in the class schema. In validator definition, we have to provide name of the field
on which validation should take place, type of the validator, some options if required and error messages. We can also add validators that works on many fields like Compare validator. To validate object we call validate()
function on given object. Now few questions arises.
save()
method on the object?true
or false
return value from the validate()
method? And later you would like to call function like for instance object.getErrors()
to get all errors.object.validate('comparePasswords')
?If you have any other idea I'm open for discussion :)
It doesn't appear that these work.. I realise it's an edge case!
Code something like this:
Parent = Astro.Class({
name: 'Parent',
fields: {
'system': {
type: 'object',
default: {}
},
'system.field2': {
type: 'string',
default: 'field2test'
},
});
Child = Base.extend({
name: 'Child',
collection: Childs,
fields: {
"field1": {
type: 'string',
default: 'field1test'
}
});
new Child().save();
The record created will have the values {} for system, and field1test for 'field1', but not the nested field. Moving the nested field up to the top level causes this default to be inserted properly.
Will see if I can dig in the code and suggest a fix.
Thanks for creating an amazing package. I would request you to add two way data binding.
I understand that meteor-astronomy uses the transform
function to create classes and add methods, but some libraries (e.g. https://github.com/alethes/meteor-pages) use cursor.observechanges
instead of cursor.find
, so the transform function is never called. Is there a plan to patch this?
I would love to be able to use model methods from astronomy on my paginated pages.
Apologies if I missed this somewhere. Thanks for an awesome product!
In my code I use the "reload" prototype method in some cases.
In Version 0.11.0 I was able to save a already persisted document after a reload. In 0.12.0 its recognized as new after reloading and saving it causes a duplicate key exception.
var post = Posts.findOne();
post.save(); // No error here
post.reload();
post.save(); // Exception while invoking method 'Post.save'
// MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error index
After reload new property ".isNew" becomes true
Just learned about your package. I'll be tracking it to see how it evolves :)
First impression was that the setter/getter syntax is verbose. From docs
Setters and getters
Each class has the setter (set) and getter (get) functions. Let's take an example.
Post = Astro.Class({
name: 'Post',
collection: Posts,
fields: ['title', 'commentsCount']
});
var post = new Post();
post.set('title', 'Title'); // Call field setter.
alert(post.get('title')); // Call field getter.
But you can also set values directly but assigned values won't be converted to the proper type of the fields.
post.title = 'New title';
post.title = 123; // Value won't be converted to the string.
alert(post.title);
You should always set values coming from the forms using the set function.
Can we not consolidate the approach somehow? object.property = x is much more terse.
Not a big deal at all, but it might be helpful to have a way to remove a document given just an _id. Because removes are often done in a meteor method, it can be more efficient to call the method with just an _id and not have to pass a full copy of the object back to the server. Similarly for cases where all I have is an _id (perhaps from a related record) and haven't yet pulled the whole document.
If I do Posts.remove(id), then any Astronomy beforeremove/afterremove events don't get called. So I've been doing (new Post({_id:id})).remove()
, which seems a bit clunky. Is there a better way?
This issue tracks the release of Astronomy 0.13.0.
init
function on every document creationMoved to the 0.14.0 release.
I'm trying to use MomentJS to format the date for the createdAt and updatedAt timestamps. I tried using an aftersave and a beforesave event like this
events: {
aftersave: function() {
var date = moment();
date = moment(date, 'MM-DD-YYYY');
post.set('postedAt', date );
}
}
Neither of them seem to create a postedAt field on the db when I add another post. Am I approaching this the correct way?
Hi @jagi,
Perhaps you can help me out here. As soon as I do a server-side transformation the astronomy "Automatic document transformation" doesn't work anymore. Perhaps you have some insights you can give me on this on how to make it work?
I created a repo showing the issue here https://github.com/banglashi/astronomy-serverside-transform
Let me know if you need any more information. Thanks.
Is there a way that from inside a beforeSave/Insert/Update/Remove event we can cause the database operation to be canceled? For example, this would enable the soft delete functionality you've shown to apply to actual removes vs. having to call a special function.
I have a lot of nested documents and would like to manage them using Astronomy package.
A = Astro.Class( {
name: 'A',
collection: ACollection,
fields: {
name: {
type: 'string',
default: ''
},
displayName: {
type: 'string',
default: ''
},
notes: {
type: 'string',
default: null
},
links: {
type: 'array',
default: []
},
category: {
type: 'string',
default: ''
},
subObject: {
type: 'boolean',
default: false
}
}
)
Field links
is array of objects.
Usually I'm using js-schema
library :
ASchema = schema({
name : String,
displayName : String,
notes : String ,
links : Array.of( {
_id: String,
amount: Number,
unit: String,
prepMethods: Array.of( String )
}),
category : String,
subObject : Boolean
})
However js-schema
doesn't allows me to specify default values for each field.
I would love to have js-schema
syntax together with your validators.
Something like that, would be brilliant :
var t = function(type, default){
var obj = {type:type, default : default};
return obj
}
A = Astro.Class( {
name: 'A',
collection: ACollection,
fields: {
name : t('string', 'hello'),
displayName : t('string', 'displayname'),
notes : t('string', 'default notes') ,
links : Astro.arrayOf( {
_id: t('string', 'default notes'),
amount: t('number', 0) ,
unit: t('string', 'default notes'),
prepMethods: Astro.arrayOf( t('string', 'default') )
}),
category : t('string', 'default'),
subObject : t('boolean', true)
}
)
Let me know what do you think about it?
Hi.
First of all thank you very much for your excellent package.
I'm having an issue when trying to attach currently logged in userId as a default value for field in Astro.Class.
When I have
Message = Astro.Class({
name: 'Messages',
collection: Messages,
fields: {
'senderId': { type: 'string', default: Meteor.userId() },
I got an error: Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.
But with 'senderId': { type: 'string', default: this.userId },
this keyword is AstroClass and its userId is null.
Not sure if it's possible, since validators are a separate module, but I think it could be helpful to be able to specify single-field validators inside the fields object (as an option, in addition to the current syntax):
fields: {
emailAddress: {
type: 'string',
validators: Validators.email
}
}
For more lengthy object definitions it would make it simpler to be able to tell which fields have been validated and which haven't (particularly important during development to be able to make sure I haven't forgotten about one).
Just a thought.
With the new "supportLegacyBrowsers" config (see #32) the "_values" object have been reintroduced. This causes some trouble with "Meteor.loginWithPassword".
It fails with "User has no password set" as it does not find a password in key "services" of user object - because its hidden inside the "_values" object
Nice package. The question is - when I get the post
instance - will it reactively update when its data change or do I need to call reload
method ?
Is it possible for behaviors to emit custom events? For example, I'm doing a slightly different variant on softdelete that actually just runs on a remove via beforeremove. It flags the object as deleted and cancels the remove. Because the remove was cancelled, the afterremove event no longer fires (I assume). And it shouldn't.
But I'd like the ability to emit an "aftersoftdelete" event that objects with this behavior could handle.
Is that possible?
Sorry for the flood of new issues... hopefully they're helpful.
The type validators (date, email, etc.) all appear to return false (invalid) if the field is blank. To me, it's more intuitive that they'd return true in that case. If I want to fail a blank field, I'd add the "required" validator. But if it's not marked as "required", it should not be required. :)
Not sure what others think, though.
@jagi
We can team together instead of re-inventing the wheel. Great work though!
https://github.com/orionjs
http://orionjs.org/
I noticed validation stops on first invalid field, that is perfect for most use cases.
However there is one use case where validation should not stop on first invalid field.
In my apps I try to keep structure of documents consistent all the time ( what a suprise ;-) ) so on every startup app is validating all documents in all collections.
It may happen that doing some changes manually on db admin simply broke structure.
It is better to see upfront how much db was damaged.
Let me know if sth like that could be easy to add.
Again, thanks for your hard work ! :)
I am trying to see if packages astronomy+validator can replace aldeed::Collection2
in my project.
One thing I don't see clearly is how to validate any object that I intend to save to the database. By validate I mean "throwing an exception if the object is ill-formed".
What are the best practices? Should I implement my own validateAndSave()
function? Is there something I am missing?
As discussed here it would be good to have a way to track changes made to a DB/collection (or a table in future) by some server-side process, or a user who can access the DB.
The module should be smart to analyze what kind of change has been made?, when?, and by whom? and be flexible to fire certain events with custom messages, something like:
"[a process, or] a user with *username* (*IP address*)
touched *DB/collection* on *Date/Time* and *modified/deleted/created* this doc:
docID, old version: "{modified properties}", new version:"{modified properties}"
This is just a quick example I can think of and surely there are better ways to design this, so I'd love to here some comments.
p.s.: perhaps it could be named as "DB-watch" or something. :)
Would this be feasible? Like in simple-schema?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.