yogo-db 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'yogo-db'
@@ -0,0 +1,9 @@
1
+ Feature: something something
2
+ In order to something something
3
+ A user something something
4
+ something something something
5
+
6
+ Scenario: something something
7
+ Given inspiration
8
+ When I create a sweet new gem
9
+ Then everyone should see how awesome I am
@@ -0,0 +1,4 @@
1
+ # Load Application pieces
2
+ require 'yogo/rack/model_lookup'
3
+ require 'yogo/schema_app'
4
+ require 'yogo/data_app'
@@ -0,0 +1,89 @@
1
+ module Yogo
2
+ module Data
3
+ module DefaultMethods
4
+
5
+ def self.included(base)
6
+ base.send(:extend, ClassMethods)
7
+ base.send(:include, InstanceMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def schema
12
+ class_variable_get(:@@schema)
13
+ end
14
+
15
+ def parse_json(body)
16
+ json = JSON.parse(body)
17
+ props = labeled_properties
18
+ ret = {}
19
+ # I don't think we care about other items passed back.
20
+ json['data'].each_pair do |key,value|
21
+ property = props.select{|p| p.options['label'] == key || p.name.to_s == key}
22
+ return nil unless property.length == 1
23
+ # raise Exception, "There shouldn't be more the one property with the same label"
24
+ ret[property.first.name] = value
25
+ end
26
+ return ret
27
+ end
28
+
29
+ def labeled_properties
30
+ properties.select{|p| !p.options['label'].nil? }
31
+ end
32
+
33
+ def unlabeled_properties
34
+ properties.select{|p| p.options['label'].nil? }
35
+ end
36
+ end
37
+
38
+ module InstanceMethods
39
+ def to_url
40
+ raise Exception, "Need to save before item has a url" if new?
41
+ "/data/" << self.model.name << "/" << yogo_id.to_s
42
+ end
43
+
44
+ def attributes_by_label
45
+ attributes = {}
46
+ properties.each do |property|
47
+ label = property.options['label']
48
+ next if label.nil?
49
+ name = property.name
50
+ next unless model.public_method_defined?(name)
51
+ attributes[label] = __send__(name)
52
+ end
53
+ return attributes
54
+ end
55
+
56
+ def labeled_properties
57
+ self.class.labeled_properties
58
+ end
59
+
60
+ def unlabeled_properties
61
+ self.class.unlabeled_properties
62
+ end
63
+
64
+ def to_json(*args)
65
+ options = args.first || {}
66
+ options = options.to_h if options.respond_to?(:to_h)
67
+
68
+ result = as_json(*args)
69
+
70
+ if options.fetch(:to_json, true)
71
+ result.to_json
72
+ else
73
+ result
74
+ end
75
+ end
76
+
77
+ def as_json(*a)
78
+ data = labeled_properties.reduce({}){ |result, property| result.merge(property.name => __send__(property.name)) }
79
+ default_data = unlabeled_properties.reduce({}){ |result, property| result.merge(property.name => __send__(property.name)) }
80
+ default_data.merge({
81
+ :url => self.to_url,
82
+ :data => data
83
+ })
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,50 @@
1
+ require 'sinatra'
2
+
3
+ module Yogo
4
+ class DataApp < ::Sinatra::Base
5
+ before do
6
+ content_type :json
7
+ end
8
+
9
+ get '/data/:model_id/?' do
10
+ { :content => env['yogo.resource'].all.to_json(:to_json => false) }.to_json
11
+ end
12
+
13
+ get '/data/:model_id/:id' do
14
+ resource = env['yogo.resource']
15
+ item = resource.get(params[:id])
16
+
17
+ { :content => item }.to_json
18
+ end
19
+
20
+ post '/data/:model_id' do
21
+ resource = env['yogo.resource']
22
+ opts = resource.parse_json(request.body.read) rescue nil
23
+
24
+ halt(401, 'Invalid Format') if opts.nil?
25
+
26
+ item = resource.new(opts)
27
+
28
+ halt(500, 'Could not save item') unless item.save
29
+
30
+ response['Location'] = item.to_url
31
+ response.status = 201
32
+ end
33
+
34
+ put '/schema/:model_id/:id' do
35
+ resource = env['yogo.resource']
36
+ item = resource.get(params[:id])
37
+ opts = resource.parse_json(request.body.read) rescue nil
38
+ halt(401, 'Invalid Format') if opts.nil?
39
+
40
+ halt(500, 'Could not update schema') unless item.update(opts)
41
+
42
+ { :content => item }.to_json
43
+ end
44
+
45
+ delete '/schema/:model_id/:id' do
46
+ env['yogo.resource'].get(params[:id]).destroy
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,138 @@
1
+ require 'dm-types'
2
+ require 'yogo/datamapper/model/configuration'
3
+ require 'yogo/operations'
4
+
5
+ class Schema
6
+ include ::DataMapper::Resource
7
+ # include Yogo::DataMapper::Model::Configuration
8
+ include Dataflow
9
+
10
+ # Common::Properties::UUIDKey[self]
11
+ property :id, ::DataMapper::Property::UUID, {:key => true, :default => lambda{|*args| UUIDTools::UUID.timestamp_create}}
12
+
13
+ property :name, String, :required => true, :key => true, :unique => true
14
+
15
+ property :operation_definitions, Yaml, :lazy => false, :default => []
16
+
17
+ before :save, :reset_data_model
18
+ before :destroy, :destroy_data_model_bucket
19
+
20
+ # How do we know what variables need to be constantized.
21
+ # Also, we might have more or fewer then 3 parameters in the operation
22
+ # def operation_definitions
23
+ # @__operation_definitions ||= attribute_get(:op_defs)
24
+ # end
25
+
26
+ # Set only the operations we want, don't add to them
27
+ def operations=(ops)
28
+ # Clear the current operations
29
+ self.operation_definitions = []
30
+ # Load operations with the set given
31
+ ops.each{|op| operation(*op) }
32
+ end
33
+
34
+ def operation(op_name, *args)
35
+ op_def = replace_nil_items([op_name.to_s, args].flatten)
36
+
37
+ unless self.operation_definitions.include?(op_def)
38
+ # We need to dup this else the model doesn't get marked as dirty and won't save.
39
+ self.operation_definitions = self.operation_definitions.dup << op_def
40
+ end
41
+ end
42
+
43
+ def data_model
44
+ @data_model ||= by_need { gen_model }
45
+ end
46
+
47
+ def to_url
48
+ "/schema/#{self.name}"
49
+ end
50
+
51
+ def to_json(*args)
52
+ options = args.first || {}
53
+ options = options.to_h if options.respond_to?(:to_h)
54
+
55
+ result = as_json(*args)
56
+
57
+ if options.fetch(:to_json, true)
58
+ result.to_json
59
+ else
60
+ result
61
+ end
62
+ end
63
+
64
+ def as_json(*a)
65
+ {
66
+ :guid => self.to_url,
67
+ :name => self.name,
68
+ :operations => self.operation_definitions,
69
+ }
70
+ end
71
+
72
+ REQUIRED_JSON_KEYS=[:name, :operations]
73
+
74
+ def self.parse_json(body)
75
+ json = JSON.parse(body)
76
+
77
+ ret = { :name => json['name'], :operations => json['operations'] }
78
+
79
+ return nil if REQUIRED_JSON_KEYS.any? { |r| ret[r].nil? }
80
+
81
+ ret
82
+ end
83
+
84
+ def to_proc
85
+ base_op = Yogo::DataMapper::Model::Operations['add/default_properties']
86
+ # base_op = Yogo::DataMapper::Model::Operations['add/yogo_methods']
87
+ ops = operation_definitions.map{|op_def| Yogo::DataMapper::Model::Operations[op_def.first] }
88
+ partial_ops = []
89
+ ops.each_with_index do |op, i|
90
+ next unless op
91
+ partial_ops[i] = op.partial(X, *operation_definitions[i][1..-1])
92
+ end
93
+ partial_ops << Yogo::DataMapper::Model::Operations['add/yogo_methods']
94
+ partial_ops.compact!
95
+ partial_ops.reduce(base_op){|composed, partial_op| composed * partial_op}
96
+
97
+ end
98
+
99
+ private
100
+
101
+ def gen_model
102
+ id = attribute_get(:id)
103
+ model_name = attribute_get(:name)
104
+ base_model = ::DataMapper::Model.new do
105
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
106
+
107
+ def self.default_storage_name
108
+ "#{id}"
109
+ end
110
+
111
+ def self.name
112
+ "#{model_name}"
113
+ end
114
+ RUBY
115
+ end
116
+
117
+ base_model.class_variable_set(:@@schema, self)
118
+ self.to_proc[base_model]
119
+ base_model.auto_upgrade!
120
+ return base_model
121
+ end
122
+
123
+ def reset_data_model
124
+ @data_model = nil
125
+ end
126
+
127
+ def destroy_data_model_bucket
128
+ data_model.auto_migrate_down!
129
+ end
130
+
131
+ def generate_column_uuid
132
+ "col_#{UUIDTools::UUID.timestamp_create.to_s.gsub('-', '_')}"
133
+ end
134
+
135
+ def replace_nil_items(array)
136
+ array.map{|i| i.nil? ? generate_column_uuid : i }
137
+ end
138
+ end # Configuration
@@ -0,0 +1,2 @@
1
+ require 'yogo/operations/basic'
2
+ require 'yogo/operations/file'
@@ -0,0 +1,31 @@
1
+ require 'yogo/operation'
2
+ require 'yogo/data/default_methods'
3
+
4
+ # Move into DataMapper::Property extension file
5
+ unless ::DataMapper::Property.accepted_options.include?('label')
6
+ ::DataMapper::Property.accept_options('label')
7
+ end
8
+
9
+ module Operations
10
+ Yogo::DataMapper::Model::Operations['add/yogo_methods'] = Yogo::Op.on(::DataMapper::Model) do |model|
11
+
12
+ # model.define some properties
13
+
14
+ model.send(:include, Yogo::Data::DefaultMethods)
15
+
16
+ model
17
+
18
+ end
19
+
20
+ Yogo::DataMapper::Model::Operations['add/yogo_id_property'] =
21
+ Yogo::DataMapper::Model::Operations['add/property'].partial(X, :yogo_id, 'Serial', {})
22
+ Yogo::DataMapper::Model::Operations['add/created_at_property'] =
23
+ Yogo::DataMapper::Model::Operations['add/property'].partial(X, :created_at, 'DateTime', {})
24
+ Yogo::DataMapper::Model::Operations['add/updated_at_property'] =
25
+ Yogo::DataMapper::Model::Operations['add/property'].partial(X, :updated_at, 'DateTime', {})
26
+
27
+ Yogo::DataMapper::Model::Operations['add/default_properties'] =
28
+ Yogo::DataMapper::Model::Operations['add/yogo_id_property'] *
29
+ Yogo::DataMapper::Model::Operations['add/created_at_property'] *
30
+ Yogo::DataMapper::Model::Operations['add/updated_at_property']
31
+ end
@@ -0,0 +1,44 @@
1
+ # Operation to add a file type here
2
+
3
+ Yogo::DataMapper::Model::Operations['add/file'] = Yogo::Op.on(::DataMapper::Model) do |model, name, options|
4
+
5
+ uploader = Class.new(CarrierWave::Uploader::Base)
6
+ uploader.storage(:file)
7
+ uploader.class_eval %{
8
+ def store_dir
9
+ File.join('assets', '#{model.schema.id}', '#{name}')
10
+ end
11
+
12
+ def filename
13
+ # Digest::MD5.hexdigest(self.read)
14
+ UUIDTools::UUID.timestamp_create
15
+ end
16
+ }, __FILE__, __LINE__+1
17
+
18
+ # model.define some properties
19
+ model.class_eval do
20
+ without_auto_validations do
21
+ # property :content_type, String
22
+ # property :description, String
23
+ property "#{name}_asset_file".to_sym, String, options
24
+ property "#{name}_original_filename".to_sym, String
25
+ end
26
+
27
+ mount_uploader name.to_sym, uploader, :mount_on => "#{name}_asset_file".to_sym
28
+ after "#{name}=".to_sym, "write_#{name}_identifier".to_sym
29
+ after "#{name}=".to_sym, "set_#{name}_original_filename".to_sym
30
+ end
31
+
32
+ model.class_eval %{
33
+ private
34
+
35
+ def set_#{name}_original_filename
36
+ original_filename = #{name}.send(:original_filename)
37
+ attribute_set(:#{name}_original_filename, original_filename)
38
+ end
39
+ }, __FILE__, __LINE__+1
40
+
41
+
42
+ model
43
+
44
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+ require 'dm-core'
3
+ require 'yogo/model/schema'
4
+
5
+ module Yogo
6
+ module Rack
7
+ class ModelLookup
8
+ def initialize(app, options = {})
9
+ paths = options[:paths] || ['data']
10
+ @app = app
11
+ @scope = options[:scope] || lambda { |*args| return Schema }
12
+ @base_regexp = /^\/(#{paths.join('|')})\/((\w|-|\s)+)/
13
+ end
14
+
15
+ def call(env)
16
+ if env['PATH_INFO'] =~ @base_regexp
17
+ model_id = $2
18
+ env['yogo.schema'], env['yogo.resource'] = get_model(model_id, env)
19
+ return [ 404, {'Content-Type' => 'text/plain'}, ["#{model_id} not found"] ] if env['yogo.schema'].nil?
20
+ end
21
+
22
+ @app.call(env)
23
+ end
24
+
25
+ private
26
+
27
+ def get_model(model_id, env)
28
+ config = @scope.call(env).first(:name => model_id)
29
+
30
+ unless config.nil?
31
+ return config, config.data_model
32
+ end
33
+ end
34
+
35
+ end # ModelLookup
36
+ end # Rack
37
+ end # Yogo
@@ -0,0 +1,43 @@
1
+ require 'sinatra'
2
+
3
+ module Yogo
4
+ class SchemaApp < ::Sinatra::Base
5
+ before do
6
+ content_type :json
7
+ end
8
+
9
+ get '/schema/?' do
10
+ { :content => Schema.all.to_json(:to_json => false) }.to_json
11
+ end
12
+
13
+ get '/schema/:model_id/?' do
14
+ { :content => env['yogo.schema'] }.to_json
15
+ end
16
+
17
+ post '/schema/?' do
18
+ opts = Schema.parse_json(request.body.read) rescue nil
19
+ halt(401, 'Invalid Format') if opts.nil?
20
+ schema = Schema.new(opts)
21
+
22
+ halt(500, 'Could not save schema') unless schema.save
23
+
24
+ response['Location'] = schema.to_url
25
+ response.status = 201
26
+ end
27
+
28
+ put '/schema/:model_id' do
29
+ schema = env['yogo.schema']
30
+ opts = Schema.parse_json(request.body.read) rescue nil
31
+ halt(401, 'Invalid Format') if opts.nil?
32
+
33
+ halt(500, 'Could not update schema') unless schema.update(opts)
34
+
35
+ { :content => schema }.to_json
36
+ end
37
+
38
+ delete '/schema/:model_id' do
39
+ env['yogo.schema'].destroy
40
+ end
41
+
42
+ end
43
+ end