lib/mixed_gauge/model.rb in mixed_gauge-0.1.1 vs lib/mixed_gauge/model.rb in mixed_gauge-0.1.2
- old
+ new
@@ -23,48 +23,118 @@
class_attribute :sub_model_repository, instance_writer: false
class_attribute :distkey, instance_writer: false
end
module ClassMethods
+ # The cluster config must be defined before `use_cluster`.
# @param [Symbol] A cluster name which is set by MixedGauge.configure
def use_cluster(name)
config = MixedGauge.config.fetch_cluster_config(name)
self.cluster_routing = MixedGauge::Routing.new(config)
self.sub_model_repository = MixedGauge::SubModelRepository.new(config, self)
self.abstract_class = true
end
+ # Distkey is a column. mixed_gauge hashes that value and determine which
+ # shard to store.
# @param [Symbol] column
def def_distkey(column)
self.distkey = column.to_sym
end
- # @param [Object] key
- # @return [ActiveRecord::Base] A auto-generated sub model of included model
- def get(key)
- shard_for(key.to_s).find_by(distkey => key)
- end
-
+ # Create new record with given attributes in proper shard for given key.
+ # When distkey value is empty, raises MixedGauge::MissingDistkeyAttribute
+ # error.
# @param [Hash] attributes
# @return [ActiveRecord::Base] A sub class instance of included model
+ # @raise [MixedGauge::MissingDistkeyAttribute]
def put!(attributes)
+ @before_put_callback.call(attributes) if @before_put_callback
+
if key = attributes[distkey] || attributes[distkey.to_s]
shard_for(key).create!(attributes)
else
raise MixedGauge::MissingDistkeyAttribute
end
end
- # @param [Object] key A value of distkey
+ # Returns nil when not found. Except that, is same as `.get!`.
+ # @param [String] key
+ # @return [ActiveRecord::Base, nil] A sub model instance of included model
+ def get(key)
+ raise 'key must be a String' unless key.is_a?(String)
+ shard_for(key.to_s).find_by(distkey => key)
+ end
+
+ # `.get!` raises MixedGauge::RecordNotFound which is child class of
+ # `ActiveRecord::RecordNotFound` so you can rescue that exception as same
+ # as AR's RecordNotFound.
+ # @param [String] key
+ # @return [ActiveRecord::Base] A sub model instance of included model
+ # @raise [MixedGauge::RecordNotFound]
+ def get!(key)
+ get(key) or raise MixedGauge::RecordNotFound
+ end
+
+ # Register hook to assign auto-generated distkey or something.
+ # Sometimes you want to generates distkey value before validation. Since
+ # mixed_gauge generates sub class of your models, AR's callback is not
+ # usesless for this usecase, so mixed_gauge offers its own callback method.
+ # @example
+ # class User
+ # include MixedGauge::Model
+ # use_cluster :user
+ # def_distkey :name
+ # before_put do |attributes|
+ # attributes[:name] = generate_name unless attributes[:name]
+ # end
+ # end
+ def before_put(&block)
+ @before_put_callback = block
+ end
+
+ # Returns a generated sub class of this model which is connected proper
+ # shard for given key.
+ # @param [String] key A value of distkey
# @return [Class] A sub model for this distkey value
def shard_for(key)
connection_name = cluster_routing.route(key.to_s)
sub_model_repository.fetch(connection_name)
end
+ # Returns all generated sub class of this model. Useful to query to
+ # all shards.
# @return [Array<Class>] An array of sub models
+ # @example
+ # User.all_shards.flat_map {|m| m.find_by(name: 'alice') }.compact
def all_shards
sub_model_repository.all
+ end
+
+ # Define utility methods which uses all shards or specific shard.
+ # These methods can be called from included model class.
+ # @example
+ # class User
+ # include MixedGauge::Model
+ # use_cluster :user
+ # def_distkey :name
+ # parent_methods do
+ # def all_count
+ # all_shards.map {|m| m.count }.reduce(&:+)
+ # end
+ #
+ # def find_from_all_by(condition)
+ # all_shards.flat_map {|m m.find_by(condition) }.compact.first
+ # end
+ # end
+ # end
+ #
+ # User.put!(email: 'a@m.com', name: 'a')
+ # User.put!(email: 'b@m.com', name: 'b')
+ # User.all_count #=> 2
+ # User.find_from_all_by(name: 'b') #=> User b
+ def parent_methods(&block)
+ instance_eval(&block)
end
end
end
end