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
@@ -4,8 +4,6 @@ module Ohm
4
4
  # You can implement callbacks by overriding any of the following
5
5
  # methods:
6
6
  #
7
- # - before_validate
8
- # - after_validate
9
7
  # - before_create
10
8
  # - after_create
11
9
  # - before_save
@@ -22,8 +20,6 @@ module Ohm
22
20
  # class Post < Ohm::Model
23
21
  # include Ohm::Callbacks
24
22
  #
25
- # before :validate, :clean_decimals
26
- #
27
23
  # before :create, :timestamp!
28
24
  # before :save, :recalc_votes
29
25
  #
@@ -53,11 +49,11 @@ module Ohm
53
49
  # end
54
50
 
55
51
  module Callbacks
56
- def self.included(base)
57
- base.extend Macros
52
+ def self.included(model)
53
+ model.extend ClassMethods
58
54
  end
59
55
 
60
- module Macros
56
+ module ClassMethods
61
57
  # Use to add a before callback on `method`. Only symbols
62
58
  # are allowed, no string eval, no block option also.
63
59
  #
@@ -79,12 +75,12 @@ module Ohm
79
75
  # end
80
76
  # end
81
77
  #
82
- # @param [Symbol] method the method type, `:validate`, `:create`, or `:save`
78
+ # @param [Symbol] method the method type, e.g. `:create`, or `:save`
83
79
  # @param [Symbol] callback the name of the method to execute
84
80
  # @return [Array] the callback in an array if added e.g. [:timestamp]
85
81
  # @return [nil] if the callback already exists
86
82
  def before(method, callback)
87
- unless callbacks[:before][method].include? callback
83
+ unless callbacks[:before][method].include?(callback)
88
84
  callbacks[:before][method] << callback
89
85
  end
90
86
  end
@@ -115,7 +111,7 @@ module Ohm
115
111
  # @return [Array] the callback in an array if added e.g. [:timestamp]
116
112
  # @return [nil] if the callback already exists
117
113
  def after(method, callback)
118
- unless callbacks[:after][method].include? callback
114
+ unless callbacks[:after][method].include?(callback)
119
115
  callbacks[:after][method] << callback
120
116
  end
121
117
  end
@@ -126,76 +122,35 @@ module Ohm
126
122
  end
127
123
  end
128
124
 
129
- # Overrides the validate method of Ohm::Model. This is a bit tricky,
130
- # since typically you override this. Make sure you do something like:
131
- #
132
- # def validate
133
- # super
134
- #
135
- # # do your assertions
136
- # end
137
- #
138
- # This ensures that you call this method when you defined your own validate
139
- # method.
140
- #
141
- # In all honesty, I don't see the value of putting this here, and I'm still
142
- # weighing if this is _really_ needed.
143
- def validate
144
- execute_callback(:before, :validate)
145
- super
146
- execute_callback(:after, :validate)
147
- end
148
-
149
125
  # The overriden create of Ohm::Model. It checks if the
150
126
  # model is valid, and executes all before :create callbacks.
151
127
  #
152
128
  # If the create succeeds, all after :create callbacks are
153
129
  # executed.
154
- def create
155
- return unless valid?
130
+ def write
131
+ creating = @creating
156
132
 
157
- execute_callback(:before, :create)
133
+ execute_callback(:before, :create) if creating
134
+ execute_callback(:before, :update) unless creating
158
135
  execute_callback(:before, :save)
159
136
 
160
- initialize_id
161
-
162
- mutex do
163
- create_model_membership
164
- write
165
- add_to_indices
137
+ super
166
138
 
167
- execute_callback(:after, :create)
168
- execute_callback(:after, :save)
169
- end
139
+ execute_callback(:after, :create) if creating
140
+ execute_callback(:after, :update) unless creating
141
+ execute_callback(:after, :save)
170
142
  end
171
143
 
