Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial version

  • Loading branch information...
commit 8dfe0af93368e489e8bbbbe6723443e7b31676be 0 parents
@rlivsey authored
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2010 Richard Livsey
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 README.rdoc
@@ -0,0 +1,32 @@
+= MongoMapper::Plugins::MultiParameterAttributes
+
+Tiny plugin for MongoMapper to add multi-parameter attributes
+
+Based on Rails & gist by @bitzesty found at:
+https://gist.github.com/268948/c5ca7d8fb02470c5c8a0a8225390a4186349a8d4
+
+== Usage
+
+Either load it into all models, or individual models:
+
+ # add to all models
+ MongoMapper::Document.append_inclusions(MongoMapper::Plugins::MultiParameterAttributes)
+
+ # add to a specific model
+ plugin MongoMapper::Plugins::MultiParameterAttributes
+
+== Note on Patches/Pull Requests
+
+* Fork the project.
+* Make your feature addition or bug fix.
+* Add tests for it. This is important so I don't break it in a future version unintentionally.
+* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
+* Send me a pull request. Bonus points for topic branches.
+
+== Install
+
+ $ gem install mm-multi-parameter-attributes
+
+== Copyright
+
+See LICENSE for details.
75 Rakefile
@@ -0,0 +1,75 @@
+require "rubygems"
+require "rake/gempackagetask"
+require "rake/rdoctask"
+
+require "spec"
+require "spec/rake/spectask"
+Spec::Rake::SpecTask.new do |t|
+ t.spec_opts = %w(--format specdoc --colour)
+ t.libs = ["spec"]
+end
+
+
+task :default => ["spec"]
+
+# This builds the actual gem. For details of what all these options
+# mean, and other ones you can add, check the documentation here:
+#
+# http://rubygems.org/read/chapter/20
+#
+spec = Gem::Specification.new do |s|
+
+ # Change these as appropriate
+ s.name = "mm-multi-parameter-attributes"
+ s.version = "0.1.0"
+ s.summary = "Tiny plugin for MongoMapper to add multi-parameter attributes"
+ s.author = "Richard Livsey"
+ s.email = "richard@livsey.org"
+ s.homepage = "http://github.com/rlivsey/mm-multi-parameter-attributes"
+
+ s.has_rdoc = true
+ s.extra_rdoc_files = %w(README.rdoc)
+ s.rdoc_options = %w(--main README.rdoc)
+
+ # Add any extra files to include in the gem
+ s.files = %w(LICENSE Rakefile README.rdoc) + Dir.glob("{spec,lib/**/*}")
+ s.require_paths = ["lib"]
+
+ # If you want to depend on other gems, add them here, along with any
+ # relevant versions
+ s.add_dependency("mongo_mapper")
+
+ # If your tests use any gems, include them here
+ s.add_development_dependency("rspec")
+end
+
+# This task actually builds the gem. We also regenerate a static
+# .gemspec file, which is useful if something (i.e. GitHub) will
+# be automatically building a gem for this project. If you're not
+# using GitHub, edit as appropriate.
+#
+# To publish your gem online, install the 'gemcutter' gem; Read more
+# about that here: http://gemcutter.org/pages/gem_docs
+Rake::GemPackageTask.new(spec) do |pkg|
+ pkg.gem_spec = spec
+end
+
+desc "Build the gemspec file #{spec.name}.gemspec"
+task :gemspec do
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
+ File.open(file, "w") {|f| f << spec.to_ruby }
+end
+
+task :package => :gemspec
+
+# Generate documentation
+Rake::RDocTask.new do |rd|
+ rd.main = "README.rdoc"
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+ rd.rdoc_dir = "rdoc"
+end
+
+desc 'Clear out RDoc and generated packages'
+task :clean => [:clobber_rdoc, :clobber_package] do
+ rm "#{spec.name}.gemspec"
+end
95 lib/mm-multi-parameter-attributes.rb
@@ -0,0 +1,95 @@
+require 'mongo_mapper'
+
+module MongoMapper
+ module Plugins
+ module MultiParameterAttributes
+ def self.included(model)
+ model.plugin self
+ end
+
+ module InstanceMethods
+ def attributes=(new_attributes)
+ return if new_attributes.nil?
+
+ multi_parameter_attributes = []
+ normal_attributes = {}
+
+ new_attributes.each do |k, v|
+ if k.include?("(")
+ multi_parameter_attributes << [ k, v ]
+ else
+ normal_attributes[k] = v
+ end
+ end
+
+ assign_multiparameter_attributes(multi_parameter_attributes)
+
+ super(normal_attributes)
+ end
+
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
+ # s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.
+ def assign_multiparameter_attributes(pairs)
+ execute_callstack_for_multiparameter_attributes(
+ extract_callstack_for_multiparameter_attributes(pairs)
+ )
+ end
+
+ def execute_callstack_for_multiparameter_attributes(callstack)
+ callstack.each do |name, values_with_empty_parameters|
+ # in order to allow a date to be set without a year, we must keep the empty values.
+ # Otherwise, we wouldn't be able to distinguish it from a date with an empty day.
+ values = values_with_empty_parameters.reject(&:nil?)
+
+ if !values.reject{|x| x.blank? }.empty?
+ key = self.class.keys[name]
+ raise ArgumentError, "Unknown key #{name}" if key.nil?
+ klass = key.type
+
+ value = if Time == klass
+ Time.zone.local(*values.map(&:to_i))
+ elsif Date == klass
+ begin
+ values = values_with_empty_parameters.map{|v| v.blank? ? 1 : v.to_i}
+ Date.new(*values)
+ rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
+ Time.zone.local(*values.map(&:to_i)).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
+ end
+ else
+ klass.new(*values)
+ end
+ writer_method = "#{name}="
+ if respond_to?(writer_method)
+ self.send(writer_method, value)
+ else
+ self[name.to_s] = value
+ end
+ end
+ end
+ end
+
+ def extract_callstack_for_multiparameter_attributes(pairs)
+ attributes = { }
+
+ for pair in pairs
+ multiparameter_name, value = pair
+ attribute_name = multiparameter_name.split("(").first
+ attributes[attribute_name] = [] unless attributes.include?(attribute_name)
+
+ attributes[attribute_name] << [ find_parameter_position(multiparameter_name), value ]
+ end
+
+ attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
+ end
+
+ def find_parameter_position(multiparameter_name)
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
+ end
+ end
+ end
+ end
+end
34 mm-multi-parameter-attributes.gemspec
@@ -0,0 +1,34 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{mm-multi-parameter-attributes}
+ s.version = "0.1.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Richard Livsey"]
+ s.date = %q{2010-06-17}
+ s.email = %q{richard@livsey.org}
+ s.extra_rdoc_files = ["README.rdoc"]
+ s.files = ["LICENSE", "Rakefile", "README.rdoc", "lib/mm-multi-parameter-attributes.rb"]
+ s.homepage = %q{http://github.com/rlivsey/mm-multi-parameter-attributes}
+ s.rdoc_options = ["--main", "README.rdoc"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.7}
+ s.summary = %q{Tiny plugin for MongoMapper to add multi-parameter attributes (for date/times)}
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q<mongo_mapper>, [">= 0"])
+ s.add_development_dependency(%q<rspec>, [">= 0"])
+ else
+ s.add_dependency(%q<mongo_mapper>, [">= 0"])
+ s.add_dependency(%q<rspec>, [">= 0"])
+ end
+ else
+ s.add_dependency(%q<mongo_mapper>, [">= 0"])
+ s.add_dependency(%q<rspec>, [">= 0"])
+ end
+end
122 spec/mm-multi-parameter-attributes_spec.rb
@@ -0,0 +1,122 @@
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+
+describe "MongoMapper::Plugins::MultiParameterAttributes" do
+
+ before(:each) do
+ Time.zone = "UTC"
+ @topic = Topic.new
+ end
+
+ it "should assign date" do
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
+
+ @topic.attributes = attributes
+ @topic.last_read.to_date.should == Date.new(2004, 6, 24)
+ end
+
+ it "should assign date with empty year" do
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
+
+ @topic.attributes = attributes
+
+ # What should this do? MM use Time.utc which turns 0001 -> 2001 instead of leaving at 0001
+ # Making this pass by testing for what MM actually does, but is that a bug?
+
+ # @topic.last_read.to_date.should == Date.new(1, 6, 24)
+ @topic.last_read.to_date.should == Date.new(2001, 6, 24)
+ end
+
+ it "should assign date with empty month" do
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
+
+ @topic.attributes = attributes
+ @topic.last_read.to_date.should == Date.new(2004, 1, 24)
+ end
+
+ it "should assign date with empty day" do
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
+
+ @topic.attributes = attributes
+ @topic.last_read.to_date.should == Date.new(2004, 6, 1)
+ end
+
+ it "should assign date with empty day and year" do
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
+
+ @topic.attributes = attributes
+
+ # What should this do? MM use Time.utc which turns 0001 -> 2001 instead of leaving at 0001
+ # Making this pass by testing for what MM actually does, but is that a bug?
+
+ # @topic.last_read.to_date.should == Date.new(1, 6, 1)
+ @topic.last_read.to_date.should == Date.new(2001, 6, 1)
+ end
+
+ it "should assign date with empty day and month" do
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
+
+ @topic.attributes = attributes
+ @topic.last_read.to_date.should == Date.new(2004, 1, 1)
+ end
+
+ it "should assign date with empty year_and_month" do
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
+
+ @topic.attributes = attributes
+
+ # What should this do? MM use Time.utc which turns 0001 -> 2001 instead of leaving at 0001
+ # Making this pass by testing for what MM actually does, but is that a bug?
+
+ # @topic.last_read.to_date.should == Date.new(1, 1, 24)
+ @topic.last_read.to_date.should == Date.new(2001, 1, 24)
+ end
+
+ it "should assign date with all empty" do
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
+
+ @topic.attributes = attributes
+ @topic.last_read.should be_nil
+ end
+
+ it "should assign time" do
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+
+ @topic.attributes = attributes
+ @topic.written_on.should == Time.zone.local(2004, 6, 24, 16, 24, 0)
+ end
+
+ it "should assign time with old date" do
+ attributes = {
+ "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+
+ @topic.attributes = attributes
+ # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
+ @topic.written_on.to_s(:db).should == "1850-06-24 16:24:00"
+ end
+
+ it "should assign time with utc" do
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+
+ @topic.attributes = attributes
+ @topic.written_on.should == Time.utc(2004, 6, 24, 16, 24, 0)
+ end
+
+ it "should assign time with empty seconds" do
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
+ }
+
+ @topic.attributes = attributes
+ @topic.written_on.should == Time.zone.local(2004, 6, 24, 16, 24, 0)
+ end
+
+end
23 spec/spec_helper.rb
@@ -0,0 +1,23 @@
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+
+require 'rubygems'
+gem 'rspec'
+
+require 'mm-multi-parameter-attributes'
+require 'spec'
+
+MongoMapper.database = 'mm-multi-parameter-attributes-spec'
+
+class Topic
+ include MongoMapper::Document
+
+ plugin MongoMapper::Plugins::MultiParameterAttributes
+
+ key :title, String
+ key :last_read, Date
+ key :written_on, Time
+end
+
+Spec::Runner.configure do |config|
+end
Please sign in to comment.
Something went wrong with that request. Please try again.