<commit_before>/**
 * @module  lib/Service
 * used to create models
 * uses mixin properties from ServiceBox either as adapter or proxy,
 * according as the underlying API is normalized or full implemented
 */
import Module from './module';
import ServiceBox from './service-box';
import isArrayLike from '../helpers/array/is-array-like';
import merge from '../helpers/array/merge';

const SERVICE_TYPE = 'service';

class Service extends Module {
	
	static get type() {
		return SERVICE_TYPE;
	}

	get type() {
		return SERVICE_TYPE;
	}

	set resource(resource) {
		this._resource = resource;
	}

	get resource() {
		return this._resource;
	}

	constructor(options={}) {

		let box = options.box || new ServiceBox();
		options.box = box;

		super(options);

		this.length = 0;

		this.resource = options.resource || this;

		this.create(options.data);
	}

	/**
	 * connect to a service box data
	 * @return {mixed} data or promise
	 */
	connect() {
		return this;
	}

	/**
	 * disconnect from service box data
	 * @return {boolean}
	 */
	disconnect() {
		return this;
	}

	/**
	 * fetches data from proxied resource
	 * @param {mixed} reduce a function or a value or a key for reducing the data set 
	 * @return {Promise} resolve or error
	 */
	fetch(reduce) {
		return this;
	}

	/**
	 * drop in replacement when working with this object instead of promises
	 * @return {[type]} [description]
	 */
	then(cb) {
		cb(this.data);
		return this;
	}

	/**
	 * drop in replacement when working with this object instead of promises
	 * @return {[type]} [description]
	 */
	catch() {
		// never an error, while working with vanilla js
		return this;
	}

	/**
	 * creates a new item or a whole data set
	 * @param  {mixed} data to be created on this service and on remote when save is called or
	 *                      param remote is true
	 * @return {mixed} newly created item or collection
	 */
	create(data) {
		
		if (isArrayLike(data)) {
			merge(this, data);
		} else if(data) {
			this.add(data);
		}

		return this;
	}


	/**
	 * reads a data set, reduced by reduced parameter
	 * @param {mixed} reduce a function or a value or a key for reducing the data set 
	 * @return {mixed} 
	 */
	read(reduce) {
		return this.findIndex(reduce);<commit_msg>creates create and update methods for app storage and saves projects<commit_after>/**
 * @module  lib/Service
 * used to create models
 * uses mixin properties from ServiceBox either as adapter or proxy,
 * according as the underlying API is normalized or full implemented
 */
import Module from './module';
import ServiceBox from './service-box';
import isArrayLike from '../helpers/array/is-array-like';
import merge from '../helpers/array/merge';

const SERVICE_TYPE = 'service';

class Service extends Module {
	
	static get type() {
		return SERVICE_TYPE;
	}

	get type() {
		return SERVICE_TYPE;
	}

	set resource(resource) {
		this._resource = resource;
	}

	get resource() {
		return this._resource;
	}

	constructor(options={}) {

		let box = options.box || new ServiceBox();
		options.box = box;

		super(options);

		this.length = 0;

		this.resource = options.resource || this;
	}

	each(obj, callback) {
		
		if (typeof obj === 'function') {
			callback = obj;
			obj = this;
		}
		
		let isLikeArray = isArrayLike(obj);
		let value;
		let i = 0;

		if (isLikeArray) {

			let length = obj.length;

			for (; i < length; i++) {
				value = callback.call(obj[i], i, obj[i]);

				if (value === false) {
					break;
				}
			}
		}

		return this;
	}

	/**
	 * connect to a service box data
	 * @return {mixed} data or promise
	 */
	connect() {
		return this;
	}

	/**
	 * disconnect from service box data
	 * @return {boolean}
	 */
	disconnect() {
		return this;
	}

	/**
	 * fetches data from proxied resource
	 * @param {mixed} reduce a function or a value or a key for reducing the data set 
	 * @return {Promise} resolve or error
	 */
	fetch(reduce) {
		return this;
	}

	/**
	 * drop in replacement when working with this object instead of promises
	 * @return {[type]} [description]
	 */
	then(cb) {
		cb(this.data);
		return this;
	}

	/**
	 * drop in replacement when working with this object instead of promises
	 * @return {[type]} [description]
	 */
	catch() {
		// never an error, while working with vanilla js
		return this;
	}

	init(data) {

		if (isArrayLike(data)) {
			merge(this, data);
		} else if(data) {
			this.add(data);
		}
	}

	/**
	 * creates a new item or a whole data set
	 * @param  {mixed} data to be created on this service and on remote when save is called or
	 *                      param remote is true
	 * @return {mixed} newly created item or collection
	 */
	create(data) {

		this.init(data);

		return this;
	}

	where(characteristics) {

		if (!this.isSynced) {
			console.warn(`${this} is out of sync!`);
		}

		let results = {};
		results.length = 0;

		this.each((i, item) => {
			if (typeof characteristics === 'function' && characteristics(item)) {
				results[i] = item;
				results.length += 1;
			} else if (typeof characteristics === 'object') {

				let hasCharacteristics = true;

				for (let key in characteristics) {
					if (!item.hasOwnProperty(key) || item[key] !== characteristics[key]) {
						hasCharacteristics = false;
					}
				}

				if (hasCharacteristics) {
					results[i] = item;
					results.length += 1;
				}
			}
		})

		return results;
	}


	/**
	 * reads a data set, reduced by reduced parameter
	 * @param {mixed} reduce a function or a value or a key for reducing the data set 
	 * @return {mixed} 
	 */
	read(reduce) {
		return this.findIndex(reduce);
{'fraction_non_alphanumeric': 0.12321173104434907, 'fraction_numerical': 0.001072961373390558, 'mean_word_length': 2.897560975609756, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6840931', 'n_tokens_mistral': 1973, 'n_tokens_neox': 1688, 'n_words': 605}