ohm-contrib 0.1.2 → 1.0.rc0

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.
Files changed (52) hide show
  1. data/lib/ohm/{contrib/callbacks.rb → callbacks.rb} +20 -65
  2. data/lib/ohm/contrib.rb +9 -17
  3. data/lib/ohm/datatypes.rb +38 -0
  4. data/lib/ohm/{contrib/locking.rb → locking.rb} +1 -2
  5. data/lib/ohm/plugin.rb +54 -0
  6. data/lib/ohm/{contrib/scope.rb → scope.rb} +7 -6
  7. data/lib/ohm/{contrib/slug.rb → slug.rb} +11 -6
  8. data/lib/ohm/{contrib/soft_delete.rb → softdelete.rb} +23 -19
  9. data/lib/ohm/timestamping.rb +41 -0
  10. data/ohm-contrib.gemspec +30 -0
  11. data/rakefile +6 -0
  12. data/test/{instance_callbacks_test.rb → instance_callbacks.rb} +2 -6
  13. data/test/{macro_callbacks_test.rb → macro_callbacks.rb} +1 -9
  14. data/test/plugin.rb +212 -0
  15. data/test/{scope_test.rb → scope.rb} +2 -3
  16. data/test/slug.rb +24 -0
  17. data/test/{soft_delete_test.rb → soft_delete.rb} +13 -3
  18. data/test/{timestamping_test.rb → timestamping.rb} +5 -6
  19. metadata +33 -70
  20. data/Rakefile +0 -8
  21. data/lib/ohm/contrib/active_model_extension.rb +0 -87
  22. data/lib/ohm/contrib/boundaries.rb +0 -41
  23. data/lib/ohm/contrib/date_validations.rb +0 -23
  24. data/lib/ohm/contrib/extra_validations.rb +0 -48
  25. data/lib/ohm/contrib/fulltext_searching.rb +0 -80
  26. data/lib/ohm/contrib/length_validations.rb +0 -32
  27. data/lib/ohm/contrib/number_validations.rb +0 -14
  28. data/lib/ohm/contrib/timestamping.rb +0 -38
  29. data/lib/ohm/contrib/typecast.rb +0 -350
  30. data/lib/ohm/contrib/web_validations.rb +0 -52
  31. data/test/activemodel_test.rb +0 -27
  32. data/test/boundaries_test.rb +0 -45
  33. data/test/callbacks_lint.rb +0 -141
  34. data/test/date_validations_test.rb +0 -29
  35. data/test/fixtures/ascii8bit.txt +0 -1
  36. data/test/fulltext_searching_test.rb +0 -63
  37. data/test/length_validations_test.rb +0 -31
  38. data/test/membership_validation_test.rb +0 -31
  39. data/test/number_validations_test.rb +0 -37
  40. data/test/slug_test.rb +0 -30
  41. data/test/typecast_array_test.rb +0 -146
  42. data/test/typecast_boolean_test.rb +0 -43
  43. data/test/typecast_date_test.rb +0 -82
  44. data/test/typecast_decimal_test.rb +0 -82
  45. data/test/typecast_existing_attribute_test.rb +0 -22
  46. data/test/typecast_float_test.rb +0 -57
  47. data/test/typecast_hash_test.rb +0 -120
  48. data/test/typecast_integer_test.rb +0 -71
  49. data/test/typecast_string_test.rb +0 -43
  50. data/test/typecast_time_test.rb +0 -72
  51. data/test/typecast_timezone_test.rb +0 -37
  52. data/test/web_validations_test.rb +0 -79
