form_obj 0.3.0 → 0.4.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,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "~> 4.0.0"
6
+ gem "activemodel", "~> 4.0.0"
7
+ gem "actionpack", "~> 4.0.0"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "~> 4.1.0"
6
+ gem "activemodel", "~> 4.1.0"
7
+ gem "actionpack", "~> 4.1.0"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "~> 4.2.0"
6
+ gem "activemodel", "~> 4.2.0"
7
+ gem "actionpack", "~> 4.2.0"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "~> 5.0.0"
6
+ gem "activemodel", "~> 5.0.0"
7
+ gem "actionpack", "~> 5.0.0"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "~> 5.1.0"
6
+ gem "activemodel", "~> 5.1.0"
7
+ gem "actionpack", "~> 5.1.0"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "~> 5.2.0"
6
+ gem "activemodel", "~> 5.2.0"
7
+ gem "actionpack", "~> 5.2.0"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ rubies=()
6
+ versions=false
7
+ while read -r line; do
8
+ if [[ $line == "rvm:" ]]
9
+ then
10
+ versions=true
11
+ elif $versions && [[ $line == -* ]]
12
+ then
13
+ rubies+=(${line:2})
14
+ elif [[ ${line:0:1} != "#" ]]
15
+ then
16
+ versions=false
17
+ fi
18
+ done < ".travis.yml"
19
+
20
+ for i in "${rubies[@]}"
21
+ do
22
+ echo "====================================================="
23
+ echo "Install Ruby $i"
24
+ echo "====================================================="
25
+ rvm install $i
26
+ done
27
+
28
+ rvm all do gem install bundler
@@ -0,0 +1,36 @@
1
+ module FormObj
2
+ class Form < FormObj::Struct
3
+ class Array < FormObj::Struct::Array
4
+ def update_attributes(vals)
5
+ ids_exists = []
6
+ items_to_add = []
7
+
8
+ vals.each do |val|
9
+ id = primary_key(val)
10
+ item = self.find { |i| i.primary_key == id }
11
+ if item
12
+ item.update_attributes(val)
13
+ ids_exists << id
14
+ else
15
+ items_to_add << val
16
+ end
17
+ end
18
+
19
+ ids_to_remove = self.map(&:primary_key) - ids_exists
20
+ self.delete_if { |item| ids_to_remove.include? item.primary_key }
21
+
22
+ items_to_add.each do |item|
23
+ self.create.update_attributes(item)
24
+ end
25
+
26
+ sort! { |a, b| vals.index { |val| primary_key(val) == a.primary_key } <=> vals.index { |val| primary_key(val) == b.primary_key } }
27
+ end
28
+
29
+ private
30
+
31
+ def primary_key(hash)
32
+ hash.key?(item_class.primary_key.to_sym) ? hash[item_class.primary_key.to_sym] : hash[item_class.primary_key.to_s]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ module FormObj
2
+ class Form < FormObj::Struct
3
+ class Attribute < FormObj::Struct::Attribute
4
+ def initialize(name, array: false, class: nil, default: nil, parent:, primary_key: nil, &block)
5
+ super(name, array: array, class: binding.local_variable_get(:class), default: default, parent: parent, &block)
6
+
7
+ @nested_class.instance_variable_set(:@model_name, ActiveModel::Name.new(@nested_class, nil, name.to_s)) if !@nested_class && block_given?
8
+
9
+ if primary_key
10
+ if @nested_class
11
+ @nested_class.primary_key = primary_key
12
+ else
13
+ parent.primary_key = name.to_sym
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module FormObj
2
+ class Form < FormObj::Struct
3
+ class Attributes < FormObj::Struct::Attributes
4
+ def find(name)
5
+ @items.find { |item| item.name == name.to_sym }
6
+ end
7
+
8
+ def each(&block)
9
+ @items.each(&block)
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/form_obj/form.rb CHANGED
@@ -1,50 +1,44 @@
1
- require 'tree_struct'
2
- require 'form_obj/attributes'
3
- require 'form_obj/attribute'
4
- require 'form_obj/array'
1
+ require 'form_obj/struct'
2
+ require 'form_obj/form/attributes'
3
+ require 'form_obj/form/attribute'
4
+ require 'form_obj/form/array'
5
5
  require 'active_model'
