menilite 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2228ad770fdb5fc5a5343e3c27644c350c623e86
4
- data.tar.gz: 0cc3d1e02e37a9a080e639f83c0ada12a23d87e2
3
+ metadata.gz: 7ff9db82163232317d82fcbfe864f020e713e3dc
4
+ data.tar.gz: b4511f2ece8e027dd0f0cac02632e3676d1f1fe3
5
5
  SHA512:
6
- metadata.gz: c3d37d812e06e8c8f34c6b728d145a1895722809981252a7e8f2f1f0f77b37aad4dda12176a5581d6506ac06ff538b8dbeff1b3c753f65e341e4c9800aec0804
7
- data.tar.gz: cdda6647a91b84a2617df44ed5c85f1ca9d785e9402e15da7c95eb4e8477fba3d56350ab9e65e4f5b6339ee7c30097c532f714def55b53f5c6a8466f66547058
6
+ metadata.gz: b18f844aca4213efcd77f13c6598a5b2ed16326849221d221e177d668cd751a34054e358c6c7788c23a3ef484278c4326fefe3a3b29faec3d249e1718d9a7f6b
7
+ data.tar.gz: 004d4eaeceb5e821e0457aeb4416930cde8e769d04a0a33fe0f61b62919f82834927c2d03387cf787b333addf9e25f09cc4857132e36c0973aa2ef5975f4f639
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Menilite
2
2
 
3
+ [![Build Status](https://travis-ci.org/youchan/menilite.svg?branch=master)](https://travis-ci.org/youchan/menilite)
4
+
3
5
  An isomophic web programming framework in Ruby.
4
6
  Ruby codes also run on the client side by using [Opalrb](http://opalrb.org).
5
7
 
data/Rakefile CHANGED
@@ -1,6 +1,11 @@
1
+ require 'opal'
2
+ require 'opal-browser'
3
+ require 'opal/rspec/rake_task'
1
4
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
5
 
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
6
+ Opal::RSpec::RakeTask.new(:default) do |server, task|
7
+ task.pattern = 'spec/opal/**/*_spec.rb'
8
+ server.append_path File.expand_path('../lib', __FILE__)
9
+ server.source_map = true
10
+ server.debug = true
11
+ end
@@ -3,6 +3,8 @@ require "menilite/version"
3
3
  require "menilite/helper"
4
4
 
5
5
  if RUBY_ENGINE == "opal"
6
+ require 'native'
7
+ require 'browser'
6
8
  require 'menilite/model'
7
9
  require 'menilite/controller'
8
10
  require 'menilite/client/store'
@@ -1,6 +1,6 @@
1
1
  require 'securerandom'
2
2
 
3
- Menilite.if_client do
3
+ if RUBY_ENGINE == 'opal'
4
4
  require 'browser/http'
5
5
  require 'opal-parser'
6
6
  end
@@ -13,6 +13,9 @@ class String
13
13
  end
14
14
 
15
15
  module Menilite
16
+ class ValidationError < StandardError; end;
17
+ class TypeError < StandardError; end;
18
+
16
19
  class Model
17
20
  include Menilite::Helper
18
21
 
@@ -21,15 +24,25 @@ module Menilite
21
24
  def initialize(fields = {})
22
25
  self.class.init
23
26
 
24
- if client?
25
- fields = fields.clone
26
- else
27
+ if server?
27
28
  fields = fields.map{|k,v| [k.to_sym, v] }.to_h
29
+ end
30
+
31
+ @guid = fields.delete(:id) || SecureRandom.uuid
32
+
33
+ self.class.field_info.select{|_, i| i.type == :reference}.each do |name, info|
34
+ fields[:"#{name}_id"] = fields[info.name] if fields.has_key?(info.name)
35
+ end
36
+
37
+ fields.each{|k, v| type_validate(k, v) }
38
+ fields = fields.map{|k,v| [k, convert_value(self.class.field_info[k].type, v)] }.to_h
39
+
40
+ if server?
28
41
  fields.merge!(self.class.privilege_fields)
29
42
  end
30
43
 
31
44
  defaults = self.class.field_info.map{|k, d| [d.name, d.params[:default]] if d.params.has_key?(:default) }.compact.to_h
32
- @guid = fields.delete(:id) || SecureRandom.uuid
45
+ fields = defaults.merge(fields)
33
46
  @fields = defaults.merge(fields)
34
47
  @listeners = {}
35
48
  end
@@ -39,6 +52,7 @@ module Menilite
39
52
  end
40
53
 
41
54
  def save(&block)
55
+ self.validate_all
42
56
  self.class.store.save(self, &block)
43
57
  self
44
58
  end
@@ -88,8 +102,13 @@ module Menilite
88
102
  @action_info ||= {}
89
103
  end
90
104
 
105
+ def validators
106
+ @validators ||= {}
107
+ end
108
+
91
109
  def save(collection, &block)
92
110
  self.init
111
+ colection.each {|obj| obj.validate_all }
93
112
  self.store.save(collection, &block)
94
113
  end
95
114
 
@@ -105,7 +124,7 @@ module Menilite
105
124
 
106
125
  def fetch(filter: {}, order: nil)
107
126
  self.init
108
- filter = filter.map{|k, v| type_convert(k, v) }.to_h
127
+ filter = filter.map{|k, v| type_convert(k.to_sym, v) }.to_h
109
128
 
110
129
  if_server do
111
130
  filter.merge!(privilege_filter)
@@ -118,7 +137,7 @@ module Menilite
118
137
  end
119
138
 
120
139
  def type_convert(key, value)
121
- field_info = self.field_info[key.to_s] || self.field_info[key.to_s.sub(/_id\z/,'')]
140
+ field_info = self.field_info[key] || self.field_info[key.to_s.sub(/_id\z/,'').to_sym]
122
141
  raise "no such field #{key} in #{self}" unless field_info
123
142
  converted = case field_info.type
124
143
  when :boolean
@@ -152,7 +171,11 @@ module Menilite
152
171
  return unless params[:server]
153
172
  end
154
173
 
155
- field_info[name.to_s] = FieldInfo.new(name, type, params)
174
+ if type == :reference
175
+ field_info[:"#{name}_id"] = FieldInfo.new(name, type, params)
176
+ else
177
+ field_info[name] = FieldInfo.new(name, type, params)
178
+ end
156
179
 
157
180
  self.instance_eval do
158
181
  if type == :reference
@@ -160,6 +183,7 @@ module Menilite
160
183
 
161
184
  define_method(name) do
162
185
  id = @fields[field_name.to_sym]
186
+ next nil unless id
163
187
  model_class = Object.const_get(name.to_s.camel_case)
164
188
  model_class[id]
165
189
  end
@@ -172,14 +196,18 @@ module Menilite
172
196
  end
173
197
 
174
198
  define_method(field_name) do
175
- @fields[field_name.to_sym]
199
+ value = @fields[field_name.to_sym]
200
+ if type.is_a?(Hash) && type.keys.first == :enum
201
+ value = type[:enum][value]
202
+ end
203
+ value
176
204
  end
177
205
 
178
206
  define_method(field_name + "=") do |value|
179
207
  unless type_validator(type).call(value, name)
180
- raise 'type error'
208
+ raise TypeError.new("type error: field_name: #{name}, value: #{value}")
181
209
  end
182
- @fields[field_name.to_sym] = value
210
+ @fields[field_name.to_sym] = convert_value(type, value)
183
211
  handle_event(:change, field_name.to_sym, value)
184
212
  end
185
213
  end
@@ -223,6 +251,15 @@ module Menilite
223
251
  end
224
252
  end
225
253
 
254
+ def validation(field_name, params = {}, &block)
255
+ params.each do |k, v|
256
+ if validator = Validators[k, v]
257
+ (validators[field_name] ||= []) << validator.new(field_name)
258
+ end
259
+ end
260
+ (validators[field_name] ||= []) << Validator.new(field_name, &block) if block
261
+ end
262
+
226
263
  def find(id)
227
264
  self.init
228
265
 
@@ -280,27 +317,58 @@ module Menilite
280
317
  def type_validator(type)
281
318
  case type
282
319
  when :string
283
- -> (value, name) { value.is_a? String }
320
+ -> (value, name) { value.nil? || value.is_a?(String) }
284
321
  when :int
285
- -> (value, name) { value.is_a? Integer }
322
+ -> (value, name) { value.nil? || value.is_a?(Integer) }
286
323
  when :boolean
287
- -> (value, name) { value == true || value == false }
324
+ -> (value, name) { value.nil? || value == true || value == false }
288
325
  when :date
289
- -> (value, name) { value.is_a? Date }
326
+ -> (value, name) { value.nil? || value.is_a?(Date) || value.is_a?(String) }
290
327
  when :time
291
- -> (value, name) { value.is_a? Time }
328
+ -> (value, name) { value.nil? || value.is_a?(Time) }
329
+ when Hash
330
+ if type.keys.first == :enum
331
+ -> (value, name) { value.nil? || value.is_a?(Integer) || type[:enum].include?(value) }
332
+ else
333
+ raise TypeError.new("type error")
334
+ end
292
335
  when :reference
293
- -> (value, name) { valiedate_reference(value, name) }
336
+ -> (value, name) { value.nil? || validate_reference(value, name) }
337
+ else
338
+ raise TypeError.new("type error. type: #{type.inspect}")
294
339
  end
295
340
  end
296
341
 
297
- def valiedate_reference(value, name)
298
- return false unless value.is_a? String
342
+ def validate_reference(value, name)
343
+ return false unless value.is_a?(String) || value.is_a?(Menilite::Model)
299
344
 
300
- model_class = Object.const_get(name.camel_case)
345
+ model_class = Object.const_get(name.to_s.camel_case)
301
346
  not model_class[value].nil?
302
347
  end
303
348
 
349
+ def type_validate(name, value)
350
+ field_info = self.class.field_info[name]
351
+ field_info or raise ArgumentError.new("field '#{name}' is not defind")
352
+
353
+ type_validator = type_validator(field_info.type)
354
+ type_validator.call(value, field_info.name) or raise TypeError.new("type error: field_name: #{field_info.name}, value: #{value}")
355
+ end
356
+
357
+ def validate(name, value)
358
+ validator = self.class.validators[name]
359
+ if validator
360
+ messages = validator.map {|validator| validator.validate(value) }.compact
361
+ messages.empty? or raise ValidationError.new(messages.join(','))
362
+ end
363
+ end
364
+
365
+ def validate_all
366
+ self.fields.each do |k, v|
367
+ type_validate(k, v)
368
+ validate(k, v)
369
+ end
370
+ end
371
+
304
372
  def to_h
305
373
  @fields.merge(id: @guid)
306
374
  end
@@ -311,6 +379,18 @@ module Menilite
311
379
 
312
380
  private
313
381
 
382
+ def convert_value(type, value)
383
+ if type.is_a?(Hash) && type.keys.first == :enum
384
+ if value.is_a?(Integer)
385
+ value
386
+ else
387
+ type[:enum].index(value)
388
+ end
389
+ else
390
+ value
391
+ end
392
+ end
393
+
314
394
  def get_listeners(event, field_name)
315
395
  @listeners[event].try {|l1| l1[field_name] || [] } || []
316
396
  end
@@ -322,11 +402,37 @@ module Menilite
322
402
  end
323
403
 
324
404
  def resolve_references(key, value)
325
- if self.class.field_info.has_key?(key.to_s) && self.class.field_info[key.to_s].type == :reference
405
+ if self.class.field_info.has_key?(key) && self.class.field_info[key].type == :reference
326
406
  ["#{key}_id".to_sym, value.id]
327
407
  else
328
408
  [key, value]
329
409
  end
330
410
  end
411
+
412
+ class Validator
413
+ def initialize(name, &block)
414
+ @proc = block
415
+ end
416
+
417
+ def validate(value)
418
+ @proc.call(value)
419
+ end
420
+ end
421
+ class PresenceValidator < Validator
422
+ def initialize(name)
423
+ super(name) {|value| "#{name} must not be empty" if value.nil? || value == "" }
424
+ end
425
+ end
426
+
427
+ class Validators
428
+ def self.[](key, value)
429
+ case key
430
+ when :presence
431
+ if value == true
432
+ PresenceValidator
433
+ end
434
+ end
435
+ end
436
+ end
331
437
  end
332
438
  end
@@ -5,7 +5,7 @@ module Menilite
5
5
  def self.create_model(model_class)
6
6
  klass = Class.new(::ActiveRecord::Base) do
7
7
  model_class.field_info.select{|name, field| field.type == :reference }.each do |name, field|
8
- belongs_to field.name, primary_key: 'guid', foreign_key: name + '_guid', class_name: name.capitalize
8
+ belongs_to name, primary_key: 'guid', foreign_key: "#{name}_guid", class_name: name.to_s.capitalize
9
9
  #klass.instance_eval { define_method(name + '_id') { send(name + '_guid') } }
10
10
  end
11
11
  end
@@ -1,3 +1,3 @@
1
1
  module Menilite
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -21,6 +21,9 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "bundler", "~> 1.12"
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
23
  spec.add_development_dependency "rspec", "~> 3.0"
24
+ spec.add_development_dependency 'opal-rspec', '0.5.0'
24
25
 
26
+ spec.add_runtime_dependency "opal"
27
+ spec.add_runtime_dependency 'opal-browser'
25
28
  spec.add_runtime_dependency "sinatra-activerecord"
26
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: menilite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - youchan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-29 00:00:00.000000000 Z
11
+ date: 2016-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,48 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: opal-rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: opal
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: opal-browser
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
55
97
  - !ruby/object:Gem::Dependency
56
98
  name: sinatra-activerecord
57
99
  requirement: !ruby/object:Gem::Requirement