perspectives 0.0.1

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 (84) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +13 -0
  4. data/lib/generators/perspectives/install.rb +38 -0
  5. data/lib/generators/perspectives/scaffold/scaffold_generator.rb +54 -0
  6. data/lib/generators/perspectives/scaffold/templates/edit.mustache +6 -0
  7. data/lib/generators/perspectives/scaffold/templates/edit.rb +8 -0
  8. data/lib/generators/perspectives/scaffold/templates/form.mustache +25 -0
  9. data/lib/generators/perspectives/scaffold/templates/form.rb +27 -0
  10. data/lib/generators/perspectives/scaffold/templates/index.mustache +22 -0
  11. data/lib/generators/perspectives/scaffold/templates/index.rb +9 -0
  12. data/lib/generators/perspectives/scaffold/templates/new.mustache +5 -0
  13. data/lib/generators/perspectives/scaffold/templates/new.rb +7 -0
  14. data/lib/generators/perspectives/scaffold/templates/show.mustache +10 -0
  15. data/lib/generators/perspectives/scaffold/templates/show.rb +8 -0
  16. data/lib/generators/perspectives/scaffold/templates/tiny.mustache +8 -0
  17. data/lib/generators/perspectives/scaffold/templates/tiny.rb +8 -0
  18. data/lib/generators/perspectives/templates/application.js +13 -0
  19. data/lib/generators/perspectives/templates/rails/scaffold_controller/controller.rb +82 -0
  20. data/lib/perspectives/active_record.rb +17 -0
  21. data/lib/perspectives/base.rb +30 -0
  22. data/lib/perspectives/caching.rb +82 -0
  23. data/lib/perspectives/collection.rb +21 -0
  24. data/lib/perspectives/configuration.rb +14 -0
  25. data/lib/perspectives/context.rb +18 -0
  26. data/lib/perspectives/controller_additions.rb +142 -0
  27. data/lib/perspectives/forms/base.rb +5 -0
  28. data/lib/perspectives/forms/text_field.rb +15 -0
  29. data/lib/perspectives/forms.rb +7 -0
  30. data/lib/perspectives/memoization.rb +30 -0
  31. data/lib/perspectives/mustache_compiler.rb +33 -0
  32. data/lib/perspectives/params.rb +48 -0
  33. data/lib/perspectives/properties.rb +130 -0
  34. data/lib/perspectives/railtie.rb +42 -0
  35. data/lib/perspectives/rendering.rb +15 -0
  36. data/lib/perspectives/responder.rb +15 -0
  37. data/lib/perspectives/templating.rb +39 -0
  38. data/lib/perspectives/version.rb +3 -0
  39. data/lib/perspectives.rb +48 -0
  40. data/lib/rails/projections.json +12 -0
  41. data/lib/tasks/perspectives_tasks.rake +4 -0
  42. data/spec/dummy/README.rdoc +28 -0
  43. data/spec/dummy/Rakefile +6 -0
  44. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  45. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  46. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  47. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  48. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  49. data/spec/dummy/bin/bundle +3 -0
  50. data/spec/dummy/bin/rails +4 -0
  51. data/spec/dummy/bin/rake +4 -0
  52. data/spec/dummy/config/application.rb +23 -0
  53. data/spec/dummy/config/boot.rb +5 -0
  54. data/spec/dummy/config/database.yml +25 -0
  55. data/spec/dummy/config/environment.rb +5 -0
  56. data/spec/dummy/config/environments/development.rb +29 -0
  57. data/spec/dummy/config/environments/production.rb +80 -0
  58. data/spec/dummy/config/environments/test.rb +36 -0
  59. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  61. data/spec/dummy/config/initializers/inflections.rb +16 -0
  62. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  63. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  64. data/spec/dummy/config/initializers/session_store.rb +3 -0
  65. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  66. data/spec/dummy/config/locales/en.yml +23 -0
  67. data/spec/dummy/config/routes.rb +56 -0
  68. data/spec/dummy/config.ru +4 -0
  69. data/spec/dummy/log/test.log +0 -0
  70. data/spec/dummy/public/404.html +58 -0
  71. data/spec/dummy/public/422.html +58 -0
  72. data/spec/dummy/public/500.html +57 -0
  73. data/spec/dummy/public/favicon.ico +0 -0
  74. data/spec/lib/perspectives/properties_spec.rb +31 -0
  75. data/spec/lib/perspectives/templating_spec.rb +18 -0
  76. data/spec/lib/perspectives_spec.rb +5 -0
  77. data/spec/mustaches/users/simple_info.mustache +1 -0
  78. data/spec/rails_helper.rb +11 -0
  79. data/spec/spec_helper.rb +16 -0
  80. data/vendor/assets/javascripts/mustache-0.8.1.js +570 -0
  81. data/vendor/assets/javascripts/perspectives/forms/text_field.mustache +3 -0
  82. data/vendor/assets/javascripts/perspectives.js +152 -0
  83. data/vendor/assets/javascripts/perspectives_views.js +1 -0
  84. metadata +233 -0