6
6
 
7
7
  module FormObj
8
8
  class UnknownAttributeError < RuntimeError; end
9
9
 
10
- class Form < ::TreeStruct
10
+ class Form < FormObj::Struct
11
11
  extend ::ActiveModel::Naming
12
12
  extend ::ActiveModel::Translation
13
13
 
14
14
  include ::ActiveModel::Conversion
15
15
  include ::ActiveModel::Validations
16
16
 
17
- private
17
+ class_attribute :primary_key, instance_predicate: false, instance_reader: false, instance_writer: false
18
+ self.primary_key = :id
18
19
  self._attributes = Attributes.new
19
20
 
20
- public
21
+ class << self
22
+ def array_class
23
+ Array
24
+ end
21
25
 
22
- def self.array_class
23
- Array
24
- end
26
+ def nested_class
27
+ ::FormObj::Form
28
+ end
25
29
 
26
- def self.nested_class
27
- ::TreeStruct
28
- end
30
+ def attribute_class
31
+ Attribute
32
+ end
29
33
 
30
- def self.attribute_class
31
- Attribute
34
+ def model_name
35
+ @model_name || super
36
+ end
32
37
  end
33
38
 
34
39
  attr_accessor :persisted
35
40
  attr_reader :errors
36
41
 
37
- class_attribute :primary_key, instance_predicate: false, instance_reader: false, instance_writer: false
38
- self.primary_key = :id
39
-
40
- def self.nested_class
41
- ::FormObj::Form
42
- end
43
-
44
- def self.model_name
45
- @model_name || super
46
- end
47
-
48
42
  def initialize()
49
43
  @errors = ActiveModel::Errors.new(self)
50
44
  @persisted = false
@@ -1,6 +1,6 @@
1
1
  module FormObj
2
- module Mappable
3
- class Array < FormObj::Array
2
+ module ModelMapper
3
+ class Array < FormObj::Form::Array
4
4
  def initialize(item_class, model_attribute:)
5
5
  @model_attribute = model_attribute
6
6
  super(item_class)
@@ -14,14 +14,14 @@ module FormObj
14
14
  self
15
15
  end
16
16
 
17
- def save_to_models(models)
17
+ def sync_to_models(models)
18
18
  model_array = models[:default]
19
19
  ids_exists = []
20
20
  items_to_add = []
21
21
 
22
22
  self.each do |item|
23
23
  if model = find_model(model_array, id = item.primary_key)
24
- item.save_to_models(models.merge(default: model))
24
+ item.sync_to_models(models.merge(default: model))
25
25
  ids_exists << id
26
26
  else
27
27
  items_to_add << item
@@ -33,7 +33,7 @@ module FormObj
33
33
 
34
34
  items_to_add.each do |item|
35
35
  model_array << model = @model_attribute.create_model # || model_array.create_model
36
- item.save_to_models(models.merge(default: model))
36
+ item.sync_to_models(models.merge(default: model))
37
37
  end
38
38
  end
39
39
 
@@ -1,24 +1,23 @@
1
- require 'form_obj/mappable/model_attribute'
1
+ require 'form_obj/model_mapper/model_attribute'
2
2
 
3
3
  module FormObj
4
- module Mappable
5
- class Attribute < FormObj::Attribute
4
+ module ModelMapper
5
+ class Attribute < FormObj::Form::Attribute
6
6
  attr_reader :model_attribute
7
7
 
