roar-extensions 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in roar-extensions.gemspec
4
+ gemspec
@@ -0,0 +1,24 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14
+ watch('config/routes.rb') { "spec/routing" }
15
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
16
+
17
+ # Capybara request specs
18
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
19
+
20
+ # Turnip features and steps
21
+ watch(%r{^spec/acceptance/(.+)\.feature$})
22
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
23
+ end
24
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Donald Plummer
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Roar::Extensions
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'roar-extensions'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install roar-extensions
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require "rspec/core/rake_task"
5
+
6
+ desc "Default: run specs."
7
+ task :default => :spec
8
+
9
+ desc "Run specs"
10
+ RSpec::Core::RakeTask.new
@@ -0,0 +1 @@
1
+ require "roar_extensions"
@@ -0,0 +1,30 @@
1
+ require "roar_extensions/version"
2
+ require "representable"
3
+ require "representable/json"
4
+ require "roar"
5
+ require 'roar/representer/json'
6
+ require 'roar/representer/json/hal'
7
+
8
+ module RoarExtensions
9
+ def self.base_url=(url)
10
+ @base_url = url
11
+ end
12
+
13
+ def self.base_url
14
+ @base_url || raise("Set base url like RoarExtensions.base_url = 'http://example.com'")
15
+ end
16
+ end
17
+
18
+ require 'active_support'
19
+ require 'active_support/core_ext/module'
20
+
21
+ require 'roar_extensions/helpers/embedded_parameter_parsing'
22
+ require 'roar_extensions/json_hal_extensions'
23
+ require 'roar_extensions/representable_json_extensions'
24
+ require 'roar_extensions/representer'
25
+ require 'roar_extensions/presenter'
26
+ require 'roar_extensions/destroyed_record_presenter'
27
+ require 'roar_extensions/link_presenter'
28
+ require 'roar_extensions/money_presenter'
29
+ require 'roar_extensions/paginated_collection_presenter'
30
+ require 'roar_extensions/resource_links'
@@ -0,0 +1,14 @@
1
+ module RoarExtensions
2
+ module DestroyedRecordPresenter
3
+ def self.included(receiver)
4
+ receiver.send(:include, RoarExtensions::Presenter)
5
+
6
+ receiver.instance_eval do
7
+ property :id
8
+
9
+ alias_method :id, :record
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ module RoarExtensions::Helpers
2
+ module EmbeddedParameterParsing
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ before_filter :parse_embedded_params_filter
7
+ end
8
+ end
9
+
10
+ def parse_embedded_params_filter
11
+ params[:embedded] = parse_embedded_params(params[:embedded])
12
+ end
13
+
14
+ def parse_embedded_params(embedded_str)
15
+ parse_embedded_params_list((embedded_str || '').split(','))
16
+ end
17
+
18
+ private
19
+ def parse_embedded_params_list(embedded)
20
+ with_children, without_children = embedded.map {|embed| embed.split(':', 2) }.
21
+ each {|pair| pair[0] = pair[0].to_sym}.
22
+ partition {|pair| pair.length > 1}
23
+
24
+ nested_embeds = with_children.inject({}) {|acc, (direct_embed, child)|
25
+ acc[direct_embed] ||= []
26
+ acc[direct_embed] << child
27
+ acc
28
+ }.map do |(direct_embed, children)|
29
+ [direct_embed, parse_embedded_params_list(children)]
30
+ end
31
+
32
+ without_children.flatten!
33
+ without_children << Hash[nested_embeds] unless nested_embeds.empty?
34
+ without_children
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ module Roar::Representer::JSON::HAL::Resources
2
+ def compile_fragment(bin, doc)
3
+ return super unless bin.definition.options[:embedded]
4
+ super(bin, doc["_embedded"] ||= {})
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ class RoarExtensions::LinkPresenter
2
+ attr_reader :href, :rel, :title
3
+
4
+ def initialize(rel, href, title = nil)
5
+ @href = href
6
+ @rel = rel
7
+ @title = title
8
+ end
9
+
10
+ def ==(other)
11
+ href == other.href &&
12
+ rel == other.rel &&
13
+ title == other.title
14
+ end
15
+
16
+ def to_hash(*args)
17
+ {
18
+ rel => {
19
+ :href => href,
20
+ :title => title
21
+ }.delete_if {|k,v| v.nil? || v.empty?}
22
+ }
23
+ end
24
+
25
+ alias :as_json :to_hash
26
+ end
@@ -0,0 +1,13 @@
1
+ class RoarExtensions::MoneyPresenter
2
+ include RoarExtensions::Presenter
3
+
4
+ root_element :money
5
+
6
+ delegated_property :cents
7
+ property :currency, :from => :currency_iso_code
8
+
9
+ private
10
+ def currency_iso_code
11
+ record.currency.iso_code
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ module RoarExtensions
2
+ class PaginatedCollectionPresenter
3
+ include RoarExtensions::Presenter
4
+
5
+ root_element :paginated_collection
6
+
7
+ delegated_property :per_page
8
+ delegated_property :total_pages
9
+ delegated_property :total_entries
10
+ delegated_property :current_page
11
+ delegated_property :next_page
12
+ delegated_property :previous_page
13
+
14
+ link(:rel => "self") { page_link(record.current_page) }
15
+ link(:rel => "next_page") { page_link(record.next_page) }
16
+ link(:rel => "previous_page") { page_link(record.previous_page) }
17
+
18
+ def initialize(record, base_path)
19
+ super(record)
20
+ @base_path = base_path
21
+ end
22
+
23
+ def page_link(page_number)
24
+ if page_number == 1
25
+ @base_path
26
+ elsif !page_number.nil?
27
+ "#{@base_path}?page=#{page_number}"
28
+ end
29
+ end
30
+
31
+ alias_method :to_hash_without_entries, :to_hash
32
+
33
+ # Hack to push the :include and :exclude options to the collection results
34
+ def to_hash(options = {})
35
+ opt_include = options.delete(:include)
36
+ opt_exclude = options.delete(:exclude)
37
+
38
+ res = to_hash_without_entries(options)
39
+ res["paginated_collection"]["entries"] = record.collect.map do |e|
40
+ entry_include = opt_include && opt_include.map {|name| get_actual_property_name(e, name)}
41
+ entry_exclude = opt_exclude && opt_exclude.map {|name| get_actual_property_name(e, name)}
42
+ e.to_hash(options.merge(:include => entry_include,
43
+ :exclude => entry_exclude))
44
+ end
45
+ res
46
+ end
47
+
48
+ private
49
+ def get_actual_property_name(entry, aliased_name)
50
+ if attr = entry.send(:representable_attrs).
51
+ detect {|a| a.options[:from].to_s == aliased_name.to_s}
52
+ attr.name
53
+ else
54
+ aliased_name
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,65 @@
1
+ require 'roar/representer/json'
2
+ require 'roar/representer/json/hal'
3
+
4
+ module RoarExtensions::Presenter
5
+
6
+ module ConditionalEmbeds
7
+ def skip_property?(bin, options)
8
+ if bin.definition.options[:embedded]
9
+ super || !embed_property?(bin.definition)
10
+ else
11
+ super
12
+ end
13
+ end
14
+
15
+ def embed_property?(definition)
16
+ (@embedded & [definition.name.to_sym, definition.from.to_sym]).present?
17
+ end
18
+ end
19
+
20
+ def initialize(record)
21
+ @record = record
22
+ @embedded ||= []
23
+ end
24
+
25
+ def self.included(base)
26
+ base.send(:include, Roar::Representer::JSON::HAL)
27
+ base.send(:include, ConditionalEmbeds)
28
+ base.extend(RoarExtensions::Representer)
29
+
30
+ base.class_eval do
31
+ alias :as_json :to_hash
32
+
33
+ def self.delegated_property(name, options = {})
34
+ if from = options.delete(:from)
35
+ define_method(name) do
36
+ record.send(from)
37
+ end
38
+ else
39
+ delegate name, :to => :record, :allow_nil => true
40
+ end
41
+
42
+ property name, options
43
+ end
44
+
45
+
46
+ def self.delegated_collection(name, options ={})
47
+ if from = options.delete(:from)
48
+ define_method(name) do
49
+ record.send(from)
50
+ end
51
+ else
52
+ delegate name, :to => :record, :allow_nil => true
53
+ end
54
+
55
+ collection name, options
56
+ end
57
+
58
+ end
59
+ end
60
+
61
+ private
62
+ def record
63
+ @record
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ module Representable
2
+ module JSON
3
+ alias_method :to_hash_without_always_include_attributes, :to_hash
4
+
5
+ def to_hash(options = {})
6
+ if options[:include]
7
+ options[:include].map!(&:to_sym)
8
+ options[:include] |= self.class.always_include_attributes.map(&:to_sym)
9
+ end
10
+ to_hash_without_always_include_attributes(options)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,61 @@
1
+ module RoarExtensions::Representer
2
+ def root_element(element)
3
+ self.representation_wrap = element.to_s
4
+ end
5
+
6
+ def always_include_attributes
7
+ @always_include_attributes ||= []
8
+ end
9
+
10
+ # In the real API, :from and name are backwards?
11
+ def property(name, options = {})
12
+ options.merge!(:render_nil => true)
13
+
14
+ if options[:always_include]
15
+ always_include_attributes << name
16
+ end
17
+
18
+ if create_object = options.delete(:as)
19
+ define_method("#{name}_presented") do
20
+ attr_value = send(name)
21
+ attr_value && create_object.new(attr_value).as_json
22
+ end
23
+
24
+ options[:from] = "#{name}_presented"
25
+ end
26
+
27
+ if from = options.delete(:from)
28
+ super(from, options.merge(:from => name))
29
+ else
30
+ super(name, options)
31
+ end
32
+ end
33
+
34
+ def collection(name, options = {})
35
+ if create_object = options.delete(:as)
36
+ define_method("#{name}_presented") do
37
+ send(name).map do |element|
38
+ element && create_object.new(element).as_json
39
+ end
40
+ end
41
+ options[:from] = "#{name}_presented"
42
+ end
43
+
44
+ super name, options
45
+ end
46
+
47
+ def link(name, &block)
48
+ development = (defined?(Rails) && Rails.env.development?) ||
49
+ ENV['RAILS_ENV'] == 'development' ||
50
+ ENV['ENVIRONMENT'] == 'development'
51
+ if development
52
+ super(name) do
53
+ if path = instance_exec(&block)
54
+ "#{RoarExtensions.base_url}#{path}"
55
+ end
56
+ end
57
+ else
58
+ super
59
+ end
60
+ end
61
+ end