@@ -0,0 +1,142 @@
1
+ module Perspectives
2
+ module ControllerAdditions
3
+ def self.included(base)
4
+ base.before_filter :set_perspectives_version
5
+ base.helper_method :assets_meta_tag
6
+ base.class_attribute :perspectives_enabled_actions
7
+ delegate :perspectives_enabled_actions, to: 'self.class'
8
+ base.helper_method :perspective
9
+
10
+ base.class_attribute :perspectives_wrapping
11
+ base.perspectives_wrapping = []
12
+
13
+ base.extend(ClassMethods)
14
+
15
+ delegate 'resolve_perspective_class_name', to: 'self.class'
16
+ end
17
+
18
+ private
19
+
20
+ unless defined?(ActionController::Responder)
21
+ def respond_to(*mimes, &block)
22
+ return super if block_given? || mimes.many? || !mimes.first.is_a?(Perspectives::Base)
23
+
24
+ perspectives_object = mimes.first
25
+ perspectives_object = wrap_perspective(perspectives_object) if wrap_perspective?
26
+
27
+ super() do |format|
28
+ format.html { render text: perspectives_object.to_html, layout: :default }
29
+ format.json { render json: perspectives_object }
30
+ end
31
+ end
32
+ end
33
+
34
+ def perspective(name, params_or_options = {})
35
+ if params_or_options.key?(:context) || params_or_options.key?(:params)
36
+ params = params_or_options.fetch(:params, {})
37
+ context = params_or_options.fetch(:context, default_context)
38
+ else
39
+ context = default_context
40
+ params = params_or_options
41
+ end
42
+
43
+ resolve_perspective_class_name(name).new(context, params)
44
+ end
45
+
46
+ def respond_with(*resources, &block)
47
+ return super unless wrap_perspective? && resources.first.is_a?(Perspectives::Base)
48
+
49
+ wrapped = wrap_perspective(resources.shift)
50
+
51
+ super(*resources.unshift(wrapped), &block)
52
+ end
53
+
54
+ def default_context
55
+ {}
56
+ end
57
+
58
+ def assets_version
59
+ Rails.application.assets.index.each_file.to_a.map { |f| File.new(f).mtime }.max.to_i
60
+ end
61
+
62
+ def assets_meta_tag
63
+ view_context.content_tag(:meta, nil, :'http-equiv' => 'x-perspectives-version', content: assets_version)
64
+ end
65
+
66
+ def set_perspectives_version
67
+ response.headers['X-Perspectives-Version'] = assets_version.to_s
68
+ end
69
+
70
+ def perspectives_enabled_action?
71
+ action_enabled_by?(perspectives_enabled_actions)
72
+ end
73
+
74
+ def perspectives_wrapper
75
+ return unless perspectives_enabled_action? && (request.headers['X-Perspectives-Full-Page'].to_s == 'true' || !request.xhr?)
76
+
77
+ perspectives_wrapping.find do |_, options|
78
+ next unless action_enabled_by?(options)
79
+
80
+ if options[:unless].present?
81
+ !options[:unless].call(self)
82
+ elsif options[:if].present?
83
+ options[:if].call(self)
84
+ else
85
+ true
86
+ end
87
+ end
88
+ end
89
+ alias_method :wrap_perspective?, :perspectives_wrapper
90
+
91
+ def wrap_perspective(unwrapped_perspective)
92
+ perspective_klass, options = *perspectives_wrapper
93
+ perspective_klass.new(unwrapped_perspective.context, options[:args].call(self, unwrapped_perspective))
94
+ end
95
+
96
+ def action_enabled_by?(options)
97
+ return false if options.nil?
98
+
99
+ action = action_name.to_s
100
+
101
+ if options[:except]
102
+ !options[:except].include?(action)
103
+ elsif options[:only]
104
+ options[:only].include?(action)
105
+ else
106
+ true
107
+ end
108
+ end
109
+
110
+ module ClassMethods
111
+ def perspectives_actions(options = {})
112
+ self.perspectives_enabled_actions = options.slice(:only, :except).each_with_object({}) do |(k, v), h|
113
+ h[k] = Array(v).map(&:to_s)
114
+ end
115
+
116
+ respond_to :html, :json, options
117
+ self.responder = Perspectives::Responder
118
+ end
119
+
120
+ def wrapped_with(perspective, options = {})
121
+ perspective_klass = resolve_perspective_class_name(perspective)
122
+
123
+ options[:only] = Array(options[:only]).map(&:to_s) if options[:only]
124
+ options[:except] = Array(options[:except]).map(&:to_s) if options[:except]
125
+
126
+ options[:if] ||= lambda { |c| c.params[perspective_klass.id_param].present? }
127
+ options[:args] ||= lambda do |controller, perspective|
128
+ {
129
+ perspective_klass.active_record_klass.name.underscore => perspective_klass.active_record_klass.find(controller.params[perspective_klass.id_param]),
130
+ options.fetch(:as, controller_name.underscore.singularize) => perspective
131
+ }
132
+ end
133
+
134
+ self.perspectives_wrapping += [[perspective_klass, options]]
135
+ end
136
+
137
+ def resolve_perspective_class_name(name)
138
+ Perspectives.resolve_partial_class_name(controller_name.camelize, name)
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,5 @@
1
+ class Perspectives::Forms::Base < Perspectives::Base
2
+ def self.template_path
3
+ File.expand_path('../../../../vendor/assets/javascripts', __FILE__)
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ module Perspectives::Forms
2
+ class TextField < Base
3
+ param :object, :field
4
+
5
+ property(:param_key) { object.class.model_name.param_key }
6
+ property(:human_name) { object.class.name.humanize }
7
+ property(:field_id) { "#{param_key}_#{field}" }
8
+ property(:field_param) do
9
+ "#{param_key}[#{field.sub(/\?$/, '')}]"
10
+ end
11
+
12
+ property(:name) { object.class.human_attribute_name(field) }
13
+ property(:value) { object.__send__(field) }
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module Perspectives
2
+ module Forms
3
+ end
4
+ end
5
+
6
+ require 'perspectives/forms/base'
7
+ require 'perspectives/forms/text_field'
@@ -0,0 +1,30 @@
1
+ module Perspectives
2
+ module Memoization
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def property(name, *names, &block)
9
+ super.tap { memoize_property(name) if names.empty? }
10
+ end
11
+
12
+ def memoize_property(prop_name)
13
+ raise ArgumentError, "No method #{prop_name}" unless method_defined?(prop_name)
14
+
15
+ original_property_method = "_unmemoized_#{prop_name}"
16
+ raise ArgumentError, "Already memoized property #{prop_name.inspect}" if method_defined?(original_property_method)
17
+
18
+ ivar = "@_memoized_#{prop_name.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}"
19
+ alias_method original_property_method, prop_name
20
+
21
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
22
+ def #{prop_name} # def name
23
+ return #{ivar} if defined?(#{ivar}) # return @_memoized_name if defined?(@_memoized_name)
24
+ #{ivar} = #{original_property_method} # @_memoized_name = _unmemoized_name
25
+ end
26
+ CODE
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ # somewhat ganked from https://github.com/railsware/smt_rails/blob/7d63a3d5c838881690d365f41f45b9082c2611c8/lib/smt_rails/tilt.rb
2
+
3
+ require 'tilt'
4
+
5
+ module Perspectives
6
+ class MustacheCompiler < Tilt::Template
7
+ self.default_mime_type = 'application/javascript'
8
+
9
+ def prepare
10
+ end
11
+
12
+ def evaluate(scope, locals, &block)
13
+ namespace = "this.#{Perspectives.template_namespace}"
14
+
15
+ <<-MustacheTemplate
16
+ (function() {
17
+ #{namespace} || (#{namespace} = {});
18
+ #{namespace}.views || (#{namespace}.views = {})
19
+
20
+ var data = #{data.inspect}
21
+
22
+ Mustache.parse(data)
23
+
24
+ #{namespace}.views[#{scope.logical_path.inspect}] = function(object) {
25
+ if (!object){ object = {}; }
26
+ return Mustache.render(data, object)
27
+ };
28
+
29
+ }).call(this);
30
+ MustacheTemplate
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,48 @@
1
+ module Perspectives
2
+ module Params
3
+ def self.included(base)
4
+ base.class_eval do
5
+ extend ClassMethods
6
+
7
+ class_attribute :_required_params, :_optional_params
8
+ self._required_params = []
9
+ self._optional_params = []
10
+ attr_reader :_params, :context
11
+ end
12
+ end
13
+
14
+ def initialize(context = {}, params = {})
15
+ raise ArgumentError, "Params is not a hash!" unless params.is_a?(Hash)
16
+ @_params = params.symbolize_keys
17
+ @context = context
18
+ assert_valid_params!
19
+ end
20
+
21
+ private
22
+
23
+ def assert_valid_params!
24
+ missing = _required_params.select { |l| !_params.key?(l) }
25
+ unknown = _params.keys - (_required_params + _optional_params)
26
+
27
+ if missing.any?
28
+ raise ArgumentError, "Missing #{missing.join(', ').inspect} while initializing #{self.class}!"
29
+ elsif unknown.any?
30
+ raise ArgumentError, "Unrecognized params #{unknown.join(', ').inspect} while initializing #{self.class}!"
31
+ end
32
+ end
33
+
34
+ module ClassMethods
35
+ def param(*param_names)
36
+ options = param_names.extract_options!
37
+
38
+ if options[:allow_nil]
39
+ self._optional_params += param_names
40
+ else
41
+ self._required_params += param_names
42
+ end
43
+
44
+ param_names.each { |n| define_method(n) { _params[n] } }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,130 @@
1
+ module Perspectives
2
+ module Properties
3
+ CantUseLambdas = Class.new(StandardError)
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ extend ClassMethods
8
+ class_attribute :_properties, :_nested_perspectives
9
+
10
+ self._properties = []
11
+ self._nested_perspectives = []
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def _property_map
18
+ _properties.each_with_object({}) do |p, h|
19
+ h[p] = __send__(p)
20
+
21
+ if h[p].is_a?(Proc)
22
+ raise CantUseLambdas, "You cannot use the lambda mustache behavior if you want to render on the client...it's not portable!"
23
+ end
24
+ end
25
+ end
26
+
27
+ def _resolve_partial_class_name(name)
28
+ Perspectives.resolve_partial_class_name(self.class.to_s.split('::').first, name)
29
+ end
30
+
31
+ module ClassMethods
32
+ def property(name, *names, &block)
33
+ unless names.empty?
34
+ raise ArgumentError, "Can't define multiple properties and pass a block" if block_given?
35
+ return names.push(name).each(&public_method(:property))
36
+ end
37
+
38
+ self._properties += [name]
39
+
40
+ unless method_defined?(name)
41
+ raise ArgumentError, "No method #{name} and no block given" unless block_given?
42
+
43
+ define_method(name, &block)
44
+ end
45
+ end
46
+
47
+ def nested(name, args = {}, &block)
48
+ locals, options = args, {}
49
+
50
+ if args[:locals]
51
+ locals = args[:locals]
52
+ options = args.except(:locals)
53
+ end
54
+
55
+ _setup_nested(name, locals, options, &block)
56
+ end
57
+
58
+ def nested_collection(name, *args, &block)
59
+ options = args.extract_options!
60
+ collection = options.fetch(:collection, args.first)
61
+ raise ArgumentError, "You must either pass in a collection, or pass a collection option" unless collection
62
+
63
+ _setup_nested(name, options.fetch(:locals, {}), options.merge!(:collection => collection), &block)
64
+ end
65
+
66
+ def delegate_property(*props)
67
+ delegate *props
68
+ opts = props.pop
69
+
70
+ prop_names = props
71
+
72
+ if opts[:prefix]
73
+ prefix = opts[:prefix] == true ? opts[:to] : opts[:prefix]
74
+ prop_names = prop_names.map { |n| "#{prefix}_#{n}" }
75
+ end
76
+
77
+ prop_names.each(&public_method(:property))
78
+ end
79
+
80
+ private
81
+
82
+ def _setup_nested(name, locals, options, &block)
83
+ name_str, name_sym = name.to_s, name.to_sym
84
+
85
+ prop_name = options.fetch(:property, _default_property_name(name_str, options)).to_sym
86
+
87
+ unless block_given? || method_defined?(prop_name)
88
+ local_procs = locals.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.respond_to?(:to_proc) ? v.to_proc : proc { v } }
89
+ nested_klass_ivar = :"@_#{name_str.underscore.gsub('/', '__')}_klass"
90
+
91
+ define_method(prop_name) do
92
+ klass =
93
+ if self.class.instance_variable_defined?(nested_klass_ivar)
94
+ self.class.instance_variable_get(nested_klass_ivar)
95
+ else
96
+ self.class.instance_variable_set(nested_klass_ivar, _resolve_partial_class_name(name))
97
+ end
98
+
99
+ if options[:unless]
100
+ return if instance_exec(self, &options[:unless])
101
+ elsif options[:if]
102
+ return unless instance_exec(self, &options[:if])
103
+ end
104
+
105
+ realized_locals = local_procs.each_with_object({}) { |(k, v), h| h[k] = instance_exec(self, &v) }
106
+
107
+ if options.key?(:collection)
108
+ collection = instance_exec(self, &options[:collection])
109
+ return unless collection.present?
110
+
111
+ as = options.fetch(:as, collection.first.class.base_class.name.downcase).to_sym
112
+ Collection.new(collection.map { |o| klass.new(context, realized_locals.merge(as => o)) })
113
+ else
114
+ klass.new(context, realized_locals)
115
+ end
116
+ end
117
+ end
118
+
119
+ property(prop_name, &block)
120
+ self._nested_perspectives += [prop_name]
121
+ end
122
+
123
+ def _default_property_name(name_str, options)
124
+ name = name_str.split('/').last
125
+ name = name.pluralize if options.key?(:collection)
126
+ name
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,42 @@
1
+ require 'perspectives/controller_additions'
2
+ require 'perspectives/responder'
3
+ require 'perspectives/active_record'
4
+ require 'generators/perspectives/install.rb'
5
+ require 'generators/perspectives/scaffold/scaffold_generator.rb'
6
+
7
+ module Perspectives
8
+ class Railtie < Rails::Railtie
9
+ if ::Rails.version.to_s >= "3.1"
10
+ config.app_generators.template_engine :perspectives
11
+ config.app_generators.templates << File.expand_path('../../generators/perspectives/templates', __FILE__)
12
+ else
13
+ config.generators.template_engine :perspectives
14
+ config.generators.templates << File.expand_path('../../generators/perspectives/templates', __FILE__)
15
+ end
16
+
17
+ initializer 'perspectives.railtie' do |app|
18
+ app.config.autoload_paths += ['app/perspectives']
19
+ app.config.watchable_dirs['app/mustaches'] = [:mustache]
20
+
21
+ app.config.assets.paths << File.expand_path('../../../vendor/assets/javascripts', __FILE__)
22
+
23
+ Perspectives::Base.class_eval do
24
+ include ActionView::Helpers
25
+ include app.routes.url_helpers
26
+ include ERB::Util
27
+ include Perspectives::ActiveRecord
28
+ end
29
+
30
+ Perspectives.configure do |c|
31
+ c.template_path = app.root.join('app', 'mustaches')
32
+ end
33
+
34
+ app.assets.register_engine '.mustache', Perspectives::MustacheCompiler
35
+ app.config.assets.paths << Perspectives.template_path
36
+
37
+ # TODO: probably bail if we're not in rails3/sprockets land...
38
+ # TODO: probably cache asset version in prod?
39
+ ActionController::Base.send(:include, Perspectives::ControllerAdditions)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ module Perspectives
2
+ module Rendering
3
+ def as_json(options = {})
4
+ _property_map.merge(_template_key: _template_key)
5
+ end
6
+
7
+ def render_html
8
+ _mustache.render(_property_map).html_safe
9
+ end
10
+
11
+ def render; render_html; end
12
+ def to_html; render_html; end
13
+ def to_s; render_html; end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Perspectives
2
+ class Responder < ActionController::Responder
3
+ def to_html
4
+ return super unless controller.__send__(:perspectives_enabled_action?)
5
+
6
+ render text: resource.to_html, layout: :default
7
+ end
8
+
9
+ def to_json
10
+ return super unless controller.__send__(:perspectives_enabled_action?)
11
+
12
+ render json: resource
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ module Perspectives
2
+ module Templating
3
+ def self.included(base)
4
+ base.class_eval do
5
+ extend ClassMethods
6
+
7
+ delegate :_mustache, :_template_key, to: 'self.class'
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def raise_on_context_miss?
13
+ Perspectives.raise_on_context_miss?
14
+ end
15
+
16
+ def template_path
17
+ Perspectives.template_path
18
+ end
19
+
20
+ def _mustache
21
+ return @_mustache if defined?(@_mustache)
22
+
23
+ klass = self
24
+ @_mustache = Class.new(Mustache) do
25
+ self.template_name = klass.to_s.underscore
26
+ self.raise_on_context_miss = klass.raise_on_context_miss?
27
+ self.template_path = klass.template_path
28
+ end
29
+ end
30
+
31
+ def _template_key
32
+ @_template_key ||=
33
+ _mustache.template_file.
34
+ sub(/^#{Regexp.escape(_mustache.template_path)}\//, '').
35
+ chomp(".#{_mustache.template_extension}")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ module Perspectives
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,48 @@
1
+ require 'forwardable'
2
+ require 'mustache'
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'active_support/core_ext/module/delegation'
5
+ require 'active_support/core_ext/array/extract_options'
6
+ require 'active_support/core_ext/hash/keys'
7
+ require 'active_support/core_ext/class/attribute'
8
+ require 'perspectives/collection'
9
+ require 'perspectives/base'
10
+ require 'perspectives/forms'
11
+ require 'perspectives/configuration'
12
+ require 'perspectives/mustache_compiler'
13
+ require 'perspectives/railtie' if defined?(Rails) # TODO: older rails support!
14
+
15
+ module Perspectives
16
+ class << self
17
+ def template_namespace
18
+ 'Perspectives'
19
+ end
20
+
21
+ def configure
22
+ yield(configuration)
23
+ end
24
+
25
+ delegate :cache, :caching?, :template_path, :raise_on_context_miss?, to: :configuration
26
+ delegate :expand_cache_key, to: 'ActiveSupport::Cache'
27
+
28
+ def resolve_partial_class_name(top_level_view_namespace, name)
29
+ return name if name.is_a?(Class) && name < Perspectives::Base
30
+
31
+ camelized = name.to_s.camelize
32
+
33
+ [top_level_view_namespace, camelized].join('::').constantize
34
+ rescue NameError
35
+ camelized.constantize
36
+ end
37
+
38
+ private
39
+
40
+ def configuration
41
+ @configuration ||= Configuration.new
42
+ end
43
+ end
44
+
45
+ configure do |c|
46
+ c.raise_on_context_miss = true
47
+ end
48
+ end
@@ -0,0 +1,12 @@
1
+ {
2
+ "config/projections.json": {"command": "projections"},
3
+ "config/application.rb": {"command": "application"},
4
+ "app/perspectives/*.rb": {
5
+ "command": "perspective",
6
+ "alternate": "app/mustaches/%s.mustache"
7
+ },
8
+ "app/mustaches/*.mustache": {
9
+ "command": "mview",
10
+ "alternate": "app/perspectives/%s.rb"
11
+ }
12
+ }
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :perspectives do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Dummy::Application.load_tasks
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ # Prevent CSRF attacks by raising an exception.
3
+ # For APIs, you may want to use :null_session instead.
4
+ protect_from_forgery with: :exception
5
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end