frigate 0.0.1
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 +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
|