8
- def initialize(name, array: false, class: nil, default: nil, hash: false, model: :default, model_attribute: nil, model_class: nil, parent:, primary_key: nil, &block)
9
- @hash = hash
10
- @model_attribute = ModelAttribute.new(model: model, names: model_attribute, classes: model_class, default_name: name, array: array, hash: hash, subform: binding.local_variable_get(:class) || block_given?)
8
+ def initialize(name, array: false, class: nil, default: nil, model_hash: false, model: :default, model_attribute: nil, model_class: nil, parent:, primary_key: nil, &block)
9
+ @model_attribute = ModelAttribute.new(model: model, names: model_attribute, classes: model_class, default_name: name, array: array, hash: model_hash, subform: binding.local_variable_get(:class) || block_given?)
11
10
 
12
11
  if block_given?
13
12
  new_block = Proc.new do
14
- include FormObj::Mappable
13
+ include FormObj::ModelMapper
15
14
  class_eval &block
16
15
  end
17
16
  end
18
17
  super(name, array: array, class: binding.local_variable_get(:class), default: default, parent: parent, primary_key: primary_key, &new_block)
19
18
 
20
- @nested_class = Class.new(@nested_class) if binding.local_variable_get(:class)
21
- @nested_class.hash = hash if @nested_class
19
+ @nested_class = Class.new(@nested_class) if binding.local_variable_get(:class)
20
+ @nested_class.model_hash = model_hash if @nested_class
22
21
  end
23
22
 
24
23
  def array?
@@ -1,7 +1,7 @@
1
1
  require 'active_support/inflector'
2
2
 
3
3
  module FormObj
4
- module Mappable
4
+ module ModelMapper
5
5
  class ModelAttribute
6
6
  private
7
7
 
@@ -1,8 +1,8 @@
1
1
  require 'active_support/inflector'
2
- require 'form_obj/mappable/model_attribute/item'
2
+ require 'form_obj/model_mapper/model_attribute/item'
3
3
 
4
4
  module FormObj
5
- module Mappable
5
+ module ModelMapper
6
6
  class ModelAttribute
7
7
  attr_reader :model
8
8
 
@@ -1,5 +1,5 @@
1
1
  module FormObj
2
- module Mappable
2
+ module ModelMapper
3
3
  class ModelPrimaryKey
4
4
  def initialize(model_attribute)
5
5
  @model_attribute = model_attribute
@@ -1,9 +1,10 @@
1
- require 'form_obj/mappable/attribute'
2
- require 'form_obj/mappable/array'
3
- require 'form_obj/mappable/model_primary_key'
1
+ require 'form_obj/form'
2
+ require 'form_obj/model_mapper/attribute'
3
+ require 'form_obj/model_mapper/array'
4
+ require 'form_obj/model_mapper/model_primary_key'
4
5
 
5
6
  module FormObj
6
- module Mappable
7
+ module ModelMapper
7
8
  class PrimaryKeyMappingError < RuntimeError; end
8
9
 
9
10
  def self.included(base)
@@ -12,19 +13,19 @@ module FormObj
12
13
 
13
14
  module ClassMethods
14
15
  def attribute_class
15
- Mappable::Attribute
16
+ ModelMapper::Attribute
16
17
  end
17
18
 
18
19
  def array_class
19
- Mappable::Array
20
+ ModelMapper::Array
20
21
  end
21
22
 
22
- def hash=(value)
23
+ def model_hash=(value)
23
24
  _attributes.each { |attribute| attribute.model_attribute.hash_item = value }
24
25
  end
25
26
 
26
27
  def model_primary_key
27
- Mappable::ModelPrimaryKey.new(self._attributes.find(self.primary_key).model_attribute)
28
+ ModelMapper::ModelPrimaryKey.new(self._attributes.find(self.primary_key).model_attribute)
28
29
  end
29
30
  end
30
31
 
@@ -38,12 +39,12 @@ module FormObj
38
39
  self
39
40
  end
40
41
 
41
- def save_to_model(model)
42
- save_to_models(default: model)
42
+ def sync_to_model(model)
43
+ sync_to_models(default: model)
43
44
  end
44
45
 
45
- def save_to_models(models)
46
- self.class._attributes.each { |attribute | save_attribute_to_model(attribute, models) }
46
+ def sync_to_models(models)
47
+ self.class._attributes.each { |attribute | sync_attribute_to_model(attribute, models) }
47
48
  self.persisted = true