172
- # The overridden save of Ohm::Model. It checks if the model
173
- # is valid, and executes all before :save callbacks.
174
- #
175
- # If the save also succeeds, all after :save callbacks are
176
- # executed.
177
- def save
178
- return create if new?
179
- return unless valid?
180
-
181
- execute_callback(:before, :save)
182
- execute_callback(:before, :update)
183
-
184
- mutex do
185
- write
186
- update_indices
187
-
188
- execute_callback(:after, :save)
189
- execute_callback(:after, :update)
190
- end
144
+ def create
145
+ @creating = true
146
+ super
147
+ @creating = false
191
148
  end
192
149
 
193
150
  def delete
194
151
  execute_callback(:before, :delete)
195
-
196
- super.tap do |is_deleted|
197
- execute_callback(:after, :delete) if is_deleted
198
- end
152
+ super
153
+ execute_callback(:after, :delete)
199
154
  end
200
155
 
201
156
  protected
data/lib/ohm/contrib.rb CHANGED
@@ -1,21 +1,13 @@
1
+ require_relative "callbacks"
2
+ require_relative "datatypes"
3
+ require_relative "locking"
4
+ require_relative "scope"
5
+ require_relative "slug"
6
+ require_relative "softdelete"
7
+ require_relative "timestamping"
8
+
1
9
  module Ohm
2
10
  module Contrib
3
- VERSION = "0.1.2"
11
+ VERSION = "1.0.rc0"
4
12
  end
5
-
6
- autoload :ActiveModelExtension, "ohm/contrib/active_model_extension"
7
- autoload :Boundaries, "ohm/contrib/boundaries"
8
- autoload :Timestamping, "ohm/contrib/timestamping"
9
- autoload :LengthValidations, "ohm/contrib/length_validations"
10
- autoload :WebValidations, "ohm/contrib/web_validations"
11
- autoload :NumberValidations, "ohm/contrib/number_validations"
12
- autoload :DateValidations, "ohm/contrib/date_validations"
13
- autoload :ExtraValidations, "ohm/contrib/extra_validations"
14
- autoload :Typecast, "ohm/contrib/typecast"
15
- autoload :Locking, "ohm/contrib/locking"
16
- autoload :Callbacks, "ohm/contrib/callbacks"
17
- autoload :Slug, "ohm/contrib/slug"
18
- autoload :Scope, "ohm/contrib/scope"
19
- autoload :SoftDelete, "ohm/contrib/soft_delete"
20
- autoload :FulltextSearching, "ohm/contrib/fulltext_searching"
21
13
  end
@@ -0,0 +1,38 @@
1
+ require "bigdecimal"
2
+ require "date"
3
+ require "json"
4
+ require "time"
5
+
6
+ module Ohm
7
+ module DataTypes
8
+ module Type
9
+ Integer = lambda { |x| x.to_i }
10
+ Decimal = lambda { |x| x && BigDecimal(x.to_s) }
11
+ Float = lambda { |x| x.to_f }
12
+ Boolean = lambda { |x| !!x }
13
+ Time = lambda { |t| t && (t.kind_of?(::Time) ? t : ::Time.parse(t)) }
14
+ Date = lambda { |d| d && (d.kind_of?(::Date) ? d : ::Date.parse(d)) }
15
+ Timestamp = lambda { |t| t && UnixTime.at(t.to_i) }
16
+ Hash = lambda { |h| h && (h.kind_of?(::Hash) ? SerializedHash[h] : JSON(h)) }
17
+ Array = lambda { |a| a && (a.kind_of?(::Array) ? SerializedArray.new(a) : JSON(a)) }
18
+ end
19
+
20
+ class UnixTime < Time
21
+ def to_s
22
+ to_i.to_s
23
+ end
24
+ end
25
+
26
+ class SerializedHash < Hash
27
+ def to_s
28
+ JSON.dump(self)
29
+ end
30
+ end
31
+
32
+ class SerializedArray < Array
33
+ def to_s
34
+ JSON.dump(self)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -52,5 +52,4 @@ module Ohm
52
52
  lock.to_f < Time.now.to_f
