refractproject / minim Goto Github PK
View Code? Open in Web Editor NEWA library for interacting with Refract elements
License: MIT License
A library for interacting with Refract elements
License: MIT License
In converting to Refract, do not show meta, attributes, or content if they are undefined or if they are empty.
This leads to non-obvious behavior which I think we need to properly define or change. Here is the implementation as it stands:
class Meta {
getProperty(name, value) {
if (!this.meta[name]) {
this.meta[name] = registry.toType(value);
}
return this.meta[name];
}
}
Here is the surprising part:
const element = new ElementType();
console.log(element.toCompactRefract());
// => ['element', {}, {}, null]
// Access the non-existing name property
element.name;
// What? Now there is a meta name for some reason!?
console.log(element.toCompactRefract());
// => ['element', {name: ''}, {}, null]
// Try to get something from meta with a default if not found
element.getProperty('foo', 'bar');
// What? Now my default value is getting serialized!
console.log(element.toCompactRefract());
// => ['element', {name: '', foo: 'bar'}, {}, null]
I'm sorry I glossed over this in the initial review! I believe the code is written this way to cache the conversion to refract element classes for the default value, but I think we need to either do that conversion each time or keep a separate cache that doesn't mutate the original meta values..
Thoughts @smizell?
Refract has three properties:
What should the interface to these values be on an element? With #25 I've introduced meta as a special class where each property is a Minim instance. Additionally, with #27 I've introduced elementName
instance method and removed the element
.
elementName
be accessible through a getter for element
?attributes
be a lazy loaded Minim object?content
be a Minim Collection?I see that calling children
does a recursive find. Is this correct? I would expect that children only do a shallow lookup on elements.
api.deleteChildren('resource')
AFAICT the toEmbeddedRefract
is not part of the library any more, yet it is still mentioned in the README. Is it correct observation?
I am interested in making the getters and setters be immutable. This means that when you set a value, the old value is not changed, but rather a new object is returned. This requires some fancy work to pass changes up and down the hierarchy and to share the tree, which shouldn't be too hard.
When it's done, it could be a really cool library for use with React, as a component could listen in on only one part of the tree using callbacks.
This is a fun experiment:
el = new minim.ObjectElement({a: 1});
// This works
el.toRefract();
// This fails
el.set('a', undefined);
el.toRefract(); // => throws exception
// Also this one is fun
el = new minim.ArrayElement([1, undefined]);
el.findByElement('test'); // => throws exception
Lots of other stuff falls down like this too. Question: should forEach
be returning undefined items? What about for an object where the key is defined but the value is undefined? Is this just something that we need to check for in most of the convenience methods?
How would you run into this? I had some malformed serialized refract which I loaded and it wound up having undefined keys and values for object elements. Any searching that included those elements wound up throwing exceptions.
Right now, each call of #toRefract
outputs meta
and attributes
even if they are empty. We should not output these if they are empty.
When implementing dredd-transacitons
, I made my own implementation of a small Refract library on top of traverse
and sift
, because neither refract-query
or minim
seemed to solve my problems. It lives in refract.coffee
file, hence the name in the title of this issue.
I wrote down rough list of requirements I expect from a Refract library: apiaryio/dredd-transactions#10
Per issue #81, it seems like a good idea to move forward to 1.0.0 as soon we can. I've tried to go through and put the issues under the milestone of 1.0.0 that seem to be important to complete before we say "this is stable."
In addition that, I'd be really interested in other's thoughts. Is there anything else? Should any of the issues I've added not be considered?
I'm on User Support, so I have a little time to type this out. I've mostly been away from the computer this week, which has been nice :)
I wanted to write out some thoughts that I have to get Minim to a usable state. Minim started out as a POC for interacting with Refract elements. Now all the sudden it's something that we may like to use, so it will be good to give it some added care.
First, I think the most important thing here is to get the API right, and I don't think it's quite there yet. Currently, to create a new type object, you have to extend one of the primitive type classes, and this is not good for several reasons. I believe "classes" only exist in CoffeeScript and ES6, and I don't think they are interoperable at this point (maybe?).
To get it right, I think we need to have one element object that handles certain categories of elements that can be easily wrapped to create new element types and functionality (like ArrayElement.createClass
, much like React and Backbone). This base class can introspect the content to see what methods should be made available to the user. This means we don't have to care if an element is an array
or object
, we can know they are iterable because the content
of the element is iterable.
To think of an example, a string
element is a value-like element, but it is not iterable. This means calling map
on it throws a Minim error. For array
elements, though, they are iterable, so the user may map over each element in content
. This all saves us from having element explosion while encapsulating all functionality in one "class."
Next, we need to define all base elements from Refract (once we finalize the first iteration of it, which we are hopefully close). This is important for Minim to interact with new element types. With the previous recommendation above, I believe this will allow for working with any Refract document without having to pull in namespaces. This will be a big win I think, and may save us from needing to have a factory for registering elements.
Next, I believe we need to work to remove lodash
as a dependency. I like using it to get up and running quickly, but if this is going to be used in a browser, there is no need to load another library for simply things. This is not a hard requirement in my mind, but could be beneficial.
Lastly and related, to bring this all together, this needs to be ran through Browserify to make it usable in the browser. At that point, it should be a very small and usable library for interacting with Refract payloads.
Doing all of this gets Minim to a usable state in my mind, and we are not far from this at all. In light of this, I vote that we close #21 for now to focus on these other things, as error handling will be much easier with a central class. I would then like to do a refactor on how the elements class are organized to greatly simply the code, making it easier for Minim to simply interact with a document that introduces any kind of element.
This is going to come up quickly, but we need to think about how to handle circular structures.
var minim = require('.minim');
var arr1 = new minim.ArrayElement();
var arr2 = new minim.ArrayElement();
arr1.push(arr2);
arr2.push(arr1);
console.log(arr1.toRefract());
This results in RangeError: Maximum call stack size exceeded
.
Add a method to find the first item matching the criteria given. This will be similar to find
, but it will be optimized to return the first one found and stop recursing the tree.
In reading this utility code, it may be helpful to provide convenience methods for better filtering. I think the utility code there is moving toward a structured way to query the tree, which I think we want to avoid at the moment. Using pure JS code is preferred. This means we need some better convenience methods.
For collections:
.getById()
takes a string and returns the first element found with ID in entire tree.filterByClass()
returns collection of elements where class matches given class name. Searches entire tree.For items:
.getClass()
returns an array of classes (or should it be collection?) (or .classList()
or .classes()
).getId()
returns the ID of the element (undefined if not found) (or .id()
).withClass()
takes a string and returns true/false depending on if element has the class name given. Should also take an options
argument where you can do things like ignoreCase: true
..hasClass()
returns true/false if element has classes.withId()
takes a string and returns true/false if the element has matching meta.id
.hasId()
return true/false if element has an ID.withName()
takes a string and returns true/false if the element has matching meta.name
(do we have a better name for this function?) Should take options
like hasClass
..hasName()
returns true/false if name exists. Should take options
like hasClass
.isType()
takes a string and returns true/false if the element is the given value (better name?)To make this happen, I think it would be nice to also have some additional methods.
.getContent()
returns the content. Up for discussion... if it's array, it may be nice to return a collection element. This is people are not working directly with content
property, which we should consider private..children()
with no condition returns a collection of of the content. This is essentially .getContent()
.first()
returns .get(0)
.second()
returns .get(1)
For example, given I have a reference element, I should be able dereference it.
Given the following tree called, structures
const element = {
element: 'array',
content: [
{
element: 'dataStructure',
meta: {
id: {
element: 'string',
content: 'User',
}
},
content: {
element: 'object',
content: [
{
element: 'member',
content: {
key: {
element: 'string',
content: 'last_comment'
},
value: {
element: 'ref',
content: 'Comment'
}
}
}
]
}
},
{
element: 'dataStructure',
meta: {
id: {
element: 'string',
content: 'Comment',
}
}
}
]
};
Given I have a reference to the User
dataStructure as user
:
const last_comment = user.get('last_comment');
last_comment.dereference();
Right now this won't behave as expected. Think of the MSON which defines one type which is an object, then another which inherits from it:
test
Without expansion/preprocessing it's likely it will get expanded to something like this:
['object', {name: 'Data types'}, {}, [
['object', {name: 'A'}, {}, []],
['A', {name: 'B'}, {}, [
['string', {name: 'C'}, {}, 'test']
]]
]
In the above example we'll get an ElementType
for B
instead of ObjectType
because there is no registered type for A
. That means that B.content
will be the raw content above instead of proper element instances. The solution may be to create some kind of lookup tree for the base type or some kind of preprocessing loop. Ideas?
Methods should have comments that describe the input parameters and what is returned.
Maybe use Codo.
The remove method would be for array and object elements. Remove would not destroy the instance, but simply remove it from the content of the item.
APIDom is using TypeScript now and rely this minim's type.
Should we improve types support for this project? I'd be happy to do this.
Following code
new JSON06Serialiser(fury.minim).serialise(element);
blows up when provided with API Elements, which has been previously frozen:
element.freeze();
I'm getting exceptions like this one:
TypeError: Can't add property _attributes, object is not extensible
at constructor.get (node_modules/minim/lib/primitives/element.js:295:26)
at constructor.shouldRefract (node_modules/minim/lib/serialisers/json-0.6.js:217:16)
at constructor.convertKeyToRefract (node_modules/minim/lib/serialisers/json-0.6.js:250:18)
at constructor.<anonymous> (node_modules/minim/lib/serialisers/json-0.6.js:288:26)
at Array.forEach (native)
at constructor.serialiseObject (node_modules/minim/lib/serialisers/json-0.6.js:287:16)
at constructor.serialise (node_modules/minim/lib/serialisers/json-0.6.js:19:30)
at Array.map (native)
at constructor.serialiseContent (node_modules/minim/lib/serialisers/json-0.6.js:190:22)
at constructor.serialise (node_modules/minim/lib/serialisers/json-0.6.js:42:33)
at module.exports (src/api-elements-to-refract.coffee:10:36)
at compile (src/compile.coffee:19:13)
at test/utils.coffee:26:16
at src/parse.coffee:39:5
at node_modules/fury/lib/fury.js:169:13
at node_modules/fury-adapter-apib-parser/lib/adapter.js:71:7
This blocks apiaryio/dredd-transactions#88.
This writeup outlines some ideas and thoughts on bringing Minim to a stable 1.0 release. I would like to work on stabilizing the interface and get some good use out of it before moving to 1.0, but would also like to not spend forever in pre-1.0 versions.
Right now, we call elements as types, such as StringType
, NumberType
, etc. We should align the naming of these to be StringElement
, NumberElement
, etc.
This goes for places where we call things "DOM."
Right now, to extend a class, you must be using ES6 or use a separate library for extending the prototype. I believe this is less than optimal, as it is a very important aspect of creating new elements. We need to think through how to provide support for this.
Backbone.js models extend models by adding a custom extend method on to classes [0]. They actually extend these classes using the underscore extend function.
Here is an example of a class in Backbone.js being extended.
_.extend(Model.prototype, Events, {
foo: function() { return 'bar'; }
}
Model then provides a special extend method for extending that particular class with conventions such as:
var Book = Backbone.Model.extend({
foo: function() { return 'hello world'; }
});
Now Book
has all of the methods that Model
has, but has a different foo
method attached to the prototype.
I believe we should support extending classes this way, but it provides issues. For instance, how can this extend override the constructor for an ES6 class? How can we provide a super for those using ES5? It brings with it the question, should we be doing all of this in ES5 (yikes)? I believe we can do everything here in ES5 that we need without the ES6 syntax.
No matter what direction we take, I believe extending a class in ES6 will still work correctly, even if we use ES5.
Right now, the interface to meta is that it is a pseudo Minim Object that provides defaults for each of the values. Right now, you have a different interface to meta and attributes, as meta has a Minim interface and attributes is a simple object.
As Daniel pointed out, this pseudo Minim Object for meta creates some undesired effects. We need to solve this, which I belive takes:
It would be helpful to store the content in a normal array, but provide a Minim instance for accessing the content. This means that meta, attributes, and content will all have the same interface.
Setters for objects currently require two argumentsβa property name and a value for that property. Backbone.js allows for providing an object directly to set.
obj.set({ foo: 'bar' });
This would be really nice to support, and would be very similar to Backbone.js as a common convention.
Right now, outputing full refract will always output meta and attributes. It would be beneficial in reducing size to only output meta and attributes if they have properties set. Since we will be (hopefully) be setting these as Minim Objects, it may be helpful to add an isEmpty
method to determine if the contents are empty.
We need to handle null
and undefined
values better when parsing a Refract tree. For example, when the value
of a MemberType
is undefined, we should determine how to treat those values values. Same goes for StringType
instances where the value is null
.
There are more issues than outlined here, but these are the big ones. I would like to tackle these first and we can evaluate afterwards.
[0] I know JavaScript before ES6 didn't have classes, but it's the language we use.
It would be good to sync up the code of Minim with the spec.
StringType
to String Element
What is needed is a place to register types. Currently they are simply passed into a function and several if statements. Instead, this should be extendable where new types and conditions for those types can be set.
In JavaScript array there is an includes
method, it catches me out that the same functionality has a different method name in minim. I think we should align the name.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
3.11.0
to 3.11.1
.π¨ View failing branch.
This version is covered by your current version range and after updating it in your project the build failed.
watchify is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.
The new version differs by 8 commits.
c3fe218
3.11.1
e90101b
Merge pull request #362 from digipost/upgrade-deps
d163b4f
upgrade dependencies: fix errors from npm audit
1917270
Merge pull request #351 from menzow/feature/update-docs
d232754
Merge pull request #357 from Kamil93/patch-1
d924e9b
Update readme.markdown
9ea8a65
Support for working with transforms
8147bc5
Add troubleshooting information for silenced errors
See the full diff
There is a collection of frequently asked questions. If those donβt help, you can always ask the humans behind Greenkeeper.
Your Greenkeeper Bot π΄
Create a method on elements that allows for a deep copy.
In the short term, this could be as simple as:
return registry.fromRefract(this.toRefract());
Per @danielgtaylor's idea
6.0.2
to 6.1.0
.π¨ View failing branch.
This version is covered by your current version range and after updating it in your project the build failed.
mocha is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.
options
parameter (@plroebuck).jsonc
extension (@sstephant)These are soft-deprecated, and will emit a warning upon use. Support will be removed in (likely) the next major version of Mocha:
this.skip()
in "before each" hooks (@juergba)--allow-uncaught
for uncaught exceptions thrown inside hooks (@givanse)and some regressions:
Suite
cloning by copying root
property (@fatso83)The new version differs by 28 commits.
f4fc95a
Release v6.1.0
bd29dbd
update CHANGELOG for v6.1.0 [ci skip]
aaf2b72
Use cwd-relative pathname to load config file (#3829)
b079d24
upgrade deps as per npm audit fix; closes #3854
e87c689
Deprecate this.skip() for "after all" hooks (#3719)
81cfa90
Copy Suite property "root" when cloning; closes #3847 (#3848)
8aa2fc4
Fix issue 3714, hide pound icon showing on hover header on docs page (#3850)
586bf78
Update JS-YAML to address security issue (#3845)
d1024a3
Update doc examples "tests.html" (#3811)
1d570e0
Delete "/docs/example/chai.js"
ade8b90
runner.js: "self.test" undefined in Browser (#3835)
0098147
Replace findup-sync with find-up for faster startup (#3830)
d5ba121
Remove "package" flag from sample config file because it can only be passes as CLI arg (#3793)
a3089ad
update package-lock
75430ec
Upgrade yargs-parser dependency to avoid loading 2 copies of yargs
There are 28 commits in total.
See the full diff
There is a collection of frequently asked questions. If those donβt help, you can always ask the humans behind Greenkeeper.
Your Greenkeeper Bot π΄
Elements should know of their children and their parents.
Which brings me to a question I have. An ObjectType
is basically a collection of PropertyType
elements (which are essentially key-value pairs). To have the same format, and potentially help in future features, should ArrayType
collections consist of some kind of IndexType
that instead of having a key, it has an index.
Reason being, an element needs to know where it is in the tree of elements, and without this having an explicit element (like IndexType
), it would need to know the index for itself as well. If it did have this IndexType
, it could know the array that it is in plus it's location without storing that.
Hope that makes sense.
What do we do when we receive:
One piece that is going to make this library great is events. Allowing for code to listen in on parts of the tree is a great feature, especially for React components. Basically, when a setter is called, it sends a message up the tree and calls any attached listeners.
This will be dependent upon #17.
If you have an array, you should be able to call arr.isEmpty
and get back true/false depending on if array.content.length === 0
.
Don't be afraid to start with 1.0.0 and bump to 2.0.0 if things changes.
You will save our lives: imagine us when seeing 0.10 and 0.11, consider it as a minor release and then you realise that there is a breaking change behind that.
Allow for some syntax like the following.
obj.get('foo', { ignoreCase: true });
obj.set('foo', 'bar', { ignoreCase: true });
Is there a way we can handle elements inside of attributes better. The current way seems tied directly to one use case, where a property at the root of the object is an element.
preToRefract
hook?preAttributeConvert
hook?Then we leave it up to the user to decide how this works in those methods.
Better to have multiple files and associated tests to figure out what is going on and then tests and files only have one reason to change.
Right now, we have methods that include the keyword get
. Of course there is the main method get
for arrays and objects. Below are the methods we currently have get
on.
getMember
getKey
getValue
Should we have get
on these methods?
Looks like Minim is actually broken when using source maps, or seemingly any refracted element in metadata:
var minim = require('minim').namespace();
var el = minim.fromRefract({
element: 'object',
meta: {
title: {
element: 'string',
content: 'Test'
}
}
content: []
});
console.log(el.title);
// => { element: 'string', content: 'test' }
// Should be => 'test'
console.log(el.meta.get('title').element);
// => 'object'
// Should be => 'string'
The result of this bug is that Minim is basically unusable with source maps, since they break out the title/description/etc into elements and you then need to determine whether to access el.title
or el.title.content
. It also breaks the concept of el.title
always returning a simple non-refracted type.
Interestingly, el.toRefract()
seems to output the correct data.
cc @smizell
The get
method has different functionality depending on the class. Should it return the toValue()
of the content, or should it return this
when no input is given?
There are many tests for arrays that are actually for the collection class. These tests should be moved to be clearer.
We need to decide if we are going to pull the serialization formats out of Minim. I can see leaving the main Refract format in there, but pulling compact and embedded out.
This is a breaking change and I would wait a bit before we do this to settle the current unreleased changes.
We should rename the current find*
methods to filter*
because in JavaScript find
methods are expected to return the single element and our implementation if the method is inconsistent. We should implement find*
to return first matching item.
Take the following example:
// JS Array `find`
["a", "b", "c"].find(c => c === "b"); // "b"
// Array Element `find`
ArrayElement(["a", "b", "c"]).find(c => c.toValue() === "b"); // ["b"]
// We return array slice of B not just B.
There needs to be a way to handle errors in Minim. Some options:
minim.toElement({ foo: 'bar'})
.get('a') // Undefined and throws error
.set('b', 'c')
With the latter, we can attach methods from every element, and then (try to) mimic promises.
minim.toElement({ foo: 'bar'})
.get('a') // Undefined and should create an error
.set('b', 'c') // The error object would simply pass itself along
.then(function() { console.log('all good') })
.catch(function(error) { console.log('oops', error) })
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.