48
49
  self
49
50
  end
@@ -109,12 +110,12 @@ module FormObj
109
110
  end
110
111
  end
111
112
 
112
- def save_attribute_to_model(attribute, models)
113
+ def sync_attribute_to_model(attribute, models)
113
114
  if attribute.subform?
114
115
  if attribute.model_attribute.write_to_model?
115
- self.send(attribute.name).save_to_models(models.merge(default: attribute.model_attribute.read_from_models(models, create_nested_model_if_nil: true)))
116
+ self.send(attribute.name).sync_to_models(models.merge(default: attribute.model_attribute.read_from_models(models, create_nested_model_if_nil: true)))
116
117
  else
117
- self.send(attribute.name).save_to_models(models.merge(default: models[attribute.model_attribute.model]))
118
+ self.send(attribute.name).sync_to_models(models.merge(default: models[attribute.model_attribute.model]))
118
119
  end
119
120
  elsif attribute.model_attribute.write_to_model?
120
121
  attribute.model_attribute.write_to_models(models, self.send(attribute.name))
@@ -0,0 +1,22 @@
1
+ require 'typed_array'
2
+
3
+ module FormObj
4
+ class Struct
5
+ class Array < TypedArray
6
+ def create
7
+ self << (item = create_item)
8
+ item
9
+ end
10
+
11
+ def to_hash
12
+ self.map(&:to_hash)
13
+ end
14
+
15
+ private
16
+
17
+ def create_item
18
+ item_class.new
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,69 @@
1
+ module FormObj
2
+ class Struct
3
+ class Attribute
4
+ attr_reader :name
5
+
6
+ def initialize(name, array: false, class: nil, default: nil, parent:, &block)
7
+ @name = name.to_sym
8
+ @array = array
9
+ @default_value = default
10
+ @parent = parent
11
+
12
+ @nested_class = binding.local_variable_get(:class)
13
+ @nested_class = Class.new(@parent.nested_class, &block) if !@nested_class && block_given?
14
+
15
+ raise ArgumentError.new('Nested structure has to be defined (either with :class parameter or with block) for arrays if :default parameter is not specified') if @array && @nested_class.nil? && @default_value.nil?
16
+ end
17
+
18
+ def subform?
19
+ !@nested_class.nil?
20
+ end
21
+
22
+ def validate_value!(value)
23
+ if @nested_class
24
+ if @array
25
+ unless value.class == @parent.array_class
26
+ raise ArgumentError.new(":#{@name} attribute value should be of class #{@parent.nested_class.name}::Array while attempt to assign value of class #{value.class.name}")
27
+ end
28
+ unless value.item_class == @nested_class
29
+ raise ArgumentError.new(":#{@name} attribute value should be an array with items of class #{@nested_class.name} while attempt to assign an array with items of class #{value.item_class.name}")
30
+ end
31
+
32
+ else
33
+ unless value.class == @nested_class
34
+ raise ArgumentError.new(":#{@name} attribute value should be of class #{@nested_class.name} while attempt to assign value of class #{value.class.name}")
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def default_value
41
+ if @default_value.nil?
42
+ if @nested_class
43
+ if @array
44
+ create_array
45
+ else
46
+ create_nested
47
+ end
48
+ end
49
+
50
+ elsif @default_value.is_a? Proc
51
+ @default_value.call(@parent, self)
52
+
53
+ else
54
+ @default_value
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def create_nested
61
+ @nested_class.new
62
+ end
63
+
64
+ def create_array
65
+ @parent.array_class.new(@nested_class)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,21 @@
1
+ module FormObj
2
+ class Struct
3
+ class Attributes
4
+ def initialize(items = [])
5
+ @items = items
6
+ end
7
+
8
+ def add(attribute)
9
+ if @items.map(&:name).include? attribute.name
10
+ self.class.new(@items.map { |item| item.name == attribute.name ? attribute : item })
11
+ else
12
+ self.class.new(@items + [attribute])
13
+ end
14
+ end
15
+
16
+ def map(*args, &block)
17
+ @items.map(*args, &block)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,76 @@
1
+ require "form_obj/struct/array"
2
+ require "form_obj/struct/attribute"
3
+ require "form_obj/struct/attributes"
4
+ require "active_support/core_ext/class/attribute"
5
+
6
+ module FormObj
7
+ class Struct
8
+ class_attribute :_attributes, instance_predicate: false, instance_reader: false, instance_writer: false
9
+ self._attributes = Attributes.new
10
+
11
+ class << self
12
+ def array_class
13
+ Array
14
+ end
15
+
16
+ def nested_class
17
+ ::FormObj::Struct
18
+ end
19
+
20
+ def attribute_class
21
+ Attribute
22
+ end
23
+
24
+ def attribute(name, opts = {}, &block)
25
+ attr = attribute_class.new(name, opts.merge(parent: self), &block)
26
+ self._attributes = _attributes.add(attr)
27
+ _define_attribute_getter(attr)
28
+ _define_attribute_setter(attr)
29
+ self
30
+ end
31
+
32
+ def attributes
33
+ self._attributes.map(&:name)
34
+ end
35
+
36
+ def inspect
37
+ "#{name}#{attributes.size > 0 ? "(#{attributes.join(', ')})" : ''}"
38
+ end
39
+
40
+ private
41
+
42
+ def _define_attribute_getter(attribute)
43
+ define_method attribute.name do
44
+ _get_attribute_value(attribute)
45
+ end
46
+ end
47
+
48
+ def _define_attribute_setter(attribute)
49
+ define_method "#{attribute.name}=" do |value|
50
+ _set_attribute_value(attribute, value)
51
+ end
52
+ end
53
+ end
54
+
55
+ def to_hash
56
+ Hash[self.class._attributes.map { |attribute| [attribute.name, attribute.subform? ? send(attribute.name).to_hash : send(attribute.name)] }]
57
+ end
58
+
59
+ def inspect
60
+ "#<#{self.class.name} #{self.class._attributes.map { |attribute| "#{attribute.name}: #{send(attribute.name).inspect}"}.join(', ')}>"
61
+ end
62
+
63
+ private
64
+
65
+ def _get_attribute_value(attribute)
66
+ value = instance_variable_get("@#{attribute.name}")
67
+ value = instance_variable_set("@#{attribute.name}", attribute.default_value) if value.nil?
68
+ value
69
+ end
70
+
71
+ def _set_attribute_value(attribute, value)
72
+ attribute.validate_value!(value)
73
+ instance_variable_set("@#{attribute.name}", value)
74
+ end
75
+ end
76
+ end
@@ -1,3 +1,3 @@
1
1
  module FormObj
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/form_obj.rb CHANGED
@@ -2,5 +2,6 @@ module FormObj
2
2
  end
3
3
 
4
4
  require "form_obj/version"
5
- require 'form_obj/form'
6
- require 'form_obj/mappable'
5
+ require "form_obj/struct"
6
+ require "form_obj/form"
7
+ require "form_obj/model_mapper"
data/run_spec.sh ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ rubies=()
6
+ versions=false
7
+ while read -r line; do
8
+ if [[ $line == "rvm:" ]]
9
+ then
10
+ versions=true
11
+ elif $versions && [[ $line == -* ]]
12
+ then
13
+ rubies+=(${line:2})
14
+ elif [[ ${line:0:1} != "#" ]]
15
+ then
16
+ versions=false
17
+ fi
18
+ done < ".travis.yml"
19
+
20
+ for i in "${rubies[@]}"
21
+ do
22
+ echo "====================================================="
23
+ echo "$i: Start Test"
24
+ echo "====================================================="
25
+ rvm $i exec bundle exec appraisal rake spec
26
+ echo "====================================================="
27
+ echo "$i: End Test"
28
+ echo "====================================================="
29
+ done