Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 14a8faa57439820c0b6ca05631d89fab397e50f6 @dim dim committed Feb 18, 2011
Showing with 958 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +1 −0 .rspec
  3. +13 −0 Gemfile
  4. +77 −0 Gemfile.lock
  5. +8 −0 Rakefile
  6. +22 −0 lib/bsm/constrainable.rb
  7. +18 −0 lib/bsm/constrainable/field.rb
  8. +44 −0 lib/bsm/constrainable/field/base.rb
  9. +43 −0 lib/bsm/constrainable/field/common.rb
  10. +8 −0 lib/bsm/constrainable/filter.rb
  11. +19 −0 lib/bsm/constrainable/model.rb
  12. +25 −0 lib/bsm/constrainable/operation.rb
  13. +54 −0 lib/bsm/constrainable/operation/base.rb
  14. +21 −0 lib/bsm/constrainable/operation/between.rb
  15. +9 −0 lib/bsm/constrainable/operation/collection.rb
  16. +14 −0 lib/bsm/constrainable/operation/common.rb
  17. +4 −0 lib/bsm/constrainable/operation/in.rb
  18. +4 −0 lib/bsm/constrainable/operation/not_in.rb
  19. +21 −0 lib/bsm/constrainable/registry.rb
  20. +9 −0 lib/bsm/constrainable/relation.rb
  21. +52 −0 lib/bsm/constrainable/schema.rb
  22. +15 −0 lib/bsm/constrainable/util.rb
  23. +37 −0 spec/bsm/constrainable/field/base_spec.rb
  24. +52 −0 spec/bsm/constrainable/field/common_spec.rb
  25. +12 −0 spec/bsm/constrainable/field_spec.rb
  26. +31 −0 spec/bsm/constrainable/model_spec.rb
  27. +40 −0 spec/bsm/constrainable/operation/base_spec.rb
  28. +26 −0 spec/bsm/constrainable/operation/between_spec.rb
  29. +17 −0 spec/bsm/constrainable/operation/collection_spec.rb
  30. +15 −0 spec/bsm/constrainable/operation/common_spec.rb
  31. +17 −0 spec/bsm/constrainable/operation/in_spec.rb
  32. +17 −0 spec/bsm/constrainable/operation/not_in_spec.rb
  33. +12 −0 spec/bsm/constrainable/operation_spec.rb
  34. +22 −0 spec/bsm/constrainable/registry_spec.rb
  35. +19 −0 spec/bsm/constrainable/relation_spec.rb
  36. +33 −0 spec/bsm/constrainable/schema_spec.rb
  37. +36 −0 spec/bsm/constrainable/util_spec.rb
  38. +6 −0 spec/bsm/constrainable_spec.rb
  39. +6 −0 spec/fixtures/authors.yml
  40. +14 −0 spec/fixtures/posts.yml
  41. +63 −0 spec/spec_helper.rb