53
53
  end
54
54
  end
55
- end
56
-
55
+ end
data/lib/ohm/plugin.rb ADDED
@@ -0,0 +1,54 @@
1
+ module Ohm
2
+ module Plugin
3
+ def plugin(name)
4
+ mixin = Plugin[name]
5
+
6
+ include mixin
7
+ extend mixin::ClassMethods if mixin.const_defined?(:ClassMethods, false)
8
+
9
+ mixin.setup(self) if mixin.respond_to?(:setup)
10
+ end
11
+
12
+ def self.[](plugin)
13
+ case plugin
14
+ when Module
15
+ plugin
16
+ when Symbol
17
+ name, path = Plugin.registry[plugin]
18
+
19
+ raise Unknown.new(plugin) if name.nil?
20
+
21
+ require path
22
+ Ohm.const_get(name, false)
23
+ else
24
+ raise Unknown.new(plugin)
25
+ end
26
+ end
27
+
28
+ def self.register(name, name_and_path)
29
+ registry[name] = name_and_path
30
+ end
31
+
32
+ def self.registry
33
+ @registry ||= {
34
+ :callbacks => [:Callbacks, "ohm/contrib/callbacks"],
35
+ :datatypes => [:DataTypes, "ohm/contrib/data_types"],
36
+ :locking => [:Locking, "ohm/contrib/locking"],
37
+ :scope => [:Scope, "ohm/contrib/scope"],
38
+ :slug => [:Slug, "ohm/contrib/slug"],
39
+ :softdelete => [:SoftDelete, "ohm/contrib/soft_delete"],
40
+ :timestamping => [:Timestamping, "ohm/contrib/timestamping"]
41
+ }
42
+ end
43
+
44
+ class Unknown < StandardError
45
+ def initialize(plugin)
46
+ @plugin = plugin
47
+ end
48
+
49
+ def message
50
+ "Unknown plugin: #{@plugin}"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,14 +1,14 @@
1
1
  module Ohm
2
2
  module Scope
3
- def self.included(base)
4
- unless defined?(base::DefinedScopes)
5
- base.const_set(:DefinedScopes, Module.new)
3
+ def self.included(model)
4
+ unless model.const_defined?(:DefinedScopes, false)
5
+ model.const_set(:DefinedScopes, Module.new)
6
6
  end
7
7
 
8
- base.extend Macros
8
+ model.extend ClassMethods
9
9
  end
10
10
 
11
- module Macros
11
+ module ClassMethods
12
12
  def scope(scope = nil, &block)
13
13
  self::DefinedScopes.module_eval(&block) if block_given?
14
14
  self::DefinedScopes.send(:include, scope) if scope
@@ -25,5 +25,6 @@ module Ohm
25
25
 
26
26
  Ohm::Model::Set.send :include, OverloadedSet
27
27
  end
28
- end
29
28
 
29
+ Model::Set.send :include, Scope::OverloadedSet
30
+ end
@@ -1,17 +1,17 @@
1
1
  module Ohm
2
2
  module Slug
3
- def self.included(base)
4
- base.extend FinderOverride
3
+ def self.included(model)
4
+ model.extend ClassMethods
5
5
  end
6
6
 
7
- module FinderOverride
7
+ module ClassMethods
8
8
  def [](id)
9
9
  super(id.to_i)
10
10
  end
11
11
  end
12
12
 
13
13
  def slug(str = to_s)
14
- ret = iconv(str)
14
+ ret = transcode(str)
15
15
  ret.gsub!("'", "")
16
16
  ret.gsub!(/\p{^Alnum}/u, " ")
17
17
  ret.strip!
@@ -20,16 +20,21 @@ module Ohm
20
20
  end
21
21
  module_function :slug
22
22
 
23
- def iconv(str)
23
+ def transcode(str)
24
24
  begin
