plyushkin 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +58 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/plyushkin.rb +15 -0
- data/lib/plyushkin/base_value.rb +59 -0
- data/lib/plyushkin/core_ext/active_record_base.rb +27 -0
- data/lib/plyushkin/core_ext/plyushkin_extensions.rb +26 -0
- data/lib/plyushkin/model.rb +35 -0
- data/lib/plyushkin/nil_value.rb +9 -0
- data/lib/plyushkin/persistence.rb +50 -0
- data/lib/plyushkin/property.rb +76 -0
- data/lib/plyushkin/service.rb +3 -0
- data/lib/plyushkin/service/stub.rb +15 -0
- data/lib/plyushkin/string_value.rb +3 -0
- data/lib/plyushkin/test.rb +1 -0
- data/lib/plyushkin/test/value_types.rb +17 -0
- data/lib/plyushkin/validators/presence.rb +5 -0
- data/lib/plyushkin/version.rb +3 -0
- data/plyushkin.gemspec +25 -0
- data/spec/lib/base_value_spec.rb +128 -0
- data/spec/lib/core_ext/active_record_base_spec.rb +160 -0
- data/spec/lib/nil_value_spec.rb +11 -0
- data/spec/lib/persistence_spec.rb +79 -0
- data/spec/lib/property_spec.rb +136 -0
- data/spec/lib/value_spec.rb +30 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/sqlite.rb +12 -0
- metadata +135 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
plyushkin (0.0.1)
|
5
|
+
activerecord (~> 3.2.12)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (3.2.16)
|
11
|
+
activesupport (= 3.2.16)
|
12
|
+
builder (~> 3.0.0)
|
13
|
+
activerecord (3.2.16)
|
14
|
+
activemodel (= 3.2.16)
|
15
|
+
activesupport (= 3.2.16)
|
16
|
+
arel (~> 3.0.2)
|
17
|
+
tzinfo (~> 0.3.29)
|
18
|
+
activesupport (3.2.16)
|
19
|
+
i18n (~> 0.6, >= 0.6.4)
|
20
|
+
multi_json (~> 1.0)
|
21
|
+
arel (3.0.3)
|
22
|
+
builder (3.0.4)
|
23
|
+
coderay (1.1.0)
|
24
|
+
diff-lcs (1.2.5)
|
25
|
+
i18n (0.6.9)
|
26
|
+
method_source (0.8.2)
|
27
|
+
multi_json (1.8.4)
|
28
|
+
pry (0.9.12.4)
|
29
|
+
coderay (~> 1.0)
|
30
|
+
method_source (~> 0.8)
|
31
|
+
slop (~> 3.4)
|
32
|
+
pry-nav (0.2.3)
|
33
|
+
pry (~> 0.9.10)
|
34
|
+
rake (10.1.1)
|
35
|
+
rspec (2.14.1)
|
36
|
+
rspec-core (~> 2.14.0)
|
37
|
+
rspec-expectations (~> 2.14.0)
|
38
|
+
rspec-mocks (~> 2.14.0)
|
39
|
+
rspec-core (2.14.7)
|
40
|
+
rspec-expectations (2.14.4)
|
41
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
42
|
+
rspec-mocks (2.14.4)
|
43
|
+
slop (3.4.7)
|
44
|
+
sqlite3 (1.3.8)
|
45
|
+
timecop (0.7.1)
|
46
|
+
tzinfo (0.3.38)
|
47
|
+
|
48
|
+
PLATFORMS
|
49
|
+
ruby
|
50
|
+
|
51
|
+
DEPENDENCIES
|
52
|
+
bundler (~> 1.3)
|
53
|
+
plyushkin!
|
54
|
+
pry-nav
|
55
|
+
rake
|
56
|
+
rspec
|
57
|
+
sqlite3
|
58
|
+
timecop
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Craig Israel
|
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,29 @@
|
|
1
|
+
# Plyushkin
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'plyushkin'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install plyushkin
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/plyushkin.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "plyushkin/version"
|
2
|
+
require "active_record"
|
3
|
+
|
4
|
+
module Plyushkin; end
|
5
|
+
path = File.dirname(File.expand_path(__FILE__))
|
6
|
+
require path + "/plyushkin/base_value"
|
7
|
+
require path + "/plyushkin/string_value"
|
8
|
+
require path + "/plyushkin/nil_value"
|
9
|
+
require path + "/plyushkin/property"
|
10
|
+
require path + "/plyushkin/model"
|
11
|
+
require path + "/plyushkin/service"
|
12
|
+
require path + "/plyushkin/persistence"
|
13
|
+
require path + "/plyushkin/validators/presence"
|
14
|
+
require path + "/plyushkin/core_ext/plyushkin_extensions"
|
15
|
+
require path + "/plyushkin/core_ext/active_record_base"
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class Plyushkin::BaseValue
|
2
|
+
include ActiveModel::Validations
|
3
|
+
|
4
|
+
def initialize(attr={})
|
5
|
+
attr = attr.dup
|
6
|
+
@date = attr.delete(:date) || DateTime.current
|
7
|
+
attr.each do |k,v|
|
8
|
+
send("#{k}=", v) if respond_to?("#{k}=")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.persisted_attr(*attributes)
|
13
|
+
opts = attributes.last.is_a?(Hash) ? attributes.pop : {}
|
14
|
+
names = attributes
|
15
|
+
formatter = opts[:formatter]
|
16
|
+
|
17
|
+
persisted_attributes.concat(names)
|
18
|
+
attr_writer *names
|
19
|
+
names.each do |name|
|
20
|
+
define_method(name) do
|
21
|
+
value = instance_variable_get("@#{name}")
|
22
|
+
if formatter
|
23
|
+
send(formatter, value)
|
24
|
+
else
|
25
|
+
value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.persisted_attributes
|
32
|
+
if self.superclass == Object
|
33
|
+
@persisted_attributes ||= []
|
34
|
+
else
|
35
|
+
@persisted_attributes ||= superclass.persisted_attributes.dup
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
persisted_attr :date
|
40
|
+
validates_each :date do |record, attr_name, value|
|
41
|
+
record.errors.add(attr_name, "cannot be in the future") unless value < DateTime.now
|
42
|
+
end
|
43
|
+
|
44
|
+
def equal_value?(other)
|
45
|
+
self.class.persisted_attributes.each do |attribute|
|
46
|
+
return false if attribute != :date && send(attribute) != other.send(attribute)
|
47
|
+
end
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_i(value)
|
52
|
+
value.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_date(value)
|
56
|
+
value.is_a?(String) ? DateTime.parse(value) : value
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
ActiveRecord::Base.instance_eval do
|
2
|
+
def hoards(name, opts = {})
|
3
|
+
initialize_plyushkin
|
4
|
+
|
5
|
+
define_method(name) do
|
6
|
+
plyushkin.properties[name]
|
7
|
+
end
|
8
|
+
validates name, :plyushkin => true
|
9
|
+
|
10
|
+
plyushkin_model.register(name, opts[:type], opts)
|
11
|
+
plyushkin_model.register_callback(name, :after_create, opts[:after_create]) if opts[:after_create]
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize_plyushkin
|
15
|
+
class << self
|
16
|
+
def plyushkin_model
|
17
|
+
@plyushkin_model ||= Plyushkin::Model.new(Plyushkin::Service::Stub.new)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
include PlyushkinExtensions
|
22
|
+
|
23
|
+
after_initialize :load_plyushkin
|
24
|
+
after_save :save_plyushkin
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module PlyushkinExtensions
|
2
|
+
def reload(options=nil)
|
3
|
+
plyushkin.load(id)
|
4
|
+
super
|
5
|
+
end
|
6
|
+
|
7
|
+
def plyushkin
|
8
|
+
@plyushkin ||= Plyushkin::Persistence.new(self.class.plyushkin_model)
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_plyushkin
|
12
|
+
self.class.plyushkin_model.callbacks.each do |name,callbacks|
|
13
|
+
callbacks.each do |callback, handler|
|
14
|
+
plyushkin.register_callback(name, callback) do
|
15
|
+
(handler && handler.is_a?(Symbol)) ? send(handler) : handler.call
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
plyushkin.load(id)
|
21
|
+
end
|
22
|
+
|
23
|
+
def save_plyushkin
|
24
|
+
plyushkin.save(id)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Plyushkin::Model
|
2
|
+
|
3
|
+
def initialize(service)
|
4
|
+
@service = service
|
5
|
+
@types = {}
|
6
|
+
@ignore_unchanged_values = {}
|
7
|
+
@callbacks = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def register(name, type, opts={})
|
11
|
+
@types[name] = type
|
12
|
+
@ignore_unchanged_values[name] = opts[:ignore_unchanged_values]
|
13
|
+
end
|
14
|
+
|
15
|
+
def register_callback(name, callback, method_sym)
|
16
|
+
@callbacks[name] = { callback => method_sym }
|
17
|
+
end
|
18
|
+
|
19
|
+
def registered_types
|
20
|
+
@types.dup
|
21
|
+
end
|
22
|
+
|
23
|
+
def callbacks
|
24
|
+
@callbacks.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
def ignore_unchanged_values
|
28
|
+
@ignore_unchanged_values.dup
|
29
|
+
end
|
30
|
+
|
31
|
+
def service
|
32
|
+
@service
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Plyushkin::Persistence
|
2
|
+
|
3
|
+
def initialize(model)
|
4
|
+
@model = model
|
5
|
+
@callbacks = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def properties
|
9
|
+
@properties
|
10
|
+
end
|
11
|
+
|
12
|
+
def save(id)
|
13
|
+
hash = {}
|
14
|
+
(@properties || {}).each do |name, property|
|
15
|
+
hash[name] = [property.value_hashes]
|
16
|
+
end
|
17
|
+
model.service.put(id, hash.to_json)
|
18
|
+
end
|
19
|
+
|
20
|
+
def load(id)
|
21
|
+
@properties = {}
|
22
|
+
model.service.get(id).each do |name, values|
|
23
|
+
property = Plyushkin::Property.build(name, model.registered_types[name.to_sym], values,
|
24
|
+
:callbacks => @callbacks[name.to_sym],
|
25
|
+
:ignore_unchanged_values => @model.ignore_unchanged_values[name.to_sym] )
|
26
|
+
@properties[name.to_sym] = property
|
27
|
+
end
|
28
|
+
add_missing_properties
|
29
|
+
end
|
30
|
+
|
31
|
+
def register_callback(name, callback, &block)
|
32
|
+
@callbacks[name] = { callback => block }
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def model
|
37
|
+
@model
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_missing_properties
|
41
|
+
(model.registered_types.keys - @properties.keys).each do |name|
|
42
|
+
property = Plyushkin::Property.new(name,
|
43
|
+
:type => model.registered_types[name],
|
44
|
+
:callbacks => @callbacks[name],
|
45
|
+
:ignore_unchanged_values => @model.ignore_unchanged_values[name])
|
46
|
+
@properties[name] = property
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class Plyushkin::Property
|
2
|
+
include ActiveModel::Validations
|
3
|
+
|
4
|
+
DEFAULT_CALLBACK = lambda{}
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
validate do |property|
|
9
|
+
last.errors.full_messages.each do |message|
|
10
|
+
errors[:base] << "#{name.camelize}: #{message}"
|
11
|
+
end unless last.valid?
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(name, opts={})
|
15
|
+
@values = []
|
16
|
+
@value_type = opts[:type]
|
17
|
+
@callbacks = opts[:callbacks]
|
18
|
+
@ignore_unchanged_values = opts[:ignore_unchanged_values]
|
19
|
+
@name = name.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
def create(attr={})
|
23
|
+
value = value_type.new(attr)
|
24
|
+
if @ignore_unchanged_values
|
25
|
+
last = @values.last
|
26
|
+
return last if last && last.equal_value?(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
@values.insert(insert_position(value.date), value)
|
30
|
+
trigger_callback(:after_create)
|
31
|
+
value
|
32
|
+
end
|
33
|
+
|
34
|
+
def trigger_callback(sym)
|
35
|
+
@callbacks.fetch(sym, DEFAULT_CALLBACK).call if @callbacks
|
36
|
+
end
|
37
|
+
|
38
|
+
def all
|
39
|
+
@values
|
40
|
+
end
|
41
|
+
|
42
|
+
def last
|
43
|
+
@values.last || Plyushkin::NilValue.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def empty?
|
47
|
+
@values.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
def insert_position(datetime)
|
51
|
+
index = @values.rindex{|v| v.date < datetime}
|
52
|
+
index.nil? ? 0 : index + 1
|
53
|
+
end
|
54
|
+
|
55
|
+
def value_type
|
56
|
+
@value_type ||= Plyushkin::StringValue
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.build(name, type, values, opts={})
|
60
|
+
opts[:type] = type
|
61
|
+
Plyushkin::Property.new(name, opts).tap do |p|
|
62
|
+
values.each { |value| p.create(value) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def value_hashes
|
67
|
+
value_hashes = {}
|
68
|
+
all.each do |value|
|
69
|
+
value_type.persisted_attributes.each do |attr|
|
70
|
+
value_hashes[attr] = value.send(attr)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
value_hashes
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(File.expand_path(__FILE__)) + "/test/value_types.rb"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Plyushkin::Test
|
2
|
+
class Plyushkin::Test::PresenceTestValue < Plyushkin::BaseValue
|
3
|
+
persisted_attr :value
|
4
|
+
validates :value, :presence => true
|
5
|
+
end
|
6
|
+
|
7
|
+
class Plyushkin::Test::CoordinateValue < Plyushkin::BaseValue
|
8
|
+
persisted_attr :x, :y
|
9
|
+
validates :x, :y, :presence => true
|
10
|
+
end
|
11
|
+
|
12
|
+
class Plyushkin::Test::Member < ActiveRecord::Base; end
|
13
|
+
|
14
|
+
class Plyushkin::Test::DateValue < Plyushkin::BaseValue
|
15
|
+
persisted_attr :value, :formatter => :to_date
|
16
|
+
end
|
17
|
+
end
|
data/plyushkin.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'plyushkin/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "plyushkin"
|
8
|
+
spec.version = Plyushkin::VERSION
|
9
|
+
spec.authors = ["Craig Israel", "Jeremy Hinkle"]
|
10
|
+
spec.email = ["craig@theisraels.net", "jchinkle@gmail.com"]
|
11
|
+
spec.description = %q{Provides active record extension to capture historical property data}
|
12
|
+
spec.summary = %q{Plyushkin - the attribute hoarder}
|
13
|
+
spec.homepage = "http://github.com/OnlifeHealth/plyushkin"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
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_dependency "activerecord", "~> 3.2.12"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Plyushkin::BaseValue do
|
4
|
+
describe '#valid?' do
|
5
|
+
it 'should not be valid user-defined value if validations fail' do
|
6
|
+
value = Plyushkin::Test::CoordinateValue.new
|
7
|
+
value.valid?
|
8
|
+
value.errors.count.should == 2
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should be valid user-defined value if validations pass' do
|
12
|
+
value = Plyushkin::Test::CoordinateValue.new(:x => 5, :y => 10)
|
13
|
+
value.valid?
|
14
|
+
value.errors.count.should == 0
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should not be valid if date is in the future' do
|
18
|
+
value = Plyushkin::StringValue.new(:date => 2.days.from_now)
|
19
|
+
value.should_not be_valid
|
20
|
+
value.errors.full_messages.should == ["Date cannot be in the future"]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '##persisted_attr' do
|
25
|
+
it 'should add attribute to persisted_attributes' do
|
26
|
+
clazz = Class.new(Plyushkin::BaseValue) do
|
27
|
+
persisted_attr :my_attr
|
28
|
+
end
|
29
|
+
|
30
|
+
clazz.persisted_attributes.should == [ :date, :my_attr ]
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should add attributes to persisted_attributes when called multiple times' do
|
34
|
+
clazz = Class.new(Plyushkin::BaseValue) do
|
35
|
+
persisted_attr :my_attr
|
36
|
+
persisted_attr :my_other_attr
|
37
|
+
end
|
38
|
+
|
39
|
+
clazz.persisted_attributes.should == [ :date, :my_attr, :my_other_attr ]
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should add attributes to persisted_attributes when called with multiple attributes' do
|
43
|
+
clazz = Class.new(Plyushkin::BaseValue) do
|
44
|
+
persisted_attr :my_attr, :my_other_attr
|
45
|
+
end
|
46
|
+
|
47
|
+
clazz.persisted_attributes.should == [ :date, :my_attr, :my_other_attr ]
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should add reader and writer for attribute' do
|
51
|
+
clazz = Class.new(Plyushkin::BaseValue) do
|
52
|
+
persisted_attr :my_attr
|
53
|
+
end
|
54
|
+
|
55
|
+
value = clazz.new
|
56
|
+
value.my_attr = 5
|
57
|
+
value.my_attr.should == 5
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should format value readers using format methods when specified" do
|
61
|
+
clazz = Class.new(Plyushkin::BaseValue) do
|
62
|
+
persisted_attr :my_attr, :formatter => :to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
value = clazz.new
|
66
|
+
value.my_attr = "5"
|
67
|
+
value.my_attr.should == 5
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should format multiple value readers using format methods when specified" do
|
71
|
+
clazz = Class.new(Plyushkin::BaseValue) do
|
72
|
+
persisted_attr :my_attr, :my_attr2, :formatter => :to_i
|
73
|
+
end
|
74
|
+
|
75
|
+
value = clazz.new
|
76
|
+
value.my_attr = "5"
|
77
|
+
value.my_attr2 = "9999"
|
78
|
+
value.my_attr.should == 5
|
79
|
+
value.my_attr2.should == 9999
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#to_date' do
|
83
|
+
it 'format a json string to a date' do
|
84
|
+
value = Plyushkin::BaseValue.new
|
85
|
+
now = DateTime.now
|
86
|
+
value.to_date(now.to_json).should === now
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#equal_value' do
|
93
|
+
it 'should test equality of persisted_attributes' do
|
94
|
+
clazz = Class.new(Plyushkin::BaseValue) do
|
95
|
+
persisted_attr :my_attr, :my_attr2
|
96
|
+
end
|
97
|
+
|
98
|
+
value1 = clazz.new
|
99
|
+
value1.my_attr = 5
|
100
|
+
value1.my_attr2 = "test"
|
101
|
+
|
102
|
+
value2 = clazz.new
|
103
|
+
value2.my_attr = 5
|
104
|
+
value2.my_attr2 = "test"
|
105
|
+
|
106
|
+
value1.equal_value?(value2).should be_true
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should be true if dates are different' do
|
110
|
+
clazz = Class.new(Plyushkin::BaseValue) do
|
111
|
+
persisted_attr :my_attr, :my_attr2
|
112
|
+
end
|
113
|
+
|
114
|
+
value1 = clazz.new
|
115
|
+
value1.my_attr = 5
|
116
|
+
value1.my_attr2 = "test"
|
117
|
+
value1.date = DateTime.now - 3.days
|
118
|
+
|
119
|
+
value2 = clazz.new
|
120
|
+
value2.my_attr = 5
|
121
|
+
value2.my_attr2 = "test"
|
122
|
+
value2.date = DateTime.now - 5.days
|
123
|
+
|
124
|
+
value1.equal_value?(value2).should be_true
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecord::Base do
|
4
|
+
describe '##hoards' do
|
5
|
+
describe 'read' do
|
6
|
+
it 'should return the same property instance when called multiple times' do
|
7
|
+
clazz = Class.new(Plyushkin::Test::Member) do
|
8
|
+
hoards :login_date
|
9
|
+
end
|
10
|
+
|
11
|
+
member = clazz.new
|
12
|
+
property = member.login_date
|
13
|
+
property.class.should == Plyushkin::Property
|
14
|
+
property.should == member.login_date
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should register a type for a hoarding property with persistence class' do
|
18
|
+
clazz = Class.new(Plyushkin::Test::Member) do
|
19
|
+
hoards :login_date
|
20
|
+
hoards :geolocation, :type => Plyushkin::Test::CoordinateValue
|
21
|
+
end
|
22
|
+
|
23
|
+
clazz.plyushkin_model.registered_types[:login_date].should be_nil
|
24
|
+
clazz.plyushkin_model.registered_types[:geolocation].should == Plyushkin::Test::CoordinateValue
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should allow specifying value type' do
|
28
|
+
clazz = Class.new(Plyushkin::Test::Member) do
|
29
|
+
hoards :geolocation, :type => Plyushkin::Test::CoordinateValue
|
30
|
+
end
|
31
|
+
|
32
|
+
member = clazz.new
|
33
|
+
property = member.geolocation
|
34
|
+
property.value_type.should == Plyushkin::Test::CoordinateValue
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'after_create option' do
|
39
|
+
it 'should call method specified by option after property create is called' do
|
40
|
+
clazz = Class.new(Plyushkin::Test::Member) do
|
41
|
+
attr_accessor :login_date_created_called
|
42
|
+
hoards :login_date, :after_create => :login_date_created
|
43
|
+
|
44
|
+
def login_date_created
|
45
|
+
self.login_date_created_called = true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
member = clazz.new
|
50
|
+
member.login_date.create :value => DateTime.now
|
51
|
+
member.login_date_created_called.should be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should call method specified by option after property create is called for the property named' do
|
55
|
+
clazz = Class.new(Plyushkin::Test::Member) do
|
56
|
+
attr_accessor :login_date_created_called, :other_prop_created_called
|
57
|
+
|
58
|
+
hoards :login_date, :after_create => :login_date_created
|
59
|
+
hoards :other_prop, :after_create => :other_prop_created
|
60
|
+
|
61
|
+
def login_date_created
|
62
|
+
self.login_date_created_called = true
|
63
|
+
end
|
64
|
+
|
65
|
+
def other_prop_created
|
66
|
+
self.other_prop_created_called = true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
member = clazz.new
|
71
|
+
member.other_prop.create :value => DateTime.now
|
72
|
+
member.other_prop_created_called.should be_true
|
73
|
+
member.login_date_created_called.should be_false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'ignore_unchanged_value option' do
|
78
|
+
it 'should not add a value when ignore_unchanged_value is true and value is the same as the last value' do
|
79
|
+
clazz = Class.new(Plyushkin::Test::Member) do
|
80
|
+
hoards :login_date, :ignore_unchanged_values => true
|
81
|
+
end
|
82
|
+
|
83
|
+
member = clazz.new
|
84
|
+
now = DateTime.now
|
85
|
+
member.login_date.create(:value => now)
|
86
|
+
member.login_date.create(:value => now)
|
87
|
+
|
88
|
+
member.login_date.all.length.should == 1
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#validates' do
|
94
|
+
it 'should not be valid if hoarding property is not valid' do
|
95
|
+
clazz = Class.new(Plyushkin::Test::Member) do
|
96
|
+
hoards :geolocation, :type => Plyushkin::Test::CoordinateValue
|
97
|
+
end
|
98
|
+
clazz.stub(:model_name).and_return(ActiveModel::Name.new(clazz, nil, "test"))
|
99
|
+
|
100
|
+
member = clazz.new
|
101
|
+
member.geolocation.create(:x => 5)
|
102
|
+
member.should_not be_valid
|
103
|
+
member.errors.messages[:geolocation].count.should == 1
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#save' do
|
108
|
+
let(:clazz) do
|
109
|
+
clazz = Class.new(Plyushkin::Test::Member) do
|
110
|
+
hoards :login_date, :type => Plyushkin::Test::DateValue
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should load plyushkin persistence after find is called' do
|
115
|
+
member1 = clazz.new
|
116
|
+
now = DateTime.now
|
117
|
+
member1.login_date.create(:value => now)
|
118
|
+
member1.save!
|
119
|
+
|
120
|
+
member2 = clazz.find(member1.id)
|
121
|
+
member2.login_date.last.value.should === now
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should save hoarding property by id' do
|
125
|
+
member1 = clazz.new
|
126
|
+
now = DateTime.now
|
127
|
+
later = DateTime.now + 5
|
128
|
+
member1.login_date.create(:value => now)
|
129
|
+
member1.save!
|
130
|
+
|
131
|
+
member2 = clazz.new
|
132
|
+
member2.login_date.create(:value => later)
|
133
|
+
member2.save!
|
134
|
+
|
135
|
+
member1.reload.login_date.last.value.should === now
|
136
|
+
member2.reload.login_date.last.value.should === later
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#reload' do
|
141
|
+
let(:clazz) do
|
142
|
+
clazz = Class.new(Plyushkin::Test::Member) do
|
143
|
+
hoards :login_date, :type => Plyushkin::Test::DateValue
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should reload new data from persisted store' do
|
148
|
+
member = clazz.new
|
149
|
+
now = DateTime.now
|
150
|
+
member.login_date.create(:value => now)
|
151
|
+
member.save!
|
152
|
+
member.login_date.create(:value => now + 5)
|
153
|
+
|
154
|
+
member.reload
|
155
|
+
member.login_date.last.value.should === now
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Plyushkin::Persistence do
|
4
|
+
let(:service) do
|
5
|
+
service = Plyushkin::Service::Stub.new
|
6
|
+
service.put(1,
|
7
|
+
{ :name => [{ :value => "Steve" }] ,
|
8
|
+
:weight => [{ :value => 150 }],
|
9
|
+
:udt => [{ :x => 10, :y => 20 }]
|
10
|
+
}.to_json)
|
11
|
+
service
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:model) do
|
15
|
+
m = Plyushkin::Model.new(service)
|
16
|
+
m.register(:name, Plyushkin::StringValue)
|
17
|
+
m.register(:weight, Plyushkin::StringValue)
|
18
|
+
m.register(:udt, Plyushkin::Test::CoordinateValue)
|
19
|
+
m
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:persistence) do
|
23
|
+
p = Plyushkin::Persistence.new(model)
|
24
|
+
p.load(1)
|
25
|
+
p
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#properties' do
|
29
|
+
|
30
|
+
it 'should allow access to properties' do
|
31
|
+
persistence.properties.length.should == 3
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#save' do
|
36
|
+
it 'should save to source that load uses' do
|
37
|
+
persistence.properties[:name].create(:value => "Mike")
|
38
|
+
persistence.save(1)
|
39
|
+
persistence.load(1)
|
40
|
+
persistence.properties[:name].last.value.should == "Mike"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#load' do
|
45
|
+
it 'should parse json into an hash of properties' do
|
46
|
+
persistence.load(1)
|
47
|
+
property = persistence.properties[:name]
|
48
|
+
property.class.should == Plyushkin::Property
|
49
|
+
property.last.value.should == "Steve"
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should parse json for a user-defined type' do
|
53
|
+
persistence.load(1)
|
54
|
+
property = persistence.properties[:udt]
|
55
|
+
property.last.x.should == 10
|
56
|
+
property.last.y.should == 20
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should parse all history for a property' do
|
60
|
+
service.put(1,
|
61
|
+
{
|
62
|
+
:name => [
|
63
|
+
{ :value => "Ms. Julie Jones", :date => DateTime.now - 2.days },
|
64
|
+
{ :value => "Mrs. Julie Smith", :date => DateTime.now - 3.days }
|
65
|
+
]
|
66
|
+
}.to_json)
|
67
|
+
|
68
|
+
property = persistence.properties[:name]
|
69
|
+
property.all.length.should == 2
|
70
|
+
property.all[0].value.should == "Mrs. Julie Smith"
|
71
|
+
property.all[1].value.should == "Ms. Julie Jones"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should add an empty array for a property that isn't returned from service" do
|
75
|
+
model.register(:missing_property, Plyushkin::StringValue)
|
76
|
+
persistence.properties[:missing_property].all.should == []
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Plyushkin::Property do
|
4
|
+
describe '#create' do
|
5
|
+
it 'should add a StringValue' do
|
6
|
+
property = Plyushkin::Property.new(:property_name)
|
7
|
+
value = property.create(:value => 5)
|
8
|
+
value.class.should == Plyushkin::StringValue
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should pass the attributes to the simple value' do
|
12
|
+
property = Plyushkin::Property.new(:property_name)
|
13
|
+
date = DateTime.now - 3.days
|
14
|
+
value = property.create(:value => 5, :date => date)
|
15
|
+
value.value.should == 5
|
16
|
+
value.date.should == date
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should add a user-defined value' do
|
20
|
+
property = Plyushkin::Property.new(:property_name, :type => Plyushkin::Test::CoordinateValue)
|
21
|
+
value = property.create(:x => 5, :y => 10)
|
22
|
+
value.class.should == Plyushkin::Test::CoordinateValue
|
23
|
+
value.x.should == 5
|
24
|
+
value.y.should == 10
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'callbacks' do
|
28
|
+
it 'should call after_create callback after create' do
|
29
|
+
called = 0
|
30
|
+
callbacks = { :after_create => lambda{ called += 1 } }
|
31
|
+
property = Plyushkin::Property.new(:property_name, :callbacks => callbacks)
|
32
|
+
property.create(:value => 5)
|
33
|
+
called.should == 1
|
34
|
+
property.last.value.should == 5
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should not require an after_create callback' do
|
38
|
+
called = 0
|
39
|
+
callbacks = { :before_create => lambda{ called += 1 } }
|
40
|
+
property = Plyushkin::Property.new(:property_name, :callbacks => callbacks)
|
41
|
+
property.create(:value => 5)
|
42
|
+
called.should == 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'ignore_unchanged_value' do
|
47
|
+
it 'should not add a value when ignore_unchanged_value is true and value does not change' do
|
48
|
+
property = Plyushkin::Property.new(:property_name, :ignore_unchanged_values => true)
|
49
|
+
property.create(:value => 5)
|
50
|
+
property.create(:value => 5)
|
51
|
+
property.all.length.should == 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#all' do
|
57
|
+
it 'should return an array of all created values' do
|
58
|
+
property = Plyushkin::Property.new(:property_name)
|
59
|
+
value1 = property.create(:value => 5)
|
60
|
+
value2 = property.create(:value => 7)
|
61
|
+
property.all.should == [ value1, value2 ]
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should be sorted by value class date' do
|
65
|
+
property = Plyushkin::Property.new(:property_name)
|
66
|
+
value1 = property.create(:value => 5, :date => DateTime.now - 5.days)
|
67
|
+
value2 = property.create(:value => 7, :date => DateTime.now - 7.days)
|
68
|
+
property.all.should == [ value2, value1 ]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#last' do
|
73
|
+
it 'should return the most-recently added value' do
|
74
|
+
property = Plyushkin::Property.new(:property_name)
|
75
|
+
value1 = property.create(:value => 5)
|
76
|
+
value2 = property.create(:value => 7)
|
77
|
+
property.last.should == value2
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should return a new value if there are no values' do
|
81
|
+
property = Plyushkin::Property.new(:property_name)
|
82
|
+
property.last.should be_a(Plyushkin::NilValue)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#valid?' do
|
87
|
+
it 'should be valid if no values exist' do
|
88
|
+
property = Plyushkin::Property.new(:property_name)
|
89
|
+
property.should be_valid
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should be valid if last is valid and other values in array are not valid' do
|
93
|
+
property = Plyushkin::Property.new(:property_name, :type => Plyushkin::Test::PresenceTestValue)
|
94
|
+
value1 = property.create
|
95
|
+
value2 = property.create(:value => 5)
|
96
|
+
property.should be_valid
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should not be valid if last is not valid' do
|
100
|
+
property = Plyushkin::Property.new(:property_name, :type => Plyushkin::Test::PresenceTestValue)
|
101
|
+
value1 = property.create(:value => 5)
|
102
|
+
value2 = property.create
|
103
|
+
property.should_not be_valid
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should populate errors from values when not valid' do
|
107
|
+
property = Plyushkin::Property.new(:property_name, :type => Plyushkin::Test::PresenceTestValue)
|
108
|
+
property.create
|
109
|
+
property.valid?
|
110
|
+
property.errors.full_messages.should == [ "PropertyName: Value can't be blank" ]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#empty?' do
|
115
|
+
it 'should return true if no values were created' do
|
116
|
+
property = Plyushkin::Property.new(:property_name, :type => Plyushkin::Test::PresenceTestValue)
|
117
|
+
property.should be_empty
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should return false if values were not created' do
|
121
|
+
property = Plyushkin::Property.new(:property_name, :type => Plyushkin::Test::PresenceTestValue)
|
122
|
+
property.create
|
123
|
+
property.should_not be_empty
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it '#insert_position' do
|
128
|
+
property = Plyushkin::Property.new(:property_name)
|
129
|
+
value1 = property.create(:value => 7, :date => DateTime.now - 7.days)
|
130
|
+
value2 = property.create(:value => 5, :date => DateTime.now - 5.days)
|
131
|
+
property.insert_position(DateTime.now).should == 2
|
132
|
+
property.insert_position(DateTime.now - 6.days).should == 1
|
133
|
+
property.insert_position(DateTime.now - 8.days).should == 0
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Plyushkin::StringValue do
|
4
|
+
describe '#new' do
|
5
|
+
it 'should allow storing an integer' do
|
6
|
+
val = Plyushkin::StringValue.new(:value => 5)
|
7
|
+
val.value.should == 5
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should allow storing a string' do
|
11
|
+
val = Plyushkin::StringValue.new(:value => 'test')
|
12
|
+
val.value.should == 'test'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should use current if not specified' do
|
16
|
+
Timecop.freeze do
|
17
|
+
val = Plyushkin::StringValue.new(:value => 5)
|
18
|
+
val.date.should == DateTime.current
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should use provided date if specified' do
|
23
|
+
date = DateTime.new(2013, 12, 20)
|
24
|
+
val = Plyushkin::StringValue.new(:value => 5, :date => date)
|
25
|
+
val.date.should == date
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
path = File.dirname(File.expand_path(__FILE__))
|
9
|
+
require path + '/../lib/plyushkin'
|
10
|
+
require path + '/../lib/plyushkin/test'
|
11
|
+
Dir[path + "/support/**/*.rb"].each{ |f| require f }
|
12
|
+
|
13
|
+
require 'timecop'
|
14
|
+
require 'pry'
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
18
|
+
config.run_all_when_everything_filtered = true
|
19
|
+
config.filter_run :focus
|
20
|
+
|
21
|
+
# Run specs in random order to surface order dependencies. If you find an
|
22
|
+
# order dependency and want to debug it, you can fix the order by providing
|
23
|
+
# the seed, which is printed after each run.
|
24
|
+
# --seed 1234
|
25
|
+
config.order = 'random'
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Add sqlite
|
2
|
+
I18n.enforce_available_locales = false
|
3
|
+
ActiveRecord::Migration.verbose = false
|
4
|
+
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
5
|
+
class CreateMembers < ActiveRecord::Migration
|
6
|
+
def self.up
|
7
|
+
create_table :members do |t|
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
CreateMembers.up
|
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: plyushkin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Craig Israel
|
9
|
+
- Jeremy Hinkle
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-01-22 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activerecord
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.2.12
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 3.2.12
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: bundler
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '1.3'
|
39
|
+
type: :development
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.3'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
description: Provides active record extension to capture historical property data
|
64
|
+
email:
|
65
|
+
- craig@theisraels.net
|
66
|
+
- jchinkle@gmail.com
|
67
|
+
executables: []
|
68
|
+
extensions: []
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- .gitignore
|
72
|
+
- .rspec
|
73
|
+
- Gemfile
|
74
|
+
- Gemfile.lock
|
75
|
+
- LICENSE.txt
|
76
|
+
- README.md
|
77
|
+
- Rakefile
|
78
|
+
- lib/plyushkin.rb
|
79
|
+
- lib/plyushkin/base_value.rb
|
80
|
+
- lib/plyushkin/core_ext/active_record_base.rb
|
81
|
+
- lib/plyushkin/core_ext/plyushkin_extensions.rb
|
82
|
+
- lib/plyushkin/model.rb
|
83
|
+
- lib/plyushkin/nil_value.rb
|
84
|
+
- lib/plyushkin/persistence.rb
|
85
|
+
- lib/plyushkin/property.rb
|
86
|
+
- lib/plyushkin/service.rb
|
87
|
+
- lib/plyushkin/service/stub.rb
|
88
|
+
- lib/plyushkin/string_value.rb
|
89
|
+
- lib/plyushkin/test.rb
|
90
|
+
- lib/plyushkin/test/value_types.rb
|
91
|
+
- lib/plyushkin/validators/presence.rb
|
92
|
+
- lib/plyushkin/version.rb
|
93
|
+
- plyushkin.gemspec
|
94
|
+
- spec/lib/base_value_spec.rb
|
95
|
+
- spec/lib/core_ext/active_record_base_spec.rb
|
96
|
+
- spec/lib/nil_value_spec.rb
|
97
|
+
- spec/lib/persistence_spec.rb
|
98
|
+
- spec/lib/property_spec.rb
|
99
|
+
- spec/lib/value_spec.rb
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
- spec/support/sqlite.rb
|
102
|
+
homepage: http://github.com/OnlifeHealth/plyushkin
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 1.8.23
|
124
|
+
signing_key:
|
125
|
+
specification_version: 3
|
126
|
+
summary: Plyushkin - the attribute hoarder
|
127
|
+
test_files:
|
128
|
+
- spec/lib/base_value_spec.rb
|
129
|
+
- spec/lib/core_ext/active_record_base_spec.rb
|
130
|
+
- spec/lib/nil_value_spec.rb
|
131
|
+
- spec/lib/persistence_spec.rb
|
132
|
+
- spec/lib/property_spec.rb
|
133
|
+
- spec/lib/value_spec.rb
|
134
|
+
- spec/spec_helper.rb
|
135
|
+
- spec/support/sqlite.rb
|