@@ -0,0 +1,2 @@
+.project
+spec/tmp
1 .rspec
@@ -0,0 +1 @@
+--colour
13 Gemfile
@@ -0,0 +1,13 @@
+source "http://rubygems.org"
+
+gem "activesupport"
+gem "activemodel"
+gem "abstract"
+
+group :test do
+ gem "activerecord"
+ gem "rspec"
+ gem "rspec-rails", :require => false
+ gem "sqlite3-ruby"
+ gem "shoulda", "~> 3.0.0.beta"
+end
@@ -0,0 +1,77 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ abstract (1.0.0)
+ actionpack (3.0.4)
+ activemodel (= 3.0.4)
+ activesupport (= 3.0.4)
+ builder (~> 2.1.2)
+ erubis (~> 2.6.6)
+ i18n (~> 0.4)
+ rack (~> 1.2.1)
+ rack-mount (~> 0.6.13)
+ rack-test (~> 0.5.7)
+ tzinfo (~> 0.3.23)
+ activemodel (3.0.4)
+ activesupport (= 3.0.4)
+ builder (~> 2.1.2)
+ i18n (~> 0.4)
+ activerecord (3.0.4)
+ activemodel (= 3.0.4)
+ activesupport (= 3.0.4)
+ arel (~> 2.0.2)
+ tzinfo (~> 0.3.23)
+ activesupport (3.0.4)
+ arel (2.0.8)
+ builder (2.1.2)
+ diff-lcs (1.1.2)
+ erubis (2.6.6)
+ abstract (>= 1.0.0)
+ i18n (0.5.0)
+ rack (1.2.1)
+ rack-mount (0.6.13)
+ rack (>= 1.0.0)
+ rack-test (0.5.7)
+ rack (>= 1.0)
+ railties (3.0.4)
+ actionpack (= 3.0.4)
+ activesupport (= 3.0.4)
+ rake (>= 0.8.7)
+ thor (~> 0.14.4)
+ rake (0.8.7)
+ rspec (2.5.0)
+ rspec-core (~> 2.5.0)
+ rspec-expectations (~> 2.5.0)
+ rspec-mocks (~> 2.5.0)
+ rspec-core (2.5.1)
+ rspec-expectations (2.5.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.5.0)
+ rspec-rails (2.5.0)
+ actionpack (~> 3.0)
+ activesupport (~> 3.0)
+ railties (~> 3.0)
+ rspec (~> 2.5.0)
+ shoulda (3.0.0.beta2)
+ shoulda-context (~> 1.0.0.beta1)
+ shoulda-matchers (~> 1.0.0.beta1)
+ shoulda-context (1.0.0.beta1)
+ shoulda-matchers (1.0.0.beta1)
+ sqlite3 (1.3.3)
+ sqlite3-ruby (1.3.3)
+ sqlite3 (>= 1.3.3)
+ thor (0.14.6)
+ tzinfo (0.3.24)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ abstract
+ activemodel
+ activerecord
+ activesupport
+ rspec
+ rspec-rails
+ shoulda (~> 3.0.0.beta)
+ sqlite3-ruby
@@ -0,0 +1,8 @@
+require 'rake'
+require 'rspec/mocks/version'
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new(:spec)
+
+desc 'Default: run specs.'
+task :default => :spec
@@ -0,0 +1,22 @@
+require "active_support/core_ext"
+
+module Bsm
+ module Constrainable
+ autoload :Util, "bsm/constrainable/util"
+ autoload :Model, "bsm/constrainable/model"
+ autoload :Relation, "bsm/constrainable/relation"
+ autoload :Schema, "bsm/constrainable/schema"
+ autoload :Registry, "bsm/constrainable/registry"
+ autoload :Field, "bsm/constrainable/field"
+ autoload :Operation, "bsm/constrainable/operation"
+ autoload :Filter, "bsm/constrainable/filter"
+ end
+end
+
+ActiveRecord::Base.class_eval do
+ include Bsm::Constrainable::Model
+end
+
+ActiveRecord::Relation.class_eval do
+ include Bsm::Constrainable::Relation
+end
@@ -0,0 +1,18 @@
+module Bsm::Constrainable::Field
+ extend Bsm::Constrainable::Registry
+
+ autoload :Base, 'bsm/constrainable/field/base'
+ autoload :Number, 'bsm/constrainable/field/common'
+ autoload :Integer, 'bsm/constrainable/field/common'
+ autoload :Decimal, 'bsm/constrainable/field/common'
+ autoload :String, 'bsm/constrainable/field/common'
+ autoload :Timestamp,'bsm/constrainable/field/common'
+ autoload :Date, 'bsm/constrainable/field/common'
+
+ register self::Number
+ register self::Integer
+ register self::Decimal
+ register self::String
+ register self::Timestamp
+ register self::Date
+end
@@ -0,0 +1,44 @@
+class Bsm::Constrainable::Field::Base
+ DEFAULT_OPERATORS = [:eq, :not_eq, :gt, :gteq, :lt, :lteq, :between].freeze
+
+ class_inheritable_accessor :operators, :defaults, :instance_reader => false, :instance_writer => false
+ self.operators = DEFAULT_OPERATORS.dup
+ self.defaults = [:eq]
+
+ def self.kind
+ @kind ||= name.demodulize.underscore.to_sym
+ end
+
+ attr_reader :name, :operators, :attribute, :scope
+
+ def initialize(name, options = {})
+ @name = name.to_s
+ @attribute = options[:using] || name
+ @operators = Set.new(self.class.operators & Array.wrap(options[:with]))
+ @operators = Set.new(self.class.defaults) if @operators.empty?
+ @scope = options[:scope]
+ end
+
+ def merge(relation, params)
+ params.slice(*operators).each do |operator, value|
+ operation = Bsm::Constrainable::Operation.new(operator, value, relation, self)
+ clause = operation.clause
+ next if clause.nil?
+
+ relation = relation.instance_eval(&:scope) if scope
+ relation = relation.where(clause)
+ end
+ relation
+ end
+
+ def convert(value)
+ value.is_a?(Array) ? value.map {|v| convert(v) } : _convert(value)
+ end
+
+ protected
+
+ def _convert(value)
+ value.to_s
+ end
+
+end
@@ -0,0 +1,43 @@
+module Bsm::Constrainable::Field
+ class Number < Base
+ self.operators += [:in, :not_in]
+
+ protected
+
+ def _convert(v)
+ Float(v) rescue nil
+ end
+ end
+
+ class Integer < Number
+ protected
+
+ def _convert(v)
+ result = super
+ result ? result.to_i : nil
+ end
+ end
+
+ class Decimal < Number
+ end
+
+ class String < Base
+ self.operators = [:eq, :not_eq]
+ end
+
+ class Timestamp < Base
+ protected
+
+ def _convert(v)
+ v.to_time(:utc) rescue nil
+ end
+ end
+
+ class Date < Base
+ protected
+
+ def _convert(v)
+ v.to_date rescue nil
+ end
+ end
+end
@@ -0,0 +1,8 @@
+class Bsm::Constrainable::Filter < Hash
+ include Bsm::Constrainable::Util
+
+ def initialize(schema, params)
+ end
+
+end
+
@@ -0,0 +1,19 @@
+module Bsm::Constrainable::Model
+ extend ActiveSupport::Concern
+
+ included do
+ class_inheritable_accessor :_constrainable
+ self._constrainable = {}
+ end
+
+ module ClassMethods
+
+ def constrainable(name = nil, &block)
+ name = name.present? ? name.to_sym : :default
+ _constrainable[name] ||= Bsm::Constrainable::Schema.new
+ _constrainable[name.to_sym].instance_eval(&block) if block
+ _constrainable[name]
+ end
+
+ end
+end
@@ -0,0 +1,25 @@
+module Bsm::Constrainable::Operation
+ extend Bsm::Constrainable::Registry
+
+ autoload :Base, 'bsm/constrainable/operation/base'
+ autoload :Collection, 'bsm/constrainable/operation/collection'
+ autoload :Eq, 'bsm/constrainable/operation/common'
+ autoload :NotEq, 'bsm/constrainable/operation/common'
+ autoload :Gt, 'bsm/constrainable/operation/common'
+ autoload :Lt, 'bsm/constrainable/operation/common'
+ autoload :Gteq, 'bsm/constrainable/operation/common'
+ autoload :Lteq, 'bsm/constrainable/operation/common'
+ autoload :In, 'bsm/constrainable/operation/in'
+ autoload :NotIn, 'bsm/constrainable/operation/not_in'
+ autoload :Between, 'bsm/constrainable/operation/between'
+
+ register self::Eq
+ register self::NotEq
+ register self::In
+ register self::NotIn
+ register self::Gt
+ register self::Lt
+ register self::Gteq
+ register self::Lteq
+ register self::Between
+end
@@ -0,0 +1,54 @@
+class Bsm::Constrainable::Operation::Base
+ extend ActiveSupport::Memoizable
+
+ def self.kind
+ name.demodulize.underscore.to_sym
+ end
+
+ attr_reader :value, :field, :relation
+
+ def initialize(value, relation, field)
+ @value = value
+ @relation = relation
+ @field = field
+ end
+
+ def parsed
+ value.to_s
+ end
+
+ def valid?
+ !invalid?
+ end
+
+ def invalid?
+ wrap = Array.wrap(normalized)
+ wrap.empty? || wrap.any?(&:nil?)
+ end
+
+ def clause
+ valid? ? _clause : nil
+ end
+
+ protected
+
+ def _clause
+ attribute.send self.class.kind, normalized
+ end
+
+ def normalized
+ field.convert(parsed)
+ end
+ memoize :normalized
+
+ def attribute
+ case field.attribute
+ when Proc
+ field.attribute.call(relation)
+ else
+ relation.table[field.attribute]
+ end
+ end
+ memoize :attribute
+
+end
@@ -0,0 +1,21 @@
+module Bsm::Constrainable::Operation
+ class Between < Base
+
+ def parsed
+ result = case value
+ when /^ *(.+?) *\.{2,} *(.+?) *$/
+ [$1, $2]
+ else
+ value
+ end
+ result.is_a?(Array) && result.size == 2 ? result.map(&:to_s) : nil
+ end
+
+ protected
+
+ def _clause
+ attribute.gteq(normalized.first).and(attribute.lteq(normalized.last))
+ end
+
+ end
+end
@@ -0,0 +1,9 @@
+module Bsm::Constrainable::Operation
+ class Collection < Base
+
+ def parsed
+ Bsm::Constrainable::Util.normalized_array(value)
+ end
+
+ end
+end
@@ -0,0 +1,14 @@
+module Bsm::Constrainable::Operation
+ class Eq < Base
+ end
+ class NotEq < Base
+ end
+ class Gt < Base
+ end
+ class Gteq < Base
+ end
+ class Lt < Base
+ end
+ class Lteq < Base
+ end
+end
Oops, something went wrong.

0 comments on commit 14a8faa

Please sign in to comment.