25
+ # TODO: replace with a String#encode version which will
26
+ # contain proper transliteration tables. For now, Iconv
27
+ # still wins because we get that for free.
28
+ v, $VERBOSE = $VERBOSE, nil
25
29
  require "iconv"
30
+ $VERBOSE = v
26
31
 
27
32
  Iconv.iconv("ascii//translit//ignore", "utf-8", str)[0]
28
33
  rescue LoadError
29
34
  return str
30
35
  end
31
36
  end
32
- module_function :iconv
37
+ module_function :transcode
33
38
 
34
39
  def to_param
35
40
  "#{ id }-#{ slug }"
@@ -4,7 +4,7 @@ module Ohm
4
4
  # @example
5
5
  #
6
6
  # class Post < Ohm::Model
7
- # include Ohm::SoftDelete
7
+ # plugin :softdelete
8
8
  #
9
9
  # attribute :title
10
10
  # index :title
@@ -23,7 +23,7 @@ module Ohm
23
23
  # Post.all.empty?
24
24
  # # => true
25
25
  #
26
- # Post.find(:title => 'Title').empty?
26
+ # Post.find(:title => 'Title').include?(post)
27
27
  # # => true
28
28
  #
29
29
  # Post.exists?(post.id)
@@ -34,35 +34,39 @@ module Ohm
34
34
  # post.deleted?
35
35
  # # => true
36
36
  module SoftDelete
37
- IS_DELETED = "1"
37
+ DELETED_FLAG = "1"
38
38
 
39
- def self.included(base)
40
- base.attribute :deleted
41
- base.index :deleted
42
- base.extend ClassMethods
39
+ def self.included(model)
40
+ model.attribute :deleted
41
+
42
+ model.extend ClassMethods
43
43
  end
44
44
 
45
45
  def delete
46
- update(:deleted => IS_DELETED)
47
- end
46
+ self.class.all.delete(self)
47
+ self.class.deleted.add(self)
48
48
 
49
- def deleted?
50
- deleted == IS_DELETED
49
+ update(deleted: DELETED_FLAG)
51
50
  end
52
51
 
53
- private
54
- def create_model_membership
55
- self.class.key[:all].sadd(self.id)
52
+ def restore
53
+ self.class.all.add(self)
54
+ self.class.deleted.delete(self)
55
+
56
+ update(deleted: nil)
56
57
  end
57
58
 
58
- def delete_model_membership
59
- key.del
60
- self.class.key[:all].srem(self.id)
59
+ def deleted?
60
+ deleted == DELETED_FLAG
61
61
  end
62
62
 
63
63
  module ClassMethods
64
- def all
65
- super.except(:deleted => IS_DELETED)
64
+ def deleted
65
+ Model::Set.new(key[:deleted], Model::Wrapper.wrap(self))
66
+ end
67
+
68
+ def exists?(id)
69
+ super || key[:deleted].sismember(id)
66
70
  end
67
71
  end
68
72
  end
