diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..c5c6e88 --- /dev/null +++ b/README.markdown @@ -0,0 +1,7 @@ +Spree MultiStore Extension + +Individual stores are accessed like this: + +http://example.com/my_store + + diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..09fab7c --- /dev/null +++ b/Rakefile @@ -0,0 +1,120 @@ +# I think this is the one that should be moved to the extension Rakefile template + +# In rails 1.2, plugins aren't available in the path until they're loaded. +# Check to see if the rspec plugin is installed first and require +# it if it is. If not, use the gem version. + +# Determine where the RSpec plugin is by loading the boot +unless defined? SPREE_ROOT + ENV["RAILS_ENV"] = "test" + case + when ENV["SPREE_ENV_FILE"] + require File.dirname(ENV["SPREE_ENV_FILE"]) + "/boot" + when File.dirname(__FILE__) =~ %r{vendor/SPREE/vendor/extensions} + require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot" + else + require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot" + end +end + +require 'rake' +require 'rake/rdoctask' +require 'rake/testtask' + +rspec_base = File.expand_path(SPREE_ROOT + '/vendor/plugins/rspec/lib') +$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base) +require 'spec/rake/spectask' +# require 'spec/translator' + +# Cleanup the SPREE_ROOT constant so specs will load the environment +Object.send(:remove_const, :SPREE_ROOT) + +extension_root = File.expand_path(File.dirname(__FILE__)) + +task :default => :spec +task :stats => "spec:statsetup" + +desc "Run all specs in spec directory" +Spec::Rake::SpecTask.new(:spec) do |t| + t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""] + t.spec_files = FileList['spec/**/*_spec.rb'] +end + +namespace :spec do + desc "Run all specs in spec directory with RCov" + Spec::Rake::SpecTask.new(:rcov) do |t| + t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""] + t.spec_files = FileList['spec/**/*_spec.rb'] + t.rcov = true + t.rcov_opts = ['--exclude', 'spec', '--rails'] + end + + desc "Print Specdoc for all specs" + Spec::Rake::SpecTask.new(:doc) do |t| + t.spec_opts = ["--format", "specdoc", "--dry-run"] + t.spec_files = FileList['spec/**/*_spec.rb'] + end + + [:models, :controllers, :views, :helpers].each do |sub| + desc "Run the specs under spec/#{sub}" + Spec::Rake::SpecTask.new(sub) do |t| + t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""] + t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"] + end + end + + # Hopefully no one has written their extensions in pre-0.9 style + # desc "Translate specs from pre-0.9 to 0.9 style" + # task :translate do + # translator = ::Spec::Translator.new + # dir = RAILS_ROOT + '/spec' + # translator.translate(dir, dir) + # end + + # Setup specs for stats + task :statsetup do + require 'code_statistics' + ::STATS_DIRECTORIES << %w(Model\ specs spec/models) + ::STATS_DIRECTORIES << %w(View\ specs spec/views) + ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) + ::STATS_DIRECTORIES << %w(Helper\ specs spec/views) + ::CodeStatistics::TEST_TYPES << "Model specs" + ::CodeStatistics::TEST_TYPES << "View specs" + ::CodeStatistics::TEST_TYPES << "Controller specs" + ::CodeStatistics::TEST_TYPES << "Helper specs" + ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/} + end + + namespace :db do + namespace :fixtures do + desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y" + task :load => :environment do + require 'active_record/fixtures' + ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym) + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file| + Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*')) + end + end + end + end +end + +desc 'Generate documentation for the multi_store extension.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'MultiStoreExtension' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +# For extensions that are in transition +desc 'Test the multi_store extension.' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end + +# Load any custom rakefiles for extension +Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f } \ No newline at end of file diff --git a/app/controllers/stores_controller.rb b/app/controllers/stores_controller.rb new file mode 100644 index 0000000..9f04e76 --- /dev/null +++ b/app/controllers/stores_controller.rb @@ -0,0 +1,10 @@ +class StoresController < Spree::BaseController + + def index + @stores = Store.find :all + end + + def show + @store = current_store + end +end diff --git a/app/helpers/stores_helper.rb b/app/helpers/stores_helper.rb new file mode 100644 index 0000000..1ed55e9 --- /dev/null +++ b/app/helpers/stores_helper.rb @@ -0,0 +1,2 @@ +module StoresHelper +end diff --git a/app/models/store.rb b/app/models/store.rb new file mode 100644 index 0000000..2787669 --- /dev/null +++ b/app/models/store.rb @@ -0,0 +1,32 @@ +class Store < ActiveRecord::Base + + class UndefinedError < StandardError; end + + has_many :users + has_many :products + + validates_presence_of :name + validates_uniqueness_of :host + + # From Altered Beast - an empty host field makes it the default store to display + def host=(value) + write_attribute :host, value.to_s.downcase + end + + def self.main + @main ||= find :first, :conditions => {:host => ''} + end + + def self.find_by_host(name) + return nil if name.nil? + name.downcase! + name.strip! + name.sub! /^www\./, '' + sites = find :all, :conditions => ['host = ? or host = ?', name, ''] + sites.reject { |s| s.default? }.first || sites.first + end + + def default? + host.blank? + end +end diff --git a/app/views/stores/index.html.haml b/app/views/stores/index.html.haml new file mode 100644 index 0000000..c70dbae --- /dev/null +++ b/app/views/stores/index.html.haml @@ -0,0 +1,2 @@ +- for store in @stores + %p= store.name \ No newline at end of file diff --git a/app/views/stores/show.html.haml b/app/views/stores/show.html.haml new file mode 100644 index 0000000..7308ff2 --- /dev/null +++ b/app/views/stores/show.html.haml @@ -0,0 +1,3 @@ +%h1= @store.name +- for product in @store.products + %p= product.name \ No newline at end of file diff --git a/app/views/users/_store.html.haml b/app/views/users/_store.html.haml new file mode 100644 index 0000000..b7a36a7 --- /dev/null +++ b/app/views/users/_store.html.haml @@ -0,0 +1,13 @@ +- fields_for :store do |s| + %fieldset + %table + %tr + %td Site name + %td + = error_message_on :store, :name + = s.text_field :name + %tr + %td Host + %td + = error_message_on :store, :host + = s.text_field :host diff --git a/db/migrate/20081005074103_create_stores.rb b/db/migrate/20081005074103_create_stores.rb new file mode 100644 index 0000000..66ab545 --- /dev/null +++ b/db/migrate/20081005074103_create_stores.rb @@ -0,0 +1,16 @@ +class CreateStores < ActiveRecord::Migration + def self.up + create_table :stores do |t| + t.string :name, :host + t.timestamps + end + add_column :products, :store_id, :integer + add_column :users, :store_id, :integer + end + + def self.down + remove_column :users, :store_id + remove_column :products, :store_id + drop_table :stores + end +end diff --git a/lib/store_system.rb b/lib/store_system.rb new file mode 100644 index 0000000..a84b551 --- /dev/null +++ b/lib/store_system.rb @@ -0,0 +1,8 @@ +module StoreSystem + protected + + def current_store + @current_store ||= Store.find_by_host(request.host) or raise Store::UndefinedError + end + + end diff --git a/lib/tasks/multi_store_extension_tasks.rake b/lib/tasks/multi_store_extension_tasks.rake new file mode 100644 index 0000000..bbb0268 --- /dev/null +++ b/lib/tasks/multi_store_extension_tasks.rake @@ -0,0 +1,17 @@ +namespace :spree do + namespace :extensions do + namespace :multi_store do + desc "Copies public assets of the MultiStore to the instance public/ directory." + task :update => :environment do + is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) } + Dir[MultiStoreExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file| + path = file.sub(MultiStoreExtension.root, '') + directory = File.dirname(path) + puts "Copying #{path}..." + mkdir_p RAILS_ROOT + directory + cp file, RAILS_ROOT + path + end + end + end + end +end \ No newline at end of file diff --git a/multi_store_extension.rb b/multi_store_extension.rb new file mode 100644 index 0000000..cdc10aa --- /dev/null +++ b/multi_store_extension.rb @@ -0,0 +1,66 @@ +# Uncomment this if you reference any of your controllers in activate +require_dependency 'application' + +class MultiStoreExtension < Spree::Extension + version "1.0" + description "Implements multistore functionality" + url "http://yourwebsite.com/stores" + + define_routes do |map| + # map.namespace :admin do |admin| + # admin.resources :whatever + # end + map.root :controller => "stores", :action => "index" + map.resources :stores + end + + def activate + + ApplicationController.class_eval do + include StoreSystem + end + + ProductsController.class_eval do + before_filter :get_store_and_products, :only => :index + before_filter :get_store_and_product, :only => :show + + def get_store_and_products + # @store = current_store + # @products = @store.products + end + + def get_store_and_product + @store = current_store + @product = @store.product.find(params[:id]) + end + + end + + Product.class_eval do + belongs_to :store + end + + UsersController.class_eval do + before_filter :add_store_fields, :only => :new + before_filter :add_store, :only => [ :create ] + + def add_store + store = @user.store.build(params[:store]) + end + + def add_store_fields + @extension_partials << 'store' + end + end + + User.class_eval do + belongs_to :store + end + + end + + def deactivate + # admin.tabs.remove "Look And Feel" + end + +end \ No newline at end of file diff --git a/spec/controllers/stores_controller_spec.rb b/spec/controllers/stores_controller_spec.rb new file mode 100644 index 0000000..05b3ecd --- /dev/null +++ b/spec/controllers/stores_controller_spec.rb @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe StoresController do + + #Delete this example and add some real ones + it "should use StoresController" do + controller.should be_an_instance_of(StoresController) + end + +end diff --git a/spec/helpers/stores_helper_spec.rb b/spec/helpers/stores_helper_spec.rb new file mode 100644 index 0000000..4823443 --- /dev/null +++ b/spec/helpers/stores_helper_spec.rb @@ -0,0 +1,11 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe StoresHelper do + + #Delete this example and add some real ones or delete this file + it "should include the StoresHelper" do + included_modules = self.metaclass.send :included_modules + included_modules.should include(StoresHelper) + end + +end diff --git a/spec/models/store_spec.rb b/spec/models/store_spec.rb new file mode 100644 index 0000000..1f5f675 --- /dev/null +++ b/spec/models/store_spec.rb @@ -0,0 +1,11 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +describe Store do + before(:each) do + @store = Store.new + end + + it "should be valid" do + @store.should be_valid + end +end diff --git a/spec/spec.opts b/spec/spec.opts new file mode 100644 index 0000000..d8c8db5 --- /dev/null +++ b/spec/spec.opts @@ -0,0 +1,6 @@ +--colour +--format +progress +--loadby +mtime +--reverse diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..ffde315 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,37 @@ +unless defined? SPREE_ROOT + ENV["RAILS_ENV"] = "test" + case + when ENV["SPREE_ENV_FILE"] + require ENV["SPREE_ENV_FILE"] + when File.dirname(__FILE__) =~ %r{vendor/SPREE/vendor/extensions} + require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment" + else + require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment" + end +end +require "#{SPREE_ROOT}/spec/spec_helper" + +if File.directory?(File.dirname(__FILE__) + "/scenarios") + Scenario.load_paths.unshift File.dirname(__FILE__) + "/scenarios" +end +if File.directory?(File.dirname(__FILE__) + "/matchers") + Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file } +end + +Spec::Runner.configure do |config| + # config.use_transactional_fixtures = true + # config.use_instantiated_fixtures = false + # config.fixture_path = RAILS_ROOT + '/spec/fixtures' + + # You can declare fixtures for each behaviour like this: + # describe "...." do + # fixtures :table_a, :table_b + # + # Alternatively, if you prefer to declare them only once, you can + # do so here, like so ... + # + # config.global_fixtures = :table_a, :table_b + # + # If you declare global fixtures, be aware that they will be declared + # for all of your examples, even those that don't use them. +end \ No newline at end of file