conjoin 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/conjoin.gemspec +20 -0
- data/lib/conjoin.rb +27 -1
- data/lib/conjoin/active_record.rb +374 -0
- data/lib/conjoin/assets.rb +205 -0
- data/lib/conjoin/auth.rb +92 -0
- data/lib/conjoin/class_methods.rb +15 -0
- data/lib/conjoin/csrf.rb +26 -0
- data/lib/conjoin/cuba.rb +72 -0
- data/lib/conjoin/env_string.rb +17 -0
- data/lib/conjoin/form_builder.rb +334 -0
- data/lib/conjoin/i18n.rb +97 -0
- data/lib/conjoin/inputs/boolean.rb +8 -0
- data/lib/conjoin/inputs/checkbox.rb +14 -0
- data/lib/conjoin/inputs/date.rb +11 -0
- data/lib/conjoin/inputs/decimal.rb +9 -0
- data/lib/conjoin/inputs/file.rb +17 -0
- data/lib/conjoin/inputs/hidden.rb +10 -0
- data/lib/conjoin/inputs/integer.rb +9 -0
- data/lib/conjoin/inputs/password.rb +10 -0
- data/lib/conjoin/inputs/radio.rb +35 -0
- data/lib/conjoin/inputs/select.rb +67 -0
- data/lib/conjoin/inputs/state.rb +68 -0
- data/lib/conjoin/inputs/string.rb +9 -0
- data/lib/conjoin/inputs/time.rb +11 -0
- data/lib/conjoin/middleware.rb +40 -0
- data/lib/conjoin/recursive_ostruct.rb +117 -0
- data/lib/conjoin/tasks.rb +4 -0
- data/lib/conjoin/tasks/migrate.rake +70 -0
- data/lib/conjoin/tasks/migrate.rb +142 -0
- data/lib/conjoin/version.rb +1 -1
- data/lib/conjoin/widgets.rb +323 -0
- metadata +296 -2
data/lib/conjoin/i18n.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'r18n-core'
|
2
|
+
|
3
|
+
# https://github.com/ai/r18n
|
4
|
+
module Conjoin
|
5
|
+
module I18N
|
6
|
+
include R18n::Helpers
|
7
|
+
|
8
|
+
def self.setup(app)
|
9
|
+
app.settings[:default_locale] = 'en-US'
|
10
|
+
app.settings[:translations] = File.join(app.root, 'i18n')
|
11
|
+
::R18n::Filters.off :untranslated
|
12
|
+
::R18n::Filters.on :untranslated_html
|
13
|
+
if Conjoin.env.test? or Conjoin.env.development?
|
14
|
+
::R18n.clear_cache!
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_locale(req, force_default = false)
|
19
|
+
::R18n.set do
|
20
|
+
::R18n::I18n.default = settings[:default_locale]
|
21
|
+
locale = get_locale_from_host
|
22
|
+
# You can add support for path language info :) Just do it and pull request it ;)
|
23
|
+
# locale = get_locale_from_path if locale.nil?
|
24
|
+
if locale.nil? and not force_default
|
25
|
+
locales = ::R18n::I18n.parse_http req.env['HTTP_ACCEPT_LANGUAGE']
|
26
|
+
if req.params['locale']
|
27
|
+
locales.insert 0, req.params['locale']
|
28
|
+
elsif req.session['locale']
|
29
|
+
locales.insert 0, req.session['locale']
|
30
|
+
end
|
31
|
+
else
|
32
|
+
locales = []
|
33
|
+
locales << locale
|
34
|
+
locales << settings[:default_locale]
|
35
|
+
end
|
36
|
+
::R18n::I18n.new locales, settings[:translations]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_locale_from_host
|
41
|
+
# auxiliar method to get locale from the subdomain (assuming it is a valid locale).
|
42
|
+
data = req.host.split('.')[0]
|
43
|
+
data if ::R18n::Locale.exists? data
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module R18n
|
49
|
+
class << self
|
50
|
+
def t(*params)
|
51
|
+
if params.first.is_a? String
|
52
|
+
params.first.split('.').inject(get.t) { |h, k| h[k.to_sym] }
|
53
|
+
else
|
54
|
+
get.t(*params)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Override
|
60
|
+
# https://github.com/ai/r18n/blob/master/r18n-core/lib/r18n-core/locale.rb#L152
|
61
|
+
class Locale
|
62
|
+
# Convert +object+ to String. It support Fixnum, Bignum, Float, Time, Date
|
63
|
+
# and DateTime.
|
64
|
+
#
|
65
|
+
# For time classes you can set +format+ in standard +strftime+ form,
|
66
|
+
# <tt>:full</tt> (“01 Jule, 2009”), <tt>:human</tt> (“yesterday”),
|
67
|
+
# <tt>:standard</tt> (“07/01/09”) or <tt>:month</tt> for standalone month
|
68
|
+
# name. Default format is <tt>:standard</tt>.
|
69
|
+
def localize(obj, format = nil, *params)
|
70
|
+
case obj
|
71
|
+
when Integer
|
72
|
+
format_integer(obj)
|
73
|
+
when Float, BigDecimal
|
74
|
+
format_float(obj.to_f)
|
75
|
+
when Time, DateTime, Date
|
76
|
+
return strftime(obj, format) if format.is_a? String
|
77
|
+
return month_standalone[obj.month - 1] if :month == format
|
78
|
+
return obj.to_s if :human == format and not params.first.is_a? I18n
|
79
|
+
|
80
|
+
type = obj.is_a?(Date) ? 'date' : 'time'
|
81
|
+
format = :standard unless format
|
82
|
+
|
83
|
+
unless respond_to? "format_#{type}_#{format}"
|
84
|
+
raise ArgumentError, "Unknown time formatter #{format}"
|
85
|
+
end
|
86
|
+
|
87
|
+
send "format_#{type}_#{format}", obj, *params
|
88
|
+
else
|
89
|
+
obj.to_s
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def format_time_time time, *params
|
94
|
+
format_time(time)[1..-1]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Conjoin
|
2
|
+
module FormBuilder
|
3
|
+
class CheckboxInput < Input
|
4
|
+
def display
|
5
|
+
options[:value] = 'on' if data.value == true
|
6
|
+
options[:checked] = 'checked' if data.value
|
7
|
+
options[:type] = :checkbox
|
8
|
+
options[:class].gsub!(/form-control/, '')
|
9
|
+
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Conjoin
|
2
|
+
module FormBuilder
|
3
|
+
class FileInput < Input
|
4
|
+
def display
|
5
|
+
key = options[:s3_upload_path].call(record)
|
6
|
+
|
7
|
+
mab do
|
8
|
+
unless options[:value]
|
9
|
+
div id: id, name: options[:name], class: 'file s3-uploader', value: options[:value]
|
10
|
+
end
|
11
|
+
input id: id, type: :hidden, name: options[:name], class: 'form-control file s3-uploader', value: options[:value]
|
12
|
+
text! S3Uploader.js_button(id, key, options[:callback_url], options[:callback_params])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Conjoin
|
2
|
+
module FormBuilder
|
3
|
+
class RadioInput < Input
|
4
|
+
def display
|
5
|
+
options[:type] = :radio
|
6
|
+
options[:class].gsub!(/form-control/, '')
|
7
|
+
|
8
|
+
radios = options[:radios] || [:yes, :no]
|
9
|
+
|
10
|
+
opts = options.dup
|
11
|
+
|
12
|
+
mab do
|
13
|
+
div class: 'form-control' do
|
14
|
+
radios.each_with_index do |name, i|
|
15
|
+
name = name.to_s
|
16
|
+
opts[:value] = name
|
17
|
+
opts[:id] = "#{options[:id]}_#{i}"
|
18
|
+
|
19
|
+
if (opts[:value] == 'no' and data.value == false) \
|
20
|
+
or (opts[:value] == 'yes' and data.value == true) \
|
21
|
+
or (opts[:value] == data.value)
|
22
|
+
opts[:checked] = 'checked'
|
23
|
+
else
|
24
|
+
opts.delete :checked
|
25
|
+
end
|
26
|
+
|
27
|
+
input opts
|
28
|
+
span name.humanize
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Conjoin
|
2
|
+
module FormBuilder
|
3
|
+
class SelectInput < Input
|
4
|
+
@select_options = {}
|
5
|
+
|
6
|
+
def display
|
7
|
+
mab do
|
8
|
+
# automatically add a prompt by default
|
9
|
+
options[:prompt] = true unless options.key? :prompt
|
10
|
+
options[:class] += ' select2'
|
11
|
+
selected_value = options.delete :value
|
12
|
+
|
13
|
+
select options do
|
14
|
+
if prompt = options.delete(:prompt)
|
15
|
+
opts = {
|
16
|
+
value: ''
|
17
|
+
}
|
18
|
+
opts['selected'] = 'selected' unless selected_value
|
19
|
+
option opts do
|
20
|
+
text prompt.to_s == 'true' ? 'Please Choose One.' : prompt
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if not options[:group]
|
25
|
+
select_options.each do |name, value|
|
26
|
+
option render_opts(value, selected_value, opts) do
|
27
|
+
text name.titleize
|
28
|
+
end
|
29
|
+
end
|
30
|
+
else
|
31
|
+
select_options.each do |group_select, group|
|
32
|
+
optgroup label: group.to_s.titleize do
|
33
|
+
group_select.each do |value, name|
|
34
|
+
option render_opts(value, selected_value, opts) do
|
35
|
+
text name.titleize
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_opts value, selected_value, opts
|
46
|
+
opts = {
|
47
|
+
value: value
|
48
|
+
}
|
49
|
+
if selected_value.is_a? ActiveRecord::Associations::CollectionProxy
|
50
|
+
opts['selected'] = 'selected' if selected_value.map(&:id).include? value
|
51
|
+
else
|
52
|
+
opts['selected'] = 'selected' if selected_value == value.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
opts
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.select_options
|
59
|
+
@select_options
|
60
|
+
end
|
61
|
+
|
62
|
+
def select_options
|
63
|
+
self.class.select_options.invert
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'select'
|
2
|
+
|
3
|
+
module Conjoin
|
4
|
+
module FormBuilder
|
5
|
+
class StateInput < SelectInput
|
6
|
+
@select_options = {
|
7
|
+
'AL' => 'Alabama',
|
8
|
+
'AK' => 'Alaska',
|
9
|
+
'AS' => 'America Samoa',
|
10
|
+
'AZ' => 'Arizona',
|
11
|
+
'AR' => 'Arkansas',
|
12
|
+
'CA' => 'California',
|
13
|
+
'CO' => 'Colorado',
|
14
|
+
'CT' => 'Connecticut',
|
15
|
+
'DE' => 'Delaware',
|
16
|
+
'DC' => 'District of Columbia',
|
17
|
+
'FM' => 'Micronesia',
|
18
|
+
'FL' => 'Florida',
|
19
|
+
'GA' => 'Georgia',
|
20
|
+
'GU' => 'Guam',
|
21
|
+
'HI' => 'Hawaii',
|
22
|
+
'ID' => 'Idaho',
|
23
|
+
'IL' => 'Illinois',
|
24
|
+
'IN' => 'Indiana',
|
25
|
+
'IA' => 'Iowa',
|
26
|
+
'KS' => 'Kansas',
|
27
|
+
'KY' => 'Kentucky',
|
28
|
+
'LA' => 'Louisiana',
|
29
|
+
'ME' => 'Maine',
|
30
|
+
'MH' => 'Islands',
|
31
|
+
'MD' => 'Maryland',
|
32
|
+
'MA' => 'Massachusetts',
|
33
|
+
'MI' => 'Michigan',
|
34
|
+
'MN' => 'Minnesota',
|
35
|
+
'MS' => 'Mississippi',
|
36
|
+
'MO' => 'Missouri',
|
37
|
+
'MT' => 'Montana',
|
38
|
+
'NE' => 'Nebraska',
|
39
|
+
'NV' => 'Nevada',
|
40
|
+
'NH' => 'New Hampshire',
|
41
|
+
'NJ' => 'New Jersey',
|
42
|
+
'NM' => 'New Mexico',
|
43
|
+
'NY' => 'New York',
|
44
|
+
'NC' => 'North Carolina',
|
45
|
+
'ND' => 'North Dakota',
|
46
|
+
'OH' => 'Ohio',
|
47
|
+
'OK' => 'Oklahoma',
|
48
|
+
'OR' => 'Oregon',
|
49
|
+
'PW' => 'Palau',
|
50
|
+
'PA' => 'Pennsylvania',
|
51
|
+
'PR' => 'Puerto Rico',
|
52
|
+
'RI' => 'Rhode Island',
|
53
|
+
'SC' => 'South Carolina',
|
54
|
+
'SD' => 'South Dakota',
|
55
|
+
'TN' => 'Tennessee',
|
56
|
+
'TX' => 'Texas',
|
57
|
+
'UT' => 'Utah',
|
58
|
+
'VT' => 'Vermont',
|
59
|
+
'VI' => 'Virgin Island',
|
60
|
+
'VA' => 'Virginia',
|
61
|
+
'WA' => 'Washington',
|
62
|
+
'WV' => 'West Virginia',
|
63
|
+
'WI' => 'Wisconsin',
|
64
|
+
'WY' => 'Wyoming'
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rack/protection"
|
2
|
+
|
3
|
+
module Conjoin
|
4
|
+
class Middleware
|
5
|
+
class << self
|
6
|
+
# taken from
|
7
|
+
# https://github.com/rkh/rack-protection/blob/master/lib/rack/protection.rb#L20
|
8
|
+
def new app, options = {}
|
9
|
+
# except = Array options[:except]
|
10
|
+
# use_these = Array options[:use]
|
11
|
+
Rack::Builder.new do
|
12
|
+
if not Conjoin.env.mounted?
|
13
|
+
use Rack::Session::Cookie, secret: ENV['SECRET_BASE_KEY']
|
14
|
+
end
|
15
|
+
# we need to disable session_hijacking because IE uses different headers
|
16
|
+
# for ajax request over standard ones.
|
17
|
+
# https://github.com/rkh/rack-protection/issues/11#issuecomment-9005539
|
18
|
+
use Rack::Protection, except: :session_hijacking
|
19
|
+
|
20
|
+
if not Conjoin.env.mounted? and Conjoin.env.development?
|
21
|
+
require 'rack-livereload'
|
22
|
+
use Rack::LiveReload
|
23
|
+
use Rack::Reloader
|
24
|
+
end
|
25
|
+
|
26
|
+
if Conjoin.env.test? or Conjoin.env.development?
|
27
|
+
require 'better_errors'
|
28
|
+
use BetterErrors::Middleware
|
29
|
+
# require 'pry'
|
30
|
+
# require 'pry-rescue'
|
31
|
+
# use PryRescue::Rack
|
32
|
+
end
|
33
|
+
|
34
|
+
# continue running the application
|
35
|
+
run app
|
36
|
+
end.to_app
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# https://gist.github.com/rmw/2710460
|
2
|
+
require 'hashie'
|
3
|
+
|
4
|
+
class Hash
|
5
|
+
# options:
|
6
|
+
# :exclude => [keys] - keys need to be symbols
|
7
|
+
def to_ostruct(options = {})
|
8
|
+
convert_to_ostruct_recursive(self, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_sym_keys
|
12
|
+
# self.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
|
13
|
+
self.each_with_object({}) { |(k,v), memo| memo[k.to_sym] = v }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def convert_to_ostruct_recursive(obj, options)
|
19
|
+
result = obj
|
20
|
+
if result.is_a? Hash
|
21
|
+
result = result.dup.with_sym_keys
|
22
|
+
result.each do |key, val|
|
23
|
+
result[key] = convert_to_ostruct_recursive(val, options) unless options[:exclude].try(:include?, key)
|
24
|
+
end
|
25
|
+
result = OpenStruct.new result
|
26
|
+
elsif result.is_a? Array
|
27
|
+
result = result.map { |r| convert_to_ostruct_recursive(r, options) }
|
28
|
+
end
|
29
|
+
return result
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class HashIndifferent < Hash
|
34
|
+
include Hashie::Extensions::MergeInitializer
|
35
|
+
include Hashie::Extensions::IndifferentAccess
|
36
|
+
end
|
37
|
+
|
38
|
+
class OpenStruct
|
39
|
+
def to_hash options = {}
|
40
|
+
convert_to_hash_recursive(self, options)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def convert_to_hash_recursive(obj, options)
|
46
|
+
result = obj
|
47
|
+
if result.is_a? OpenStruct
|
48
|
+
result = result.dup.to_h.with_sym_keys
|
49
|
+
result.each do |key, val|
|
50
|
+
result[key] = convert_to_hash_recursive(val, options) unless options[:exclude].try(:include?, key)
|
51
|
+
end
|
52
|
+
result = HashIndifferent.new result
|
53
|
+
elsif result.is_a? Array
|
54
|
+
result = result.map { |r| convert_to_hash_recursive(r, options) }
|
55
|
+
end
|
56
|
+
return result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# require 'spec_helper'
|
61
|
+
#
|
62
|
+
# describe Hash do
|
63
|
+
#
|
64
|
+
# describe "#to_ostruct_recursive" do
|
65
|
+
# describe "replace a nested hash" do
|
66
|
+
# before do
|
67
|
+
# @h = { :a => { :b => { :c => 1 } } }
|
68
|
+
# @o = @h.to_ostruct_recursive
|
69
|
+
# end
|
70
|
+
# it "should be an OpenStruct" do
|
71
|
+
# @o.is_a?(OpenStruct).should be_true
|
72
|
+
# end
|
73
|
+
# it "should have a nested OpenStruct" do
|
74
|
+
# @o.a.should be
|
75
|
+
# @o.a.is_a?(OpenStruct).should be_true
|
76
|
+
# end
|
77
|
+
# it "should have a nested nested OpenStruct" do
|
78
|
+
# @o.a.b.should be
|
79
|
+
# @o.a.b.is_a?(OpenStruct).should be_true
|
80
|
+
# end
|
81
|
+
# it "should have a nested nested nested value of 1" do
|
82
|
+
# @o.a.b.c.should be
|
83
|
+
# @o.a.b.c.should == 1
|
84
|
+
# end
|
85
|
+
# describe "exclude a key from being converted to an OpenStruct" do
|
86
|
+
# before do
|
87
|
+
# @o_exclude = @h.to_ostruct_recursive({ :exclude => [:b] })
|
88
|
+
# end
|
89
|
+
# it "should be an OpenStruct" do
|
90
|
+
# @o.is_a?(OpenStruct).should be_true
|
91
|
+
# end
|
92
|
+
# it "should have a nested OpenStruct" do
|
93
|
+
# @o.a.is_a?(OpenStruct).should be_true
|
94
|
+
# end
|
95
|
+
# it "should have a nested nested Hash" do
|
96
|
+
# @o_exclude.a.b.is_a?(Hash).should be_true
|
97
|
+
# @o_exclude.a.b.should == { :c => 1 }
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
# describe "replace a nest hash in an array" do
|
102
|
+
# before do
|
103
|
+
# @h = { :a => [ {:a1 => 1 } ] }
|
104
|
+
# @o = @h.to_ostruct_recursive
|
105
|
+
# end
|
106
|
+
# it "should be an OpenStruct" do
|
107
|
+
# @o.is_a?(OpenStruct).should be_true
|
108
|
+
# end
|
109
|
+
# it "should have an array with 1 struct" do
|
110
|
+
# @o.a.is_a?(Array).should be_true
|
111
|
+
# @o.a.size.should == 1
|
112
|
+
# @o.a.first.is_a?(OpenStruct).should be_true
|
113
|
+
# @o.a.first.a1.should == 1
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
# end
|
117
|
+
# end
|