@@ -1,80 +0,0 @@
1
- begin
2
- require "text"
3
- rescue LoadError
4
- raise LoadError,
5
- "Type `[sudo] gem install text` to use Ohm::FulltextSearching."
6
- end
7
-
8
- module Ohm
9
- module FulltextSearching
10
- def self.included(model)
11
- model.extend ClassMethods
12
- end
13
-
14
- module ClassMethods
15
- def search(hash)
16
- find(hash_to_metaphones(hash))
17
- end
18
-
19
- def hash_to_metaphones(hash)
20
- ret = Hash.new { |h, k| h[k] = [] }
21
-
22
- hash.each do |att, string|
23
- metaphones(string).each { |m| ret[:"fulltext_#{att}"] << m }
24
- end
25
-
26
- return ret
27
- end
28
-
29
- def double_metaphone(str)
30
- return [] if STOPWORDS.include?(str.to_s.downcase)
31
-
32
- Text::Metaphone.double_metaphone(str).compact
33
- end
34
-
35
- def metaphones(str)
36
- str.to_s.strip.split(/\s+/).map { |s| double_metaphone(s) }.flatten
37
- end
38
-
39
- def fulltext(att)
40
- field = :"fulltext_#{att}"
41
-
42
- define_method(field) { self.class.metaphones(send(att)) }
43
- index(field)
44
- end
45
- end
46
-
47
- STOPWORDS = %w{a about above according across actually adj after
48
- afterwards again against all almost alone along already also although
49
- always among amongst an and another any anyhow anyone anything anywhere
50
- are aren't around as at b be became because become becomes becoming
51
- been before beforehand begin behind being below beside besides between
52
- beyond both but by c can can't cannot caption co co. could couldn't d
53
- did didn't do does doesn't don't down during e each eg eight eighty
54
- either else elsewhere end ending enough etc even ever every everyone
55
- everything everywhere except f few first for found from further g h
56
- had has hasn't have haven't he he'd he'll he's hence her here here's
57
- hereafter hereby herein hereupon hers herself him himself his how
58
- however hundred i i'd i'll i'm i've ie if in inc. indeed instead into is
59
- isn't it it's its itself j k l last later latter latterly least less let
60
- let's like likely ltd m made make makes many maybe me meantime meanwhile
61
- might miss more moreover most mostly mr mrs much must my myself n namely
62
- neither never nevertheless next nine ninety no nobody none nonetheless
63
- noone nor not nothing now nowhere o of off often on once one one's only
64
- onto or other others otherwise our ours ourselves out over overall own
65
- p per perhaps q r rather recent recently s same seem seemed seeming
66
- seems seven several she she'd she'll she's should shouldn't since so
67
- some somehow someone something sometime sometimes somewhere still such
68
- t taking than that that'll that's that've the their them themselves then
69
- thence there there'd there'll there're there's there've thereafter thereby
70
- therefore therein thereupon these they they'd they'll they're they've
71
- thirty this those though three through throughout thru thus to together
72
- too toward towards u under unless unlike unlikely until up upon us used
73
- using v very via w was wasn't we we'd we'll we're we've well were weren't
74
- what what'll what's what've whatever when whence whenever where where's
75
- whereafter whereas whereby wherein whereupon wherever whether which while
76
- whither who who'd who'll who's whoever whole whom whomever whose why will
77
- with within without won't would wouldn't x y yes yet you you'd you'll
78
- you're you've your yours yourself yourselves z}
79
- end
80
- end
@@ -1,32 +0,0 @@
1
- module Ohm
2
- module LengthValidations
3
-
4
- def assert_min_length(att, length, error = [att, :too_short])
5
- if assert_present(att, error)
6
- m = send(att).to_s
7
- assert is_long_enough?(m, length), error
8
- end
9
- end
10
-
11
- def assert_max_length(att, length, error = [att, :too_long])
12
- if assert_present(att, error)
13
- m = send(att).to_s
14
- assert is_too_long?(m, length), error
15
- end
16
- end
17
-
18
- private
19
- def is_too_long?(string, length)
20
- string.size <= length
21
- rescue ArgumentError
22
- return false
23
- end
24
-
25
- def is_long_enough?(string, length)
26
- string.size >= length
27
- rescue ArgumentError
28
- return false
29
- end
30
- end
31
- end
32
-
@@ -1,14 +0,0 @@
1
- module Ohm
2
- # This module will include all numeric validation needs.
3
- # As of VERSION 0.0.27, Ohm::NumberValidations#assert_decimal
4
- # is the only method provided.
5
- module NumberValidations
6
- DECIMAL_REGEX = /^(\d+)?(\.\d+)?$/
7
-
8
- protected
9
- def assert_decimal(att, error = [att, :not_decimal])
10
- assert_format att, DECIMAL_REGEX, error
11
- end
12
- end
13
- end
14
-
@@ -1,38 +0,0 @@
1
- module Ohm
2
- # Provides created_at / updated_at timestamps.
3
- #
4
- # @example
5
- #
6
- # class Post < Ohm::Model
7
- # include Ohm::Timestamping
8
- # end
9
- #
10
- # post = Post.create
11
- # post.created_at.to_s == Time.now.utc.to_s
12
- # # => true
13
- #
14
- # post = Post[post.id]
15
- # post.save
16
- # post.updated_at.to_s == Time.now.utc.to_s
17
- # # => true
18
- module Timestamping
19
- def self.included(base)
20
- base.attribute :created_at
21
- base.attribute :updated_at
22
- end
23
-
24
- def create
25
- self.created_at ||= Time.now.utc.to_s
26
-
27
- super
28
- end
29
-
30
- protected
31
- def write
32
- self.updated_at = Time.now.utc.to_s
33
-
34
- super
35
- end
36
- end
37
- end
38
-
@@ -1,350 +0,0 @@
1
- require 'bigdecimal'
2
- require 'time'
3
- require 'date'
4
- require 'forwardable'
5
- require "json"
6
-
7
- module Ohm
8
- # Provides all the primitive types. The following are included:
9
- #
10
- # * String
11
- # * Decimal
12
- # * Integer
13
- # * Float
14
- # * Date
15
- # * Time
16
- # * Hash
17
- # * Array
18
- # * Boolean
19
- module Types
20
- def self.defined?(type)
21
- @_constants ||= constants.map { |c| c.to_sym }
22
- @_constants.include?(type.to_sym)
23
- end
24
-
25
- def self.[](type)
26
- const_get(type.to_s.split('::').last)
27
- end
28
-
29
- class Base < BasicObject
30
- class Exception < ::Exception; end
31
-
32
- extend ::Forwardable
33
-
34
- @@delegation_blacklist = [
35
- :==, :to_s, :initialize, :inspect, :object_id, :__send__, :__id__,
36
- :respond_to?
37
- ]
38
-
39
- def self.[](value)
40
- return empty if value.to_s.empty?
41
-
42
- new(value)
43
- end
44
-
45
- def self.empty
46
- defined?(self::RAW) ? self::RAW.new : nil
47
- end
48
-
49
- def self.delegate_to(klass, except = @@delegation_blacklist)
50
- methods = klass.public_instance_methods.map { |e| e.to_sym } - except
51
- def_delegators :object, *methods
52
- end
53
-
54
- def inspect
55
- @raw.inspect
56
- end
57
- end
58
-
59
- class Primitive < Base
60
- def initialize(value)
61
- @raw = value
62
- end
63
-
64
- def to_s
65
- @raw.to_s
66
- end
67
-
68
- def ==(other)
69
- to_s == other.to_s
70
- end
71
-
72
- def respond_to?(method)
73
- object.respond_to?(method)
74
- rescue ::ArgumentError
75
- @raw.respond_to?(method)
76
- end
77
-
78
- def object
79
- @raw
80
- end
81
- end
82
-
83
- class String < Primitive
84
- delegate_to ::String
85
-
86
- def type
87
- ::String
88
- end
89
- end
90
-
91
- class Decimal < Primitive
92
- delegate_to ::BigDecimal
93
-
94
- def object
95
- ::Kernel::BigDecimal(@raw)
96
- end
97
-
98
- def type
99
- ::BigDecimal
100
- end
101
- end
102
-
103
- class Integer < Primitive
104
- delegate_to ::Fixnum
105
-
106
- def object
107
- ::Kernel::Integer(@raw)
108
- end
109
-
110
- def type
111
- ::Fixnum
112
- end
113
- end
114
-
115
- class Float < Primitive
116
- delegate_to ::Float
117
-
118
- def object
119
- ::Kernel::Float(@raw)
120
- end
121
-
122
- def type
123
- ::Float
124
- end
125
- end
126
-
127
- class Time < Primitive
128
- delegate_to ::Time
129
-
130
- def object
131
- ::Time.parse(@raw).utc
132
- end
133
-
134
- def type
135
- ::Time
136
- end
137
- end
138
-
139
- class Date < Primitive
140
- delegate_to ::Date
141
-
142
- def object
143
- ::Date.parse(@raw)
144
- end
145
-
146
- def type
147
- ::Date
148
- end
149
- end
150
-
151
- class Boolean
152
- def self.[](value)
153
- case value
154
- when 'false', false, '0', 0 then false
155
- when 'true', true, '1', 1 then true
156
- end
157
- end
158
- end
159
-
160
- class Serialized < Base
161
- attr :object
162
-
163
- def initialize(raw)
164
- @object = case raw
165
- when self.class::RAW
166
- raw
167
- when ::String
168
- begin
169
- ::JSON.parse(raw)
170
- rescue ::JSON::ParserError
171
- raw
172
- end
173
- when self.class
174
- raw.object
175
- else
176
- ::Kernel.raise ::TypeError,
177
- "%s does not accept %s" % [self.class, raw.inspect]
178
- end
179
- end
180
-
181
- def ==(other)
182
- object == other
183
- end
184
-
185
- def to_s
186
- object.to_json
187
- end
188
- alias :inspect :to_s
189
-
190
- def respond_to?(method)
191
- object.respond_to?(method)
192
- end
193
-
194
- def type
195
- self.class::RAW
196
- end
197
- end
198
-
199
- class Hash < Serialized
200
- RAW = ::Hash
201
-
202
- delegate_to ::Hash
203
-
204
- # @private since basic object doesn't include a #class we need
205
- # to define this manually
206
- def class
207
- ::Ohm::Types::Hash
208
- end
209
- end
210
-
211
- class Array < Serialized
212
- RAW = ::Array
213
-
214
- delegate_to ::Array
215
-
216
- # @private since basic object doesn't include a #class we need
217
- # to define this manually
218
- def class
219
- ::Ohm::Types::Array
220
- end
221
- end
222
- end
223
-
224
- # Provides unobtrusive, non-explosive typecasting. Instead of exploding on
225
- # set of an invalid value, this module takes the approach of just taking in
226
- # parameters and letting you do validation yourself. The only thing this
227
- # module does for you is the boilerplate casting you might need to do.
228
- #
229
- # @example
230
- #
231
- # # without typecasting
232
- # class Item < Ohm::Model
233
- # attribute :price
234
- # attribute :posted
235
- # end
236
- #
237
- # item = Item.create(:price => 299, :posted => Time.now.utc)
238
- # item = Item[item.id]
239
- #
240
- # # now when you try and grab `item.price`, its a string.
241
- # "299" == item.price
242
- # # => true
243
- #
244
- # # you can opt to manually cast everytime, or do it in the model, i.e.
245
- #
246
- # class Item
247
- # def price
248
- # BigDecimal(read_local(:price))
249
- # end
250
- # end
251
- #
252
- # The Typecasted way
253
- # ------------------
254
- #
255
- # class Item < Ohm::Model
256
- # include Ohm::Typecast
257
- #
258
- # attribute :price, Decimal
259
- # attribute :posted, Time
260
- # end
261
- #
262
- # item = Item.create(:price => "299", :posted => Time.now.utc)
263
- # item = Item[item.id]
264
- # item.price.class == BigDecimal
265
- # # => true
266
- #
267
- # item.price.to_s == "299"
268
- # # => true
269
- #
270
- # item.price * 2 == 598
271
- # # => true
272
- #
273
- # item.posted.strftime('%m/%d/%Y')
274
- # # => works!!!
275
- module Typecast
276
- def self.included(base)
277
- base.extend ClassMethods
278
- end
279
-
280
- module ClassMethods
281
- # Defines a typecasted attribute.
282
- #
283
- # @example
284
- #
285
- # class User < Ohm::Model
286
- # include Ohm::Typecast
287
- #
288
- # attribute :birthday, Date
289
- # attribute :last_login, Time
290
- # attribute :age, Integer
291
- # attribute :spending, Decimal
292
- # attribute :score, Float
293
- # end
294
- #
295
- # user = User.new(:birthday => "2000-01-01")
296
- # user.birthday.month == 1
297
- # # => true
298
- #
299
- # user.birthday.year == 2000
300
- # # => true
301
- #
302
- # user.birthday.day == 1
303
- # # => true
304
- #
305
- # user = User.new(:age => 20)
306
- # user.age - 1 == 19
307
- # => true
308
- #
309
- # @param [Symbol] name the name of the attribute to define.
310
- # @param [Class] type (defaults to Ohm::Types::String) a class defined in
311
- # Ohm::Types. You may define custom types in Ohm::Types if
312
- # you need to.
313
- # @return [Array] the array of attributes already defined.
314
- # @return [nil] if the attribute is already defined.
315
- def attribute(name, type = Ohm::Types::String, klass = Ohm::Types[type])
316
- # Primitive types maintain a reference to the original object
317
- # stored in @_attributes[att]. Hence mutation works for the
318
- # Primitive case. For cases like Hash, Array where the value
319
- # is `JSON.parse`d, we need to set the actual Ohm::Types::Hash
320
- # (or similar) to @_attributes[att] for mutation to work.
321
- if klass.superclass == Ohm::Types::Primitive
322
- define_method(name) { klass[read_local(name)] }
323
- else
324
- define_method(name) { write_local(name, klass[read_local(name)]) }
325
- end
326
-
327
- define_method(:"#{name}=") do |value|
328
- write_local(name, klass[value].to_s)
329
- end
330
-
331
- attributes << name unless attributes.include?(name)
332
- types[name] = type
333
- end
334
- alias :typecast :attribute
335
-
336
- def types
337
- @types ||= {}
338
- end
339
-
340
- private
341
- def const_missing(name)
342
- if Ohm::Types.defined?(name)
343
- Ohm::Types[name]
344
- else
345
- super
346
- end
347
- end
348
- end
349
- end
350
- end
@@ -1,52 +0,0 @@
1
- module Ohm
2
- # All credit goes to gnrfan of github
3
- # Basically an extraction from http://github.com/gnrfan/ohm_extra_validations
4
- #
5
- # * 2010-05-29 Updated Email Regex, Extracted out regexs to constants
6
- #
7
- # This module provides the following:
8
- # * assert_slug
9
- # * assert_email
10
- # * assert_url
11
- # * assert_ipv4
12
- module WebValidations
13
- # @see http://fightingforalostcause.net/misc/2006/compare-email-regex.php
14
- EMAIL_REGEX = /^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i
15
-
16
- SLUG_REGEX = /^[-\w]+$/
17
-
18
- URL_REGEX = /^(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}|(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}|localhost)(:[0-9]{1,5})?(\/.*)?$/ix
19
-
20
- IPV4_REGEX = /^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$/
21
-
22
- protected
23
- def assert_slug(att, error = [att, :not_slug])
24
- if assert_present(att, error) and assert_unique(att)
25
- assert_format(att, SLUG_REGEX, error)
26
- end
27
- end
28
-
29
- def assert_email(att, error = [att, :not_email])
30
- if assert_present(att, error)
31
- assert_format(att, EMAIL_REGEX, error)
32
- end
33
- end
34
-
35
- def assert_url(att, error = [att, :not_url])
36
- if assert_present(att, error)
37
- assert_format(att, URL_REGEX, error)
38
- end
39
- end
40
-
41
- def assert_ipv4(att, error = [att, :not_ipv4])
42
- if assert_present(att, error)
43
- assert_format(att, IPV4_REGEX, error)
44
- end
45
- end
46
-
47
- def assert_ipaddr(att, error = [att, :not_ipaddr])
48
- assert_ipv4(att, error)
49
- end
50
- end
51
- end
52
-
@@ -1,27 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("./helper", File.dirname(__FILE__))
4
-
5
- require "test/unit"
6
- require "active_model"
7
-
8
- class ActiveModelTest < Test::Unit::TestCase
9
- include ActiveModel::Lint::Tests
10
-
11
- class Post < Ohm::Model
12
- include Ohm::ActiveModelExtension
13
-
14
- attribute :body
15
- list :related, Post
16
-
17
- def validate
18
- assert_present :body
19
- end
20
- end
21
-
22
- def setup
23
- @model = Post.new
24
- end
25
- end
26
-
27
-
@@ -1,45 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("./helper", File.dirname(__FILE__))
4
-
5
- class Person < Ohm::Model
6
- include Ohm::Boundaries
7
-
8
- attribute :name
9
- index :name
10
- end
11
-
12
- test "first / last are nil when no records" do
13
- assert nil == Person.first
14
- assert nil == Person.last
15
- end
16
-
17
- test "first / last returns the only record when just 1 record" do
18
- matz = Person.create(:name => "matz")
19
-
20
- assert matz == Person.first
21
- assert matz == Person.last
22
- end
23
-
24
- test "has chronological order by default" do
25
- matz = Person.create(:name => "matz")
26
- linus = Person.create(:name => "linus")
27
-
28
- assert matz == Person.first
29
- assert linus == Person.last
30
- end
31
-
32
- test "respects filters passed in" do
33
- matz = Person.create(:name => "matz")
34
- linus = Person.create(:name => "linus")
35
-
36
- assert matz == Person.first(:name => "matz")
37
- assert matz == Person.last(:name => "matz")
38
-
39
- assert linus == Person.first(:name => "linus")
40
- assert linus == Person.last(:name => "linus")
41
-
42
- assert nil == Person.first(:name => "quentin")
43
- assert nil == Person.last(:name => "quentin")
44
- end
45
-