@@ -0,0 +1,41 @@
1
+ require_relative "callbacks"
2
+ require_relative "datatypes"
3
+
4
+ module Ohm
5
+ # Provides created_at / updated_at timestamps.
6
+ #
7
+ # @example
8
+ #
9
+ # class Post < Ohm::Model
10
+ # include Ohm::Timestamping
11
+ # end
12
+ #
13
+ # post = Post.create
14
+ # post.created_at.to_s == Time.now.utc.to_s
15
+ # # => true
16
+ #
17
+ # post = Post[post.id]
18
+ # post.save
19
+ # post.updated_at.to_s == Time.now.utc.to_s
20
+ # # => true
21
+ module Timestamping
22
+ def self.included(model)
23
+ model.send :include, Callbacks
24
+
25
+ model.attribute :created_at, DataTypes::Type::Timestamp
26
+ model.attribute :updated_at, DataTypes::Type::Timestamp
27
+
28
+ model.before :create, :set_created_at
29
+ model.before :save, :set_updated_at
30
+ end
31
+
32
+ protected
33
+ def set_created_at
34
+ self.created_at ||= Time.now.utc.to_i
35
+ end
36
+
37
+ def set_updated_at
38
+ self.updated_at = Time.now.utc.to_i
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "ohm-contrib"
3
+ s.version = "1.0.rc0"
4
+ s.summary = %{A collection of decoupled drop-in modules for Ohm.}
5
+ s.description = %{Includes a couple of core functions such as callbacks, timestamping, typecasting and lots of generic validation routines.}
6
+ s.author = "Cyril David"
7
+ s.email = "cyx.ucron@gmail.com"
8
+ s.homepage = "http://github.com/cyx/ohm-contrib"
9
+
10
+ s.specification_version = 2 if s.respond_to? :specification_version=
11
+
12
+ s.files = Dir[
13
+ "LICENSE",
14
+ "README.markdown",
15
+ "rakefile",
16
+ "lib/**/*.rb",
17
+ "*.gemspec",
18
+ "test/*.*",
19
+ ]
20
+
21
+ s.require_paths = ["lib"]
22
+ s.rubyforge_project = "ohm-contrib"
23
+
24
+ s.has_rdoc = false
25
+ s.add_dependency "ohm"
26
+
27
+ s.add_development_dependency "cutest"
28
+ s.add_development_dependency "redis"
29
+ s.add_development_dependency "override"
30
+ end
data/rakefile ADDED
@@ -0,0 +1,6 @@
1
+ task :test do
2
+ require 'cutest'
3
+ $:.unshift('./lib', './test')
4
+
5
+ Cutest.run(Dir['./test/*.rb'])
6
+ end
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
3
+ require_relative "helper"
4
4
 
5
5
  class Post < Ohm::Model
6
6
  include Ohm::Callbacks
@@ -22,8 +22,6 @@ class Post < Ohm::Model
22
22
  end
23
23
 
24
24
  protected
25
- def before_validate() incr(:do_before_validate) end
26
- def after_validate() incr(:do_after_validate) end
27
25
  def before_create() incr(:do_before_create) end
28
26
  def after_create() incr(:do_after_create) end
29
27
  def before_save() incr(:do_before_save) end
@@ -33,7 +31,6 @@ protected
33
31
  def before_delete() incr(:do_before_delete) end
34
32
  def after_delete() incr(:do_after_delete) end
35
33
 
36
-
37
34
  def incr(action)
38
35
  val = instance_variable_get("@#{ action }")
39
36
  val ||= 0
@@ -43,5 +40,4 @@ protected
43
40
  end
44
41
  end
45
42
 
46
- load File.expand_path('./callbacks_lint.rb', File.dirname(__FILE__))
47
-
43
+ load File.expand_path('lint/callbacks.rb', File.dirname(__FILE__))
@@ -7,9 +7,6 @@ class Post < Ohm::Model
7
7
 
8
8
  attribute :body
9
9
 
10
- before :validate, :do_before_validate
11
- after :validate, :do_after_validate
12
-
13
10
  before :create, :do_before_create
14
11
  after :create, :do_after_create
15
12
 
@@ -23,8 +20,6 @@ class Post < Ohm::Model
23
20
  after :update, :do_after_update
24
21
 
25
22
  def validate
26
- super
27
-
28
23
  assert_present :body
29
24
  end
30
25
 
@@ -37,8 +32,6 @@ class Post < Ohm::Model
37
32
  end
38
33
 
39
34
  protected
40
- def do_before_validate() incr(:do_before_validate) end
41
- def do_after_validate() incr(:do_after_validate) end
42
35
  def do_before_create() incr(:do_before_create) end
43
36
  def do_after_create() incr(:do_after_create) end
44
37
  def do_before_save() incr(:do_before_save) end
@@ -58,5 +51,4 @@ protected
58
51
  end
59
52
  end
60
53
 
61
- load File.expand_path('./callbacks_lint.rb', File.dirname(__FILE__))
62
-
54
+ load File.expand_path('lint/callbacks.rb', File.dirname(__FILE__))