model_pack 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fedf28243c4c586d1ef06698fc4a7e41615eb82a
4
+ data.tar.gz: 1d895d036f7f44226f8ffbd2a0f9f98696ae4862
5
+ SHA512:
6
+ metadata.gz: 0a9317d03361f697ff4f0a7bae3a6656f5d7573a7adbe7093a4713c1c04d5f72c20b8db5bf24a0d5cb6af43731b11109f2bdb5b87bac1b32a7337b3b3889a7e2
7
+ data.tar.gz: a9b1963cf12b3f612dc0098ce5c90889bbde5b4495755b8c9c6fcb3ff68578d87f768582cae6e178a143b584339d5489b359c36139dd296c7f21e0f5af1c6024
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in model_pack.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 che
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,47 @@
1
+ # Model Pack - model intefaces
2
+
3
+ Model Pack - набор интерфейсов, который позволяет создавать в объекте специальные аттрибуты.
4
+ Также добавляет методы работы model attributes. Прозволяет сериализировать иерахнию модельных
5
+ аттрибутов.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'model_pack'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install model_pack
22
+
23
+ ## Usage
24
+
25
+ ```ruby
26
+ class Point
27
+ include ModelPack::Document
28
+
29
+ attribute :x
30
+ attribute :y
31
+ end
32
+
33
+ class Line
34
+ include ModelPack::Document
35
+
36
+ object :begin, class_name: Point
37
+ object :end, class_name: Point
38
+ end
39
+ ```
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it ( https://github.com/chelovekov/model_pack/fork )
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,37 @@
1
+ module ModelPack
2
+ module AttributeMethods
3
+ extend Concern
4
+
5
+ class_methods do
6
+ def register_attribute(name)
7
+ raise ArgumentError, "dublicate attribute with name `#{name}`" if attribute_names.include?(name)
8
+ attribute_names.push name
9
+ nil
10
+ end
11
+
12
+ def attribute_names
13
+ class_variable_defined?(:@@attribute_names) ? class_variable_get(:@@attribute_names) : class_variable_set(:@@attribute_names, Array.new)
14
+ end
15
+ end
16
+
17
+ def attributes
18
+ self.class.attribute_names.inject({}) { |h, name| h[name] = send(name); h }
19
+ end
20
+
21
+ def update_attributes(*attributes)
22
+ attributes.each do |name, attribute|
23
+ key = "#{name}="
24
+ send(key, attribute) if respond_to?(key)
25
+ end
26
+ end
27
+
28
+ def update_attributes!(*attributes)
29
+ # check present first
30
+ attributes.each do |name, attribute|
31
+ raise ArgumentError, "undefined attribute `#{name}`" unless attribute_names.include?(method)
32
+ key = "#{name}="
33
+ send(key, attribute) if respond_to?(key)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ module ModelPack
2
+ module ClassMethods
3
+
4
+ def attribute(name, writer: lambda { |v| v }, default: nil, as: nil, serialize: nil)
5
+ attribute_reader(name, default: default, as: as, serialize: serialize)
6
+ attribute_writer(name, writer: writer)
7
+ register_attribute(name)
8
+ end
9
+
10
+ def attribute_writer(name, writer: lambda { |v| v })
11
+ define_method "#{name}=" do |value|
12
+ instance_variable_set("@#{name}", instance_exec(value, &writer))
13
+ end
14
+ end
15
+
16
+ def attribute_reader(name, default: nil, as: nil, serialize: nil)
17
+ default_dup = default.dup rescue default
18
+ default_value = default_dup || (as && as.new)
19
+
20
+ define_method name do
21
+ instance_variable_defined?("@#{name}") ? instance_variable_get("@#{name}") : instance_variable_set("@#{name}", default_value)
22
+ end
23
+
24
+ define_method "#{name}_hash" do
25
+ instance_exec(send(name), &serialize)
26
+ end if serialize
27
+ end
28
+
29
+ def object(name, class_name: nil, default: nil, serialize: nil)
30
+ attribute(name,
31
+ default: default,
32
+ serialize: serialize,
33
+ writer: lambda { |v| v.is_a?(Hash) && class_name ? class_name.new(v) : v })
34
+ end
35
+
36
+ def array(name, default: nil, serialize: nil, writer: nil, class_name: nil)
37
+ attribute(name,
38
+ default: default,
39
+ serialize: serialize,
40
+ as: Array,
41
+ writer: writer || lambda { |array| array.collect { |v| v.is_a?(Hash) && class_name ? class_name.new(v) : v } })
42
+ end
43
+
44
+ def hashable(name, default: nil, serialize: nil)
45
+ attribute(name,
46
+ default: default,
47
+ serialize: serialize,
48
+ as: Hash)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,46 @@
1
+ module ModelPack
2
+ # Copy&paste from rails/activesupport/lib/active_support/concern.rb
3
+
4
+ module Concern
5
+ class MultipleIncludedBlocks < StandardError #:nodoc:
6
+ def initialize
7
+ super "Cannot define multiple 'included' blocks for a Concern"
8
+ end
9
+ end
10
+
11
+ def self.extended(base) #:nodoc:
12
+ base.instance_variable_set(:@_dependencies, [])
13
+ end
14
+
15
+ def append_features(base)
16
+ if base.instance_variable_defined?(:@_dependencies)
17
+ base.instance_variable_get(:@_dependencies) << self
18
+ return false
19
+ else
20
+ return false if base < self
21
+ @_dependencies.each { |dep| base.send(:include, dep) }
22
+ super
23
+ base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
24
+ base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
25
+ end
26
+ end
27
+
28
+ def included(base = nil, &block)
29
+ if base.nil?
30
+ raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
31
+
32
+ @_included_block = block
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def class_methods(&class_methods_module_definition)
39
+ mod = const_defined?(:ClassMethods) ?
40
+ const_get(:ClassMethods) :
41
+ const_set(:ClassMethods, Module.new)
42
+
43
+ mod.module_eval(&class_methods_module_definition)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,9 @@
1
+ module ModelPack
2
+ module Constructor
3
+ def initialize(*a)
4
+ attributes = a.pop
5
+ update_attributes(*attributes) if attributes
6
+ super(*a)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module ModelPack
2
+ module Document
3
+ extend Concern
4
+
5
+ included do
6
+ extend ClassMethods
7
+ include AttributeMethods
8
+ prepend Constructor
9
+ include Serialization
10
+ include Serializers::JSON
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,67 @@
1
+ module ModelPack
2
+ module Serialization
3
+
4
+ def serializable_attribute_names(attributes, options)
5
+ names = attributes.keys.sort
6
+ case options
7
+ when Hash
8
+ names &= Array.wrap(options.keys).map(&:to_s)
9
+ when true
10
+ # do nothing
11
+ when false
12
+ names = {}
13
+ when nil
14
+ names = {}
15
+ else
16
+ names &= Array.wrap(options).map(&:to_s)
17
+ end
18
+
19
+ names
20
+ end
21
+
22
+ def serializable_hash(options = nil, global = nil)
23
+ serializable_value =
24
+ ->(value, *a) { value.respond_to?(:serializable_hash) ? value.serializable_hash(*a[0...value.method(:serializable_hash).arity]) : value }
25
+ enumerable_value = ->(value, *a) {
26
+ value.is_a?(Hash) ? value.inject({}) { |h, e| h[e.first] = serializable_value[e.last, *a]; h } :
27
+ (value.is_a?(Enumerable) ? value.map{ |v| serializable_value[v, *a] } : serializable_value[value, *a])
28
+ }
29
+
30
+ hash = {}
31
+ options = Hash[*options.map{|v| [v, true]}.flatten(2)] if options.is_a?(Array)
32
+ self.class.attribute_names.each do |n|
33
+ opts = options ? options[n] : nil
34
+ empty = options.nil? || options.empty?
35
+ n_hash = "#{n}_hash"
36
+ if (opts || empty) && respond_to?(n_hash)
37
+ value = send(n_hash, *(method(n_hash).arity.zero? ? [] : [opts, global]))
38
+ hash[n] = value if value
39
+ next
40
+ end
41
+
42
+ value = send(n)
43
+ data = case opts
44
+ when Symbol
45
+ value.send(opts)
46
+ when true
47
+ enumerable_value[value, nil, global]
48
+ when false
49
+ nil
50
+ when nil
51
+ # auto detect value type if options nil too
52
+ empty ? enumerable_value[value, nil, global] : nil
53
+ when Hash
54
+ enumerable_value[value, opts, global]
55
+ end
56
+ hash[n] = data if data
57
+ end
58
+ hash
59
+ end
60
+
61
+ def copy
62
+ self.class.new(self.serializable_hash)
63
+ end
64
+
65
+ #<<<
66
+ end
67
+ end
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+
3
+ module ModelPack
4
+ module Serializers::JSON
5
+ def to_json
6
+ as_json.to_json
7
+ end
8
+
9
+ def as_json(options=nil)
10
+ serializable_hash(options)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module ModelPack
2
+ VERSION = "0.9.0"
3
+ end
data/lib/model_pack.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "model_pack/version"
2
+
3
+ module ModelPack
4
+ autoload :AttributeMethods, 'model_pack/attribute_methods'
5
+ autoload :ClassMethods, 'model_pack/class_methods'
6
+ autoload :Concern, 'model_pack/concern'
7
+ autoload :Constructor, 'model_pack/constructor'
8
+ autoload :Document, 'model_pack/document'
9
+ autoload :Serialization, 'model_pack/serialization'
10
+
11
+ module Serializers
12
+ autoload :JSON, 'model_pack/serializers/json'
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'model_pack/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "model_pack"
8
+ spec.version = ModelPack::VERSION
9
+ spec.authors = ["che"]
10
+ spec.email = ["chelovekov@gmail.com"]
11
+ spec.summary = %q{Model Pack – simple model interfaces.}
12
+ #spec.description = %q{TODO: Write a longer description. Optional.}
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.6"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.0.0"
24
+ end
@@ -0,0 +1,139 @@
1
+ require "spec_helper"
2
+
3
+ describe ModelPack::ClassMethods do
4
+
5
+ it "should create model with default fields" do
6
+ class Point
7
+ include ModelPack::Document
8
+
9
+ attribute :x, default: 0
10
+ attribute :y, default: 0
11
+ end
12
+
13
+ point = Point.new
14
+ expect(point.x).to be(0)
15
+ expect(point.y).to be(0)
16
+ expect(point.attributes).to include({x: 0, y: 0})
17
+ end
18
+
19
+ it "should create model and fill it thought initialization" do
20
+ point = Point.new(x: 3, y: 5)
21
+ expect(point.x).to be(3)
22
+ expect(point.y).to be(5)
23
+ expect(point.attributes).to include({x: 3, y: 5})
24
+ end
25
+
26
+ it "should create embedded models" do
27
+ class Line
28
+ include ModelPack::Document
29
+
30
+ object :from, class_name: Point
31
+ object :to, class_name: Point
32
+
33
+ def length
34
+ Math.sqrt((from.x-to.x)**2 + (from.y-to.y)**2)
35
+ end
36
+ end
37
+
38
+ line = Line.new({
39
+ from: { x: 1, y: 1 },
40
+ to: { x: 3, y: 5}
41
+ })
42
+ expect(line.from).to be_a(Point)
43
+ expect(line.to).to be_a(Point)
44
+ expect(line.from.attributes).to include({x: 1, y: 1})
45
+ expect(line.to.attributes).to include({x: 3, y: 5})
46
+ expect(line.length).to be_within(0.1).of(4.4)
47
+ end
48
+
49
+ it "should create array of models" do
50
+ class Polygon
51
+ include ModelPack::Document
52
+
53
+ array :points, class_name: Point
54
+
55
+ def sides
56
+ return [] if points.size < 2
57
+ [[points.first]].tap do |sides|
58
+ (1...(points.size)).to_a.each do |index|
59
+ sides.last.push points[index]
60
+ sides.push [points[index]]
61
+ end
62
+ sides.last.push(points.first)
63
+ end
64
+ end
65
+
66
+ def perimeter
67
+ sides.inject(0) { |sum, side| sum + Math.sqrt((side[0].x-side[1].x)**2 + (side[0].y-side[1].y)**2) }
68
+ end
69
+ end
70
+
71
+ polygon = Polygon.new(points: [{x: 0, y: 0}, {x:5, y:0}, {x:5, y:5}])
72
+ expect(polygon.perimeter).to be_within(0.1).of(17.0)
73
+ end
74
+
75
+ it "should serialize model to hash" do
76
+ polygon = Polygon.new(points: [{x: 0, y: 0}, {x:5, y:0}, {x:5, y:5}])
77
+ json = polygon.as_json()
78
+ expect(json).to include(:points)
79
+ expect(json[:points]).to be_a(Array)
80
+ expect(json[:points][0]).to include({x: 0, y:0})
81
+ expect(json[:points][1]).to include({x: 5, y:0})
82
+ expect(json[:points][2]).to include({x: 5, y:5})
83
+ end
84
+
85
+ it "should serialize model with custom serializer" do
86
+ class SecureData
87
+ include ModelPack::Document
88
+
89
+ attribute :hidden_field, writer: lambda { |v| nil }
90
+ attribute :const_field, writer: lambda { |v| :always_this }
91
+ attribute :always_string, writer: lambda { |v| v.to_s }
92
+ end
93
+
94
+ secure_data = SecureData.new( hidden_field: "secured text", const_field: :some_value, always_string: 55)
95
+ unsecure_hash = secure_data.serializable_hash
96
+ expect(unsecure_hash).not_to include(:hidden_field)
97
+ expect(unsecure_hash).to include(const_field: :always_this)
98
+ expect(unsecure_hash).to include(always_string: "55")
99
+ end
100
+
101
+ it "should serialize and load back model" do
102
+ polygon = Polygon.new(points: [{x: 3, y: 3}, {x:2, y:1}, {x:4, y:2}])
103
+ polygon_copy = Polygon.new(polygon.serializable_hash) # or as_json
104
+ expect(polygon_copy.points).to be_a(Array)
105
+ polygon.points.each_with_index do |point, index|
106
+ expect(polygon_copy.points[index].attributes).to include(point.attributes)
107
+ end
108
+ end
109
+
110
+ it "should create copy of model" do
111
+ polygon = Polygon.new(points: [{x: 3, y: 3}, {x:2, y:1}, {x:4, y:2}])
112
+ polygon_copy = polygon.copy
113
+ polygon.points.each_with_index do |point, index|
114
+ expect(polygon_copy.points[index].attributes).to include(point.attributes)
115
+ end
116
+ end
117
+
118
+ it "should model have hashable field" do
119
+ class Options
120
+ include ModelPack::Document
121
+
122
+ hashable :options
123
+
124
+ def method_missing(name, *a)
125
+ options[name]
126
+ end
127
+ end
128
+
129
+ options = Options.new(options: { a: 5, b: 6 })
130
+
131
+ expect(options.a).to be(5)
132
+ expect(options.b).to be(6)
133
+ end
134
+
135
+ it "should serialize model with custom serializer" do
136
+
137
+ end
138
+
139
+ end
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require File.expand_path("./lib/model_pack")
5
+
6
+ RSpec.configure do |config|
7
+ config.color = true
8
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: model_pack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - che
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 3.0.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 3.0.0
55
+ description:
56
+ email:
57
+ - chelovekov@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - lib/model_pack.rb
68
+ - lib/model_pack/attribute_methods.rb
69
+ - lib/model_pack/class_methods.rb
70
+ - lib/model_pack/concern.rb
71
+ - lib/model_pack/constructor.rb
72
+ - lib/model_pack/document.rb
73
+ - lib/model_pack/serialization.rb
74
+ - lib/model_pack/serializers/json.rb
75
+ - lib/model_pack/version.rb
76
+ - model_pack.gemspec
77
+ - spec/model_pack_spec.rb
78
+ - spec/spec_helper.rb
79
+ homepage: ''
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.4.1
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Model Pack – simple model interfaces.
103
+ test_files:
104
+ - spec/model_pack_spec.rb
105
+ - spec/spec_helper.rb