frigate 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +10 -0
- data/Guardfile +67 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +13 -0
- data/frigate.gemspec +31 -0
- data/lib/frigate/form/association.rb +94 -0
- data/lib/frigate/form/base.rb +141 -0
- data/lib/frigate/form/property.rb +40 -0
- data/lib/frigate/form/synchronizer/base.rb +22 -0
- data/lib/frigate/form/synchronizer/basic.rb +75 -0
- data/lib/frigate/form/synchronizer/contract.rb +46 -0
- data/lib/frigate/form/synchronizer/form.rb +40 -0
- data/lib/frigate/form/synchronizer/fundamental.rb +17 -0
- data/lib/frigate/form/synchronizer.rb +14 -0
- data/lib/frigate/form.rb +16 -0
- data/lib/frigate/function.rb +41 -0
- data/lib/frigate/operation/action/base.rb +65 -0
- data/lib/frigate/operation/action/create.rb +21 -0
- data/lib/frigate/operation/action/update.rb +24 -0
- data/lib/frigate/operation/action.rb +12 -0
- data/lib/frigate/operation/base.rb +79 -0
- data/lib/frigate/operation/controller.rb +42 -0
- data/lib/frigate/operation/invalid_params_error.rb +7 -0
- data/lib/frigate/operation/renderable.rb +52 -0
- data/lib/frigate/operation/worker.rb +50 -0
- data/lib/frigate/operation.rb +16 -0
- data/lib/frigate/version.rb +3 -0
- data/lib/frigate.rb +61 -0
- data/spec/fixtures/user_form.rb +34 -0
- data/spec/frigate/form/base_spec.rb +120 -0
- data/spec/frigate/operation/base_spec.rb +52 -0
- data/spec/rails_test_app/Rakefile +6 -0
- data/spec/rails_test_app/app/controllers/application_controller.rb +5 -0
- data/spec/rails_test_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_test_app/app/models/user/profile/passport.rb +5 -0
- data/spec/rails_test_app/app/models/user/profile.rb +6 -0
- data/spec/rails_test_app/app/models/user.rb +5 -0
- data/spec/rails_test_app/bin/bundle +3 -0
- data/spec/rails_test_app/bin/rails +4 -0
- data/spec/rails_test_app/bin/rake +4 -0
- data/spec/rails_test_app/config/application.rb +30 -0
- data/spec/rails_test_app/config/boot.rb +4 -0
- data/spec/rails_test_app/config/database.yml +25 -0
- data/spec/rails_test_app/config/environment.rb +5 -0
- data/spec/rails_test_app/config/environments/development.rb +28 -0
- data/spec/rails_test_app/config/environments/production.rb +67 -0
- data/spec/rails_test_app/config/environments/test.rb +39 -0
- data/spec/rails_test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_test_app/config/initializers/cookies_serializer.rb +3 -0
- data/spec/rails_test_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails_test_app/config/initializers/inflections.rb +16 -0
- data/spec/rails_test_app/config/initializers/mime_types.rb +4 -0
- data/spec/rails_test_app/config/initializers/session_store.rb +3 -0
- data/spec/rails_test_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_test_app/config/locales/en.yml +23 -0
- data/spec/rails_test_app/config/routes.rb +56 -0
- data/spec/rails_test_app/config/secrets.yml +22 -0
- data/spec/rails_test_app/config.ru +4 -0
- data/spec/rails_test_app/db/migrate/20141212054646_create_users.rb +10 -0
- data/spec/rails_test_app/db/migrate/20141212054746_create_user_profiles.rb +10 -0
- data/spec/rails_test_app/db/migrate/20141212054847_create_user_profile_passports.rb +15 -0
- data/spec/rails_test_app/db/seeds.rb +7 -0
- data/spec/spec_helper.rb +84 -0
- metadata +217 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c31f5d170fc9bb2a589fc06611dd6f216706562a
|
4
|
+
data.tar.gz: 2e1b8d78dbe0ef524c4a92390af756f23115f242
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0eb87b749cda6c1f3cf39d60b6066be1e3eca7d81a1c262b0a3f6ad79cecbb8ccde1bd001def5288dde2c978cb5dfea080d8773996e85ecf1cf1b5caae075fea
|
7
|
+
data.tar.gz: b0ddf19c44b1794f463217bef7747655502b218f5217d44fe3e3a49b79e9f7199a38da64da291e48768bac2146279db29576b05ec9372b4f763ba20e95005e7c
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %(app lib config test spec feature)
|
6
|
+
|
7
|
+
## Uncomment to clear the screen before every task
|
8
|
+
# clearing :on
|
9
|
+
|
10
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
11
|
+
# rspec may be run, below are examples of the most common uses.
|
12
|
+
# * bundler: 'bundle exec rspec'
|
13
|
+
# * bundler binstubs: 'bin/rspec'
|
14
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
15
|
+
# installed the spring binstubs per the docs)
|
16
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
17
|
+
# * 'just' rspec: 'rspec'
|
18
|
+
|
19
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
20
|
+
require "ostruct"
|
21
|
+
|
22
|
+
# Generic Ruby apps
|
23
|
+
rspec = OpenStruct.new
|
24
|
+
rspec.spec = ->(m) { "spec/#{m}_spec.rb" }
|
25
|
+
rspec.spec_dir = "spec"
|
26
|
+
rspec.spec_helper = "spec/spec_helper.rb"
|
27
|
+
|
28
|
+
watch(%r{^spec/.+_spec\.rb$})
|
29
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| rspec.spec.("lib/#{m[1]}") }
|
30
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
31
|
+
|
32
|
+
# # Rails example
|
33
|
+
# rails = OpenStruct.new
|
34
|
+
# rails.app = %r{^app/(.+)\.rb$}
|
35
|
+
# rails.views_n_layouts = %r{^app/(.*)(\.erb|\.haml|\.slim)$}
|
36
|
+
# rails.controllers = %r{^app/controllers/(.+)_controller\.rb$}
|
37
|
+
# rails.routes = "config/routes.rb"
|
38
|
+
# rails.app_controller = "app/controllers/application_controller.rb"
|
39
|
+
# rails.spec_helper = "spec/rails_helper.rb"
|
40
|
+
# rails.spec_support = %r{^spec/support/(.+)\.rb$}
|
41
|
+
# rails.views = %r{^app/views/(.+)/.*\.(erb|haml|slim)$}
|
42
|
+
#
|
43
|
+
# watch(rails.app) { |m| rspec.spec.(m[1]) }
|
44
|
+
# watch(rails.views_n_layouts) { |m| rspec.spec.("#{m[1]}#{m[2]}") }
|
45
|
+
# watch(rails.controllers) do |m|
|
46
|
+
# [
|
47
|
+
# rspec.spec.("routing/#{m[1]}_routing"),
|
48
|
+
# rspec.spec.("controllers/#{m[1]}_controller"),
|
49
|
+
# rspec.spec.("acceptance/#{m[1]}")
|
50
|
+
# ]
|
51
|
+
# end
|
52
|
+
|
53
|
+
# watch(rails.spec_support) { rspec.spec_dir }
|
54
|
+
# watch(rails.spec_helper) { rspec.spec_dir }
|
55
|
+
# watch(rails.routes) { "spec/routing" }
|
56
|
+
# watch(rails.app_controller) { "spec/controllers" }
|
57
|
+
|
58
|
+
# Capybara features specs
|
59
|
+
# watch(rails.views) { |m| rspec.spec.("features/#{m[1]}") }
|
60
|
+
|
61
|
+
# Turnip features and steps
|
62
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
63
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
64
|
+
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 BroderickBrockman
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Frigate
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'frigate'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install frigate
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/frigate/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
# require 'rake/testtask'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
task :default => :spec
|
6
|
+
RSpec::Core::RakeTask.new
|
7
|
+
|
8
|
+
# task :default => [:test]
|
9
|
+
# Rake::TestTask.new(:test) do |test|
|
10
|
+
# test.libs << 'test'
|
11
|
+
# test.test_files = FileList['test/*_test.rb']
|
12
|
+
# test.verbose = true
|
13
|
+
# end
|
data/frigate.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'frigate/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "frigate"
|
8
|
+
spec.version = Frigate::VERSION
|
9
|
+
spec.authors = ['Vladislav Pauk']
|
10
|
+
spec.email = ['vladislavpauk@gmail.com']
|
11
|
+
spec.summary = %q{ thinking about it }
|
12
|
+
spec.description = %q{still thinking about it}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
# spec.add_development_dependency 'rspec', '~> 3.1.0'
|
24
|
+
# spec.add_development_dependency 'guard-rspec', '~> 4.4.2'
|
25
|
+
# spec.add_development_dependency 'rails', '~> 4.1'
|
26
|
+
# spec.add_development_dependency 'sqlite3', '~> 1.3.10'
|
27
|
+
# spec.add_development_dependency 'database_cleaner', '~> 1.3.0'
|
28
|
+
spec.add_dependency 'activemodel', '~> 4.1'
|
29
|
+
spec.add_dependency 'activesupport', '~> 4.1'
|
30
|
+
spec.add_dependency 'hashie', '~> 3.3.2'
|
31
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Frigate
|
2
|
+
module Form
|
3
|
+
# Uses to define a form association with properties as Frigate::Form::Property
|
4
|
+
class Association
|
5
|
+
class << self
|
6
|
+
# defines property for association
|
7
|
+
# @param [Symbol] name
|
8
|
+
# @param [Hash] options
|
9
|
+
def property(name, options={})
|
10
|
+
@properties ||= {}
|
11
|
+
@properties[name.to_sym] = options
|
12
|
+
end
|
13
|
+
|
14
|
+
# defines association (kinda property with properties) for association
|
15
|
+
# @param [Symbol] name
|
16
|
+
# @param [Hash] options
|
17
|
+
# @param [Proc] block
|
18
|
+
def has_one(name, options={}, &block)
|
19
|
+
@associations ||= {}
|
20
|
+
@associations[name.to_sym] = options.merge({ block: block })
|
21
|
+
end
|
22
|
+
|
23
|
+
# gets just options for defined properties
|
24
|
+
# @example properties hash
|
25
|
+
# { skype: { validates: { presence: true }, another_option: another_option_hash } }
|
26
|
+
def properties
|
27
|
+
@properties.deep_dup || {}
|
28
|
+
end
|
29
|
+
|
30
|
+
# gets just options for defined associations
|
31
|
+
# @example associations hash
|
32
|
+
# { some_name: { block: <Proc.new instance> }
|
33
|
+
def associations
|
34
|
+
@associations.deep_dup || {}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :name, :opts, :properties, :associations, :root, :parent
|
39
|
+
# @param [Symbol] name
|
40
|
+
# @param [Hash] opts
|
41
|
+
def initialize(name, root, parent, opts = {})
|
42
|
+
@name, @root, @parent, @opts = name, root, parent, opts
|
43
|
+
@properties, @associations = [], []
|
44
|
+
|
45
|
+
exec_opts_block
|
46
|
+
process_properties
|
47
|
+
process_associations
|
48
|
+
end
|
49
|
+
|
50
|
+
# gets errors of association
|
51
|
+
# @return [ActiveModel::Errors]
|
52
|
+
def errors
|
53
|
+
@errors ||= ActiveModel::Errors.new(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
# checks validness of association properties
|
57
|
+
def valid?
|
58
|
+
errors.messages.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
# validates association properties
|
62
|
+
def validate
|
63
|
+
properties.each { |_prop| errors.add(_prop.name, _prop.errors[:value]) unless _prop.valid? }
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# exec_opts_block
|
69
|
+
def exec_opts_block
|
70
|
+
singleton_class.instance_exec &@opts.delete(:block)
|
71
|
+
end
|
72
|
+
|
73
|
+
# initializes declared associations
|
74
|
+
def process_associations
|
75
|
+
singleton_class.associations.each do |_assoc_name, _assoc_options|
|
76
|
+
_assoc = Association.new(_assoc_name, root, self, _assoc_options)
|
77
|
+
@associations << _assoc
|
78
|
+
define_singleton_method(_assoc.name) { _assoc }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# initializes declared properties
|
83
|
+
def process_properties
|
84
|
+
singleton_class.properties.each do |_prop_name, _prop_options|
|
85
|
+
_prop = Property.new(_prop_name, root, self, _prop_options)
|
86
|
+
@properties << _prop
|
87
|
+
define_singleton_method(_prop.name) { _prop }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# TODO add am::verifications
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'frigate/form/property'
|
2
|
+
require 'frigate/form/association'
|
3
|
+
require 'frigate/form/synchronizer'
|
4
|
+
module Frigate
|
5
|
+
module Form
|
6
|
+
#
|
7
|
+
# Is used for model validation outside of model
|
8
|
+
#
|
9
|
+
# @example A form for User class
|
10
|
+
# class UserForm < Frigate::Form::Base
|
11
|
+
# property :email, validates: { presence: true }
|
12
|
+
# has_one :user_profile do
|
13
|
+
# property :first_name, validates: { presence: true }
|
14
|
+
# property :last_name
|
15
|
+
# property :skype
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# class UserFormCustom < Frigate::Form::Base
|
20
|
+
# property :email, validates: { presence: true },
|
21
|
+
# validate: [
|
22
|
+
# Proc.new { error(:invalid) unless value =~ /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i },
|
23
|
+
# ]
|
24
|
+
# has_one :user_profile do
|
25
|
+
# property :first_name, validates: { presence: true }
|
26
|
+
# property :last_name
|
27
|
+
# property :skype
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# class UserFormTree < Frigate::Form::Base
|
32
|
+
# property :email
|
33
|
+
# has_one :user_profile do
|
34
|
+
# property :first_name
|
35
|
+
# property :last_name
|
36
|
+
# property :skype
|
37
|
+
# has_one :user_profile_passport do
|
38
|
+
# property :number
|
39
|
+
# property :country
|
40
|
+
# property :city
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# user = User.new
|
46
|
+
# user.build_user_profile
|
47
|
+
# user_form = UserForm.new(user)
|
48
|
+
# user_form.valid? # returns false
|
49
|
+
# user_form.validate({email: 'z1@zzz.zz', user_profile: {first_name:'Alpha', last_name:'Omega'}}) # returns true
|
50
|
+
# user_form.valid? # returns true
|
51
|
+
#
|
52
|
+
# @author Vladislav Pauk <vladislavpauk@gmail.com>
|
53
|
+
#
|
54
|
+
class Base
|
55
|
+
class << self
|
56
|
+
# gets just options for defined properties
|
57
|
+
# @example properties hash
|
58
|
+
# { skype: { validates: { presence: true }, another_option: another_option_hash } }
|
59
|
+
def properties
|
60
|
+
@properties.deep_dup || {}
|
61
|
+
end
|
62
|
+
|
63
|
+
# gets just options for defined associations
|
64
|
+
# @example associations hash
|
65
|
+
# { some_name: { block: <Proc.new instance> }
|
66
|
+
def associations
|
67
|
+
@associations.deep_dup || {}
|
68
|
+
end
|
69
|
+
|
70
|
+
# defines property (root) of form/base
|
71
|
+
# @param [Symbol] name
|
72
|
+
# @param [Hash] options
|
73
|
+
def property(name, options={})
|
74
|
+
@properties ||= {}
|
75
|
+
@properties[name.to_sym] = options
|
76
|
+
end
|
77
|
+
|
78
|
+
# defines association (kinda property with properties)(root as well) of form/base
|
79
|
+
# @param [Symbol] name
|
80
|
+
# @param [Hash] options
|
81
|
+
# @param [Proc] block
|
82
|
+
def has_one(name, options={}, &block)
|
83
|
+
@associations ||= {}
|
84
|
+
@associations[name.to_sym] = options.merge({ block: block })
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
attr_reader :model, :properties, :associations
|
89
|
+
delegate :sync_with_model, :sync_properties_with_model_or_params, :sync_errors, to: :@syncronizator
|
90
|
+
|
91
|
+
# Method of initialization of Frigate::Form class
|
92
|
+
# @param [Object] model
|
93
|
+
def initialize(model, opts={})
|
94
|
+
@syncronizator = opts[:contract] ? Synchronizer::Contract.new(self) : Synchronizer::Form.new(self)
|
95
|
+
@model, @properties, @associations = model, [], []
|
96
|
+
|
97
|
+
process_properties
|
98
|
+
process_associations
|
99
|
+
end
|
100
|
+
|
101
|
+
# Initializes errors instance variable of ActiveModel::Errors type
|
102
|
+
# @return [ActiveModel::Errors] return errors
|
103
|
+
def errors
|
104
|
+
@errors ||= ActiveModel::Errors.new(self)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Validates form with params or model properties
|
108
|
+
def validate(*args)
|
109
|
+
sync_properties_with_model_or_params(*args)
|
110
|
+
sync_errors
|
111
|
+
valid?
|
112
|
+
end
|
113
|
+
|
114
|
+
# returns true if are there any errors messages in form errors
|
115
|
+
# @return [Boolean] return true if form model is valid
|
116
|
+
def valid?
|
117
|
+
errors.messages.empty?
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
# initializes declared associations
|
123
|
+
def process_associations
|
124
|
+
self.class.associations.each do |_assoc_name, _assoc_options|
|
125
|
+
_assoc = Association.new(_assoc_name, self, self, _assoc_options)
|
126
|
+
@associations << _assoc
|
127
|
+
define_singleton_method(_assoc.name) { _assoc }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# initializes declared properties
|
132
|
+
def process_properties
|
133
|
+
self.class.properties.each do |_prop_name, _prop_options|
|
134
|
+
_prop = Property.new(_prop_name, self, self, _prop_options)
|
135
|
+
@properties << _prop
|
136
|
+
define_singleton_method(_prop.name) { _prop }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Frigate
|
2
|
+
module Form
|
3
|
+
# Uses to define a form property
|
4
|
+
class Property
|
5
|
+
attr_accessor :name, :options, :value, :root, :parent
|
6
|
+
|
7
|
+
include ActiveModel::Validations
|
8
|
+
|
9
|
+
# @param [Symbol] name
|
10
|
+
# @param [Hash] options
|
11
|
+
def initialize(name, root, parent, options)
|
12
|
+
@name, @root, @parent, @options = name, root, parent, options
|
13
|
+
|
14
|
+
set_validations
|
15
|
+
set_custom_validations
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def set_validations
|
21
|
+
validations = options.delete(:validates)
|
22
|
+
singleton_class.class_eval { validates :value, validations } if validations
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_custom_validations
|
27
|
+
validations = options.delete(:validate)
|
28
|
+
validations.is_a?(Array) and validations.each do |validation|
|
29
|
+
singleton_class.class_eval { validate &validation }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_error(name)
|
34
|
+
errors.add(:value, name)
|
35
|
+
end
|
36
|
+
|
37
|
+
alias_method :error, :add_error
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Frigate
|
2
|
+
module Form
|
3
|
+
# Serves for synchronization form properties with model attributes/properties
|
4
|
+
module Synchronizer
|
5
|
+
# Base class for Frigate::Form::Base synchronizations
|
6
|
+
# It justs abstract class which has two methods
|
7
|
+
# * sync form with model
|
8
|
+
# * sync properties errors into form errors
|
9
|
+
class Base < Fundamental
|
10
|
+
# synchronizes form with model
|
11
|
+
def sync_with_model
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
# synchronizes form properties (associations:properties) errors with general form errors
|
16
|
+
def sync_errors
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Frigate
|
2
|
+
module Form
|
3
|
+
# Serves for synchronization form properties with model attributes/properties
|
4
|
+
module Synchronizer
|
5
|
+
# A syncronizator between form properties and model properties
|
6
|
+
class Basic < Base
|
7
|
+
# A class for synchronization model with params
|
8
|
+
class ModelSynchronizer < Fundamental
|
9
|
+
# Synchronizes given model with form properties and associations:properties
|
10
|
+
# @param [Hash] ops
|
11
|
+
def sync_with_model(opts={})
|
12
|
+
sync_properties
|
13
|
+
sync_associations
|
14
|
+
save_model if opts[:save]
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def save_model
|
20
|
+
form.model.save!
|
21
|
+
end
|
22
|
+
|
23
|
+
def sync_associations
|
24
|
+
_sync_associations(form, form.model)
|
25
|
+
end
|
26
|
+
|
27
|
+
def sync_properties
|
28
|
+
_sync_properties(form, form.model)
|
29
|
+
end
|
30
|
+
|
31
|
+
def _sync_associations(f_ctx, m_ctx)
|
32
|
+
f_ctx.associations.each do |_assoc|
|
33
|
+
n_m_ctx = m_ctx.send(_assoc.name.to_sym)
|
34
|
+
if m_ctx.is_a?(ActiveRecord::Base) and n_m_ctx.nil?
|
35
|
+
m_ctx.send("build_#{_assoc.name}")
|
36
|
+
end
|
37
|
+
_assoc.properties.each do |_prop|
|
38
|
+
_sync_properties(_assoc, n_m_ctx)
|
39
|
+
end
|
40
|
+
_sync_associations(_assoc, n_m_ctx)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def _sync_properties(f_ctx, m_ctx)
|
45
|
+
f_ctx.properties.each do |_prop|
|
46
|
+
m_ctx.send("#{_prop.name}=", _prop.value) unless _prop.value.nil?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Synchronizes given params with given model
|
52
|
+
# @param [Hash] opts
|
53
|
+
# @return [Boolean] true
|
54
|
+
def sync_with_model(opts={})
|
55
|
+
ModelSynchronizer.new(form).sync_with_model(opts)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Synchronizes properties errors with form errors
|
59
|
+
# @return [Boolean]
|
60
|
+
def sync_errors
|
61
|
+
form.properties.each { |_prop| form.errors.add(_prop.name, _prop.errors[:value]) unless _prop.valid? }
|
62
|
+
form.associations.each do |_assoc|
|
63
|
+
_assoc.properties.each do |_prop|
|
64
|
+
form.errors.add("#{_assoc.name}.#{_prop.name}", _prop.errors[:value]) unless _prop.valid?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def sync_properties_with_model_or_params
|
70
|
+
raise NotImplementedError
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Frigate
|
2
|
+
module Form
|
3
|
+
# Serves for synchronization form properties with model attributes/properties
|
4
|
+
module Synchronizer
|
5
|
+
# Synchronizes form properties with model properties
|
6
|
+
class Contract < Basic # TODO: tree-association support, as form does
|
7
|
+
# Synchronizes given model's attributes with form properties and associations:properties
|
8
|
+
def sync_properties_with_model_or_params
|
9
|
+
#
|
10
|
+
# sync model properties with form properties
|
11
|
+
#
|
12
|
+
form.properties.each do |_prop|
|
13
|
+
if form.model.respond_to?(_prop.name.to_sym)
|
14
|
+
_prop.value = form.model.send(_prop.name.to_sym)
|
15
|
+
else
|
16
|
+
raise StandardError.new('form model does not have %s property' % [_prop.name])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
#
|
20
|
+
# sync model associations' properties with form associations' properties
|
21
|
+
#
|
22
|
+
form.associations.each do |_a|
|
23
|
+
if form.model.respond_to?(_a.name.to_sym) and !form.model.send(_a.name.to_sym).nil?
|
24
|
+
_a.properties.each do |_p|
|
25
|
+
if form.model.send(_a.name.to_sym).respond_to?(_p.name.to_sym)
|
26
|
+
_p.value = form.model.send(_a.name.to_sym).send(_p.name.to_sym)
|
27
|
+
else
|
28
|
+
_err_msg = 'form model %s association does not have %s property' % [_a.name, _p.name]
|
29
|
+
raise StandardError.new(_err_msg)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
else
|
33
|
+
if form.model.respond_to?(_a.name.to_sym)
|
34
|
+
_err_msg = 'form model %s association is nil' % [_a.name]
|
35
|
+
raise StandardError.new(_err_msg)
|
36
|
+
else
|
37
|
+
_err_msg = 'form model does not have %s association' % [_a.name]
|
38
|
+
raise StandardError.new(_err_msg)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Frigate
|
2
|
+
module Form
|
3
|
+
# Serves for synchronization form properties with model attributes/properties
|
4
|
+
module Synchronizer
|
5
|
+
# Synchronizes form properties with given params
|
6
|
+
class Form < Basic
|
7
|
+
# It's just extracted method *sync_properties_with_model_or_params* into this class
|
8
|
+
class ParamsToPropertiesSynchronizer < Fundamental
|
9
|
+
# Synchronizes given params with form properties and associations:properties
|
10
|
+
# @param [Hash] params
|
11
|
+
def sync_params_with_properties(params)
|
12
|
+
process_params(params, form)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def process_params(params, ctx)
|
18
|
+
params.each do |_p_n, _p_v|
|
19
|
+
if _p_v.is_a?(Hash)
|
20
|
+
process_params(_p_v, ctx.send(_p_n.to_sym))
|
21
|
+
else
|
22
|
+
sync_properties(_p_n, _p_v, ctx)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def sync_properties(p_n, p_v, ctx)
|
28
|
+
ctx.properties.each { |_p| _p.value = p_v if p_n.to_sym == _p.name.to_sym }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Synchronizes given params with form properties and associations:properties
|
33
|
+
# @param [Hash] params
|
34
|
+
def sync_properties_with_model_or_params(params)
|
35
|
+
ParamsToPropertiesSynchronizer.new(form).sync_params_with_properties(params)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|