sbf-dm-types 1.3.0.beta
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 +45 -0
- data/.rspec +1 -0
- data/.rubocop.yml +468 -0
- data/.travis.yml +52 -0
- data/Gemfile +67 -0
- data/LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +4 -0
- data/dm-types.gemspec +26 -0
- data/lib/dm-types/api_key.rb +30 -0
- data/lib/dm-types/bcrypt_hash.rb +33 -0
- data/lib/dm-types/comma_separated_list.rb +28 -0
- data/lib/dm-types/csv.rb +34 -0
- data/lib/dm-types/enum.rb +55 -0
- data/lib/dm-types/epoch_time.rb +35 -0
- data/lib/dm-types/file_path.rb +24 -0
- data/lib/dm-types/flag.rb +64 -0
- data/lib/dm-types/ip_address.rb +34 -0
- data/lib/dm-types/json.rb +48 -0
- data/lib/dm-types/paranoid/base.rb +56 -0
- data/lib/dm-types/paranoid_boolean.rb +23 -0
- data/lib/dm-types/paranoid_datetime.rb +22 -0
- data/lib/dm-types/regexp.rb +21 -0
- data/lib/dm-types/slug.rb +28 -0
- data/lib/dm-types/support/dirty_minder.rb +168 -0
- data/lib/dm-types/support/flags.rb +41 -0
- data/lib/dm-types/uri.rb +27 -0
- data/lib/dm-types/uuid.rb +64 -0
- data/lib/dm-types/version.rb +5 -0
- data/lib/dm-types/yaml.rb +39 -0
- data/lib/dm-types.rb +23 -0
- data/spec/fixtures/api_user.rb +14 -0
- data/spec/fixtures/article.rb +35 -0
- data/spec/fixtures/bookmark.rb +23 -0
- data/spec/fixtures/invention.rb +7 -0
- data/spec/fixtures/network_node.rb +36 -0
- data/spec/fixtures/person.rb +25 -0
- data/spec/fixtures/software_package.rb +33 -0
- data/spec/fixtures/ticket.rb +21 -0
- data/spec/fixtures/tshirt.rb +24 -0
- data/spec/integration/api_key_spec.rb +27 -0
- data/spec/integration/bcrypt_hash_spec.rb +47 -0
- data/spec/integration/comma_separated_list_spec.rb +87 -0
- data/spec/integration/dirty_minder_spec.rb +197 -0
- data/spec/integration/enum_spec.rb +78 -0
- data/spec/integration/epoch_time_spec.rb +61 -0
- data/spec/integration/file_path_spec.rb +160 -0
- data/spec/integration/flag_spec.rb +72 -0
- data/spec/integration/ip_address_spec.rb +153 -0
- data/spec/integration/json_spec.rb +72 -0
- data/spec/integration/slug_spec.rb +67 -0
- data/spec/integration/uri_spec.rb +117 -0
- data/spec/integration/uuid_spec.rb +102 -0
- data/spec/integration/yaml_spec.rb +87 -0
- data/spec/shared/flags_shared_spec.rb +37 -0
- data/spec/shared/identity_function_group.rb +5 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/unit/bcrypt_hash_spec.rb +155 -0
- data/spec/unit/csv_spec.rb +142 -0
- data/spec/unit/dirty_minder_spec.rb +65 -0
- data/spec/unit/enum_spec.rb +126 -0
- data/spec/unit/epoch_time_spec.rb +74 -0
- data/spec/unit/file_path_spec.rb +75 -0
- data/spec/unit/flag_spec.rb +114 -0
- data/spec/unit/ip_address_spec.rb +109 -0
- data/spec/unit/json_spec.rb +126 -0
- data/spec/unit/paranoid_boolean_spec.rb +149 -0
- data/spec/unit/paranoid_datetime_spec.rb +153 -0
- data/spec/unit/regexp_spec.rb +63 -0
- data/spec/unit/uri_spec.rb +83 -0
- data/spec/unit/yaml_spec.rb +111 -0
- data/tasks/spec.rake +21 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +229 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
|
3
|
+
require 'digest/sha1'
|
4
|
+
|
5
|
+
module DataMapper
|
6
|
+
class Property
|
7
|
+
class APIKey < String
|
8
|
+
|
9
|
+
# The amount of random seed data to use to generate tha API Key
|
10
|
+
PADDING = 256
|
11
|
+
|
12
|
+
length 40
|
13
|
+
unique true
|
14
|
+
default proc { APIKey.generate }
|
15
|
+
|
16
|
+
#
|
17
|
+
# Generates a new API Key.
|
18
|
+
#
|
19
|
+
# @return [String]
|
20
|
+
# The new API Key.
|
21
|
+
#
|
22
|
+
def self.generate
|
23
|
+
sha1 = Digest::SHA1.new
|
24
|
+
|
25
|
+
PADDING.times { sha1 << rand(256).chr }
|
26
|
+
sha1.hexdigest
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'bcrypt'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
class Property
|
6
|
+
class BCryptHash < String
|
7
|
+
load_as BCrypt::Password
|
8
|
+
|
9
|
+
length 60
|
10
|
+
|
11
|
+
def load(value)
|
12
|
+
typecast(value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def dump(value)
|
16
|
+
hash = typecast(value)
|
17
|
+
return if hash.nil?
|
18
|
+
|
19
|
+
hash_string = hash.to_s
|
20
|
+
hash_string.encode!('UTF-8') if hash_string.respond_to?(:encode!)
|
21
|
+
hash_string
|
22
|
+
end
|
23
|
+
|
24
|
+
def typecast(value)
|
25
|
+
return value if value.nil? || value.kind_of?(BCrypt::Password)
|
26
|
+
|
27
|
+
BCrypt::Password.new(value)
|
28
|
+
rescue BCrypt::Errors::InvalidHash
|
29
|
+
BCrypt::Password.create(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-types/yaml'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
class Property
|
6
|
+
class CommaSeparatedList < Yaml
|
7
|
+
|
8
|
+
def dump(value)
|
9
|
+
if value.nil?
|
10
|
+
nil
|
11
|
+
elsif value.is_a?(::Array)
|
12
|
+
super(value)
|
13
|
+
elsif value.is_a?(::String)
|
14
|
+
v = []
|
15
|
+
|
16
|
+
value.split(',').each do |element|
|
17
|
+
element.strip!
|
18
|
+
v << element unless element.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
super(v)
|
22
|
+
else
|
23
|
+
raise ArgumentError, "+value+ of CommaSeparatedList must be a string, an array or nil, but given #{value.inspect}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/dm-types/csv.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-types/support/dirty_minder'
|
3
|
+
|
4
|
+
if RUBY_VERSION >= '1.9.0'
|
5
|
+
require 'csv'
|
6
|
+
else
|
7
|
+
require 'fastercsv' # must be ~>1.5
|
8
|
+
CSV = FasterCSV unless defined?(CSV)
|
9
|
+
end
|
10
|
+
|
11
|
+
module DataMapper
|
12
|
+
class Property
|
13
|
+
class Csv < String
|
14
|
+
load_as ::Array
|
15
|
+
|
16
|
+
def load(value)
|
17
|
+
case value
|
18
|
+
when ::String then CSV.parse(value)
|
19
|
+
when ::Array then value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def dump(value)
|
24
|
+
case value
|
25
|
+
when ::Array
|
26
|
+
CSV.generate { |csv| value.each { |row| csv << row } }
|
27
|
+
when ::String then value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
include ::DataMapper::Property::DirtyMinder
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-types/support/flags'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
class Property
|
6
|
+
class Enum < Integer
|
7
|
+
min 1
|
8
|
+
|
9
|
+
include Flags
|
10
|
+
|
11
|
+
load_as ::Object
|
12
|
+
dump_as ::Integer
|
13
|
+
|
14
|
+
def initialize(model, name, options = {})
|
15
|
+
@flag_map = {}
|
16
|
+
|
17
|
+
flags = options.fetch(:flags, self.class.flags)
|
18
|
+
flags.each_with_index do |flag, i|
|
19
|
+
@flag_map[i.succ] = flag
|
20
|
+
end
|
21
|
+
|
22
|
+
if self.class.accepted_options.include?(:set) && !options.include?(:set)
|
23
|
+
options[:set] = @flag_map.values_at(*@flag_map.keys.sort)
|
24
|
+
end
|
25
|
+
|
26
|
+
options[:max] = @flag_map.size
|
27
|
+
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def load(value)
|
32
|
+
flag_map[value]
|
33
|
+
end
|
34
|
+
|
35
|
+
def dump(value)
|
36
|
+
case value
|
37
|
+
when ::Array then value.collect { |v| dump(v) }
|
38
|
+
else flag_map.invert[value]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def typecast(value)
|
43
|
+
return if value.nil?
|
44
|
+
|
45
|
+
# Attempt to typecast using the class of the first item in the map.
|
46
|
+
case flag_map[1]
|
47
|
+
when ::Symbol then value.to_sym
|
48
|
+
when ::String then value.to_s
|
49
|
+
when ::Integer then value.to_i
|
50
|
+
else value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
|
3
|
+
module DataMapper
|
4
|
+
class Property
|
5
|
+
class EpochTime < Time
|
6
|
+
dump_as ::Integer
|
7
|
+
|
8
|
+
def load(value)
|
9
|
+
if value.kind_of?(::Numeric)
|
10
|
+
::Time.at(value.to_i)
|
11
|
+
else
|
12
|
+
value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def dump(value)
|
17
|
+
value&.to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def typecast(value)
|
21
|
+
case value
|
22
|
+
when ::Time then value
|
23
|
+
when ::Numeric, /\A\d+\z/ then ::Time.at(value.to_i)
|
24
|
+
when ::DateTime then datetime_to_time(value)
|
25
|
+
when ::String then ::Time.parse(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private def datetime_to_time(datetime)
|
30
|
+
utc = datetime.new_offset(0)
|
31
|
+
::Time.utc(utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'dm-core'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
class Property
|
6
|
+
class FilePath < String
|
7
|
+
load_as Pathname
|
8
|
+
|
9
|
+
length 255
|
10
|
+
|
11
|
+
def load(value)
|
12
|
+
Pathname.new(value) unless DataMapper::Ext.blank?(value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def dump(value)
|
16
|
+
value.to_s unless DataMapper::Ext.blank?(value)
|
17
|
+
end
|
18
|
+
|
19
|
+
def typecast(value)
|
20
|
+
load(value)
|
21
|
+
end
|
22
|
+
end # class FilePath
|
23
|
+
end # class Property
|
24
|
+
end # module DataMapper
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-types/support/flags'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
class Property
|
6
|
+
class Flag < Object
|
7
|
+
include Flags
|
8
|
+
|
9
|
+
load_as ::Object
|
10
|
+
dump_as ::Integer
|
11
|
+
|
12
|
+
def initialize(model, name, options = {})
|
13
|
+
super
|
14
|
+
|
15
|
+
@flag_map = {}
|
16
|
+
|
17
|
+
flags = options.fetch(:flags, self.class.flags)
|
18
|
+
flags.each_with_index do |flag, i|
|
19
|
+
flag_map[i] = flag
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def load(value)
|
24
|
+
return [] if value.nil? || value <= 0
|
25
|
+
|
26
|
+
begin
|
27
|
+
matches = []
|
28
|
+
|
29
|
+
0.upto(flag_map.size - 1) do |i|
|
30
|
+
matches << flag_map[i] if value[i] == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
matches.compact
|
34
|
+
rescue TypeError, Errno::EDOM
|
35
|
+
[]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def dump(value)
|
40
|
+
unless value.nil?
|
41
|
+
flags = Array(value).map { |flag| flag.to_sym }
|
42
|
+
flags.uniq!
|
43
|
+
|
44
|
+
flag = 0
|
45
|
+
|
46
|
+
flag_map.invert.values_at(*flags).each do |i|
|
47
|
+
next if i.nil?
|
48
|
+
flag += (1 << i)
|
49
|
+
end
|
50
|
+
|
51
|
+
flag
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def typecast(value)
|
56
|
+
case value
|
57
|
+
when nil then nil
|
58
|
+
when ::Array then value.map { |v| v.to_sym }
|
59
|
+
else [value.to_sym]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end # class Flag
|
63
|
+
end # class Property
|
64
|
+
end # module DataMapper
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'dm-core'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
class Property
|
6
|
+
class IPAddress < String
|
7
|
+
load_as IPAddr
|
8
|
+
|
9
|
+
length 39
|
10
|
+
|
11
|
+
def load(value)
|
12
|
+
if value.nil? || value_loaded?(value)
|
13
|
+
value
|
14
|
+
elsif value.is_a?(::String)
|
15
|
+
if value.empty?
|
16
|
+
IPAddr.new("0.0.0.0")
|
17
|
+
else
|
18
|
+
IPAddr.new(value)
|
19
|
+
end
|
20
|
+
else
|
21
|
+
raise ArgumentError.new("+value+ must be nil or a String")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def dump(value)
|
26
|
+
value.to_s unless value.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
def typecast(value)
|
30
|
+
load(value) unless value.nil?
|
31
|
+
end
|
32
|
+
end # class IPAddress
|
33
|
+
end # module Property
|
34
|
+
end # module DataMapper
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-types/support/dirty_minder'
|
3
|
+
require 'multi_json'
|
4
|
+
|
5
|
+
module DataMapper
|
6
|
+
class Property
|
7
|
+
class Json < Text
|
8
|
+
load_as ::Object
|
9
|
+
|
10
|
+
def load(value)
|
11
|
+
if value.nil? || value_loaded?(value)
|
12
|
+
value
|
13
|
+
elsif value.is_a?(::String)
|
14
|
+
typecast(value)
|
15
|
+
else
|
16
|
+
raise ArgumentError.new("+value+ of a property of JSON type must be nil or a String")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def dump(value)
|
21
|
+
if value.nil? || value.is_a?(::String)
|
22
|
+
value
|
23
|
+
else
|
24
|
+
MultiJson.dump(value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def typecast(value)
|
29
|
+
return if value.nil?
|
30
|
+
|
31
|
+
if value_loaded?(value)
|
32
|
+
value
|
33
|
+
else
|
34
|
+
MultiJson.load(value.to_s)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def value_loaded?(value)
|
39
|
+
value.kind_of?(::Array) || value.kind_of?(::Hash)
|
40
|
+
end
|
41
|
+
|
42
|
+
include ::DataMapper::Property::DirtyMinder
|
43
|
+
|
44
|
+
end # class Json
|
45
|
+
|
46
|
+
JSON = Json
|
47
|
+
end # class Property
|
48
|
+
end # module DataMapper
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Types
|
3
|
+
module Paranoid
|
4
|
+
module Base
|
5
|
+
def self.included(model)
|
6
|
+
model.extend ClassMethods
|
7
|
+
model.instance_variable_set(:@paranoid_properties, {})
|
8
|
+
end
|
9
|
+
|
10
|
+
def paranoid_destroy
|
11
|
+
model.paranoid_properties.each do |name, block|
|
12
|
+
attribute_set(name, block.call(self))
|
13
|
+
end
|
14
|
+
save_self
|
15
|
+
self.persistence_state = Resource::PersistenceState::Immutable.new(self)
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
def _destroy(execute_hooks = true)
|
23
|
+
return false unless saved?
|
24
|
+
|
25
|
+
if execute_hooks
|
26
|
+
paranoid_destroy
|
27
|
+
else
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
def inherited(model)
|
35
|
+
model.instance_variable_set(:@paranoid_properties, @paranoid_properties.dup)
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
# @api public
|
40
|
+
def with_deleted
|
41
|
+
with_exclusive_scope({}) { block_given? ? yield : all }
|
42
|
+
end
|
43
|
+
|
44
|
+
# @api private
|
45
|
+
def paranoid_properties
|
46
|
+
@paranoid_properties
|
47
|
+
end
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
def set_paranoid_property(name, &block)
|
51
|
+
paranoid_properties[name] = block
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'dm-types/paranoid/base'
|
2
|
+
|
3
|
+
module DataMapper
|
4
|
+
class Property
|
5
|
+
class ParanoidBoolean < Boolean
|
6
|
+
default false
|
7
|
+
lazy true
|
8
|
+
|
9
|
+
# @api private
|
10
|
+
def bind
|
11
|
+
property_name = name.inspect
|
12
|
+
|
13
|
+
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
14
|
+
include DataMapper::Types::Paranoid::Base
|
15
|
+
|
16
|
+
set_paranoid_property(#{property_name}) { true }
|
17
|
+
|
18
|
+
default_scope(#{repository_name.inspect}).update(#{property_name} => false)
|
19
|
+
RUBY
|
20
|
+
end
|
21
|
+
end # class ParanoidBoolean
|
22
|
+
end # module Property
|
23
|
+
end # module DataMapper
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'dm-types/paranoid/base'
|
2
|
+
|
3
|
+
module DataMapper
|
4
|
+
class Property
|
5
|
+
class ParanoidDateTime < DateTime
|
6
|
+
lazy true
|
7
|
+
|
8
|
+
# @api private
|
9
|
+
def bind
|
10
|
+
property_name = name.inspect
|
11
|
+
|
12
|
+
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
13
|
+
include DataMapper::Types::Paranoid::Base
|
14
|
+
|
15
|
+
set_paranoid_property(#{property_name}) { ::DateTime.now }
|
16
|
+
|
17
|
+
default_scope(#{repository_name.inspect}).update(#{property_name} => nil)
|
18
|
+
RUBY
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
|
3
|
+
module DataMapper
|
4
|
+
class Property
|
5
|
+
class Regexp < String
|
6
|
+
load_as ::Regexp
|
7
|
+
|
8
|
+
def load(value)
|
9
|
+
::Regexp.new(value) unless value.nil?
|
10
|
+
end
|
11
|
+
|
12
|
+
def dump(value)
|
13
|
+
value.source unless value.nil?
|
14
|
+
end
|
15
|
+
|
16
|
+
def typecast(value)
|
17
|
+
load(value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'stringex'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
class Property
|
6
|
+
class Slug < String
|
7
|
+
|
8
|
+
# Maximum length chosen because URI type is limited to 2000
|
9
|
+
# characters, and a slug is a component of a URI, so it should
|
10
|
+
# not exceed the maximum URI length either.
|
11
|
+
length 2000
|
12
|
+
|
13
|
+
def typecast(value)
|
14
|
+
if value.nil?
|
15
|
+
nil
|
16
|
+
elsif value.respond_to?(:to_str)
|
17
|
+
escape(value.to_str)
|
18
|
+
else
|
19
|
+
raise ArgumentError, '+value+ must be nil or respond to #to_str'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def escape(string)
|
24
|
+
string.to_url
|
25
|
+
end
|
26
|
+
end # class Slug
|
27
|
+
end # class Property
|
28
|
+
end # module DataMapper
|