menilite 0.2.1 → 0